ci: run checks, builds, releases and the website on CircleCI #417

Merged
HumanBean merged 32 commits from drcrow/Longshot:ci/circleci into main 2026-11-15 17:25:16 +00:00
Member

Type: ci · Risk: low · Tested on: Linux (devbox, Docker images of the CI jobs) · Needs: a live CircleCI run on main after merge

Why

Pull requests get only the title, description and third-party checks today. The game-free checks and the builders run by hand or at release, so a break is found after it merges. This moves CI and the release pipeline to CircleCI, which runs the checks and builders each pull request needs and nothing more. Until cutover, CircleCI's releases and website deploy are dry runs and Forgejo's workflows stay the live writers.

What changed

CircleCI runs the same checks and builders as the Forgejo workflows, chosen by what a PR changed, with one ci/circleci: gate status to require; releases and the website deploy are dry runs until cutover.

  • tests: tests/run_all.py gains --list (the checks a run would start, running none), --junit FILE, and an opt-in --exact (names must match exactly; nothing matching fails closed) that CircleCI's test splitter uses. --shard, --write-times and substring names are unchanged. Tests: tests/test_run_all_junit.py.
  • ci: .circleci/config.yml has one small setup job per event (pull request, push to main, v* tag, schedule, manual API run). Each one looks at which areas changed (.circleci/mapping.conf: tests, gpu, linux, windows, website, third-party, docs) and continues with .circleci/shared.yml plus that event's file in .circleci/events/. A docs-only PR runs lint, third-party and gate; a gpu/ change adds the Vulkan builds and SPIR-V fixtures.
  • ci: the shared setup, fork handling, release sign/publish steps and gate come from one CircleCI orb, magicbeandev/ci, used by all three repos (source in MagicBeanDev/Orb). This repo keeps only its own jobs.
  • ci: the game-free checks run with CircleCI Smarter Testing (.circleci/test-suites.yml), split across 4 containers by timing, with JUnit results.
  • ci: every push to main runs everything, then releases a nightly, as release.yml does: the release jobs start only after every check and build passes, so a red main ships nothing. v* tags rerun the core checks, then release. Release jobs run main's copy of scripts/release.py. Signing (release-sign, signing key only) and publishing (release-publish, Forgejo token only, one release at a time) are separate jobs. Until cutover, main, tags and the website refresh run as dry runs (throwaway key, nothing published or deployed), so Forgejo's release.yml and website.yml stay the only writers; the cutover PR turns CircleCI live and removes them.
  • ci: the Wine checks run on an Ubuntu 24.04 VM, like the Forgejo runner. The other jobs use CircleCI's Arm (Gen2) containers where the tools exist (docs/ci.md lists each job and why).
  • ci: website: a pull request's built site is saved as a CircleCI artifact and linked in one PR comment. Main deploys it with the production token. PR code never runs next to a Cloudflare token.
  • ci: fork pull requests: CircleCI doesn't build them from Forgejo, so a second CircleCI project on this repo runs .circleci/forks.yml, started by a Forgejo webhook. A maintainer adds the ci: approved label (Forgejo only lets people with write access do that), and it runs that exact commit with no secrets, then posts ci/circleci: gate on the PR. The label stays on, so new pushes keep running. After 14 quiet days it's removed.
  • ci: caches are saved from main only. The orb's conftest policies keep secrets off any job that runs PR code. Commit messages are checked against the Conventional Commit rule. PR title and description checks stay on Forgejo.
  • build(dev): mise.toml and mise.lock (pinned tools and the deps, lint, test, ci:smoke, validate tasks), pre-commit hooks (commit-msg included) and a Chunk sidecar config.
  • docs: docs/ci.md with flow diagrams; CONTRIBUTING.md's CI lines and a section on the tools (mise, the CircleCI CLI and MCP servers, Chunk). mise run lint checks what the branch changed, as CI does.
  • The Forgejo workflows stay until CircleCI has been green for a while. A later PR removes them.

Measurements

Verdict: no regression: run_all.py --strict has the same 4 failures on main and on this PR, one more check passes here, and the wall time is the same within noise.

main: 9c9d69ed (2026-11-14)
this PR: ci/circleci @ 02409403 (tests/run_all.py is the same in the head, b866a018)
Hardware: devbox, x86_64 Linux, Docker; one Ubuntu 24.04 image with test-linux's packages (no game, no GPU)
Scene: none: CI only
Method: run_all.py --strict, main and this PR interleaved (main, PR, main, PR), 2 runs each, the median
How to reproduce: the same image and python3 -I -B tests/run_all.py --strict on each checkout; mise run validate for the CI config

Before: main @ 9c9d69ed After: this PR @ 02409403
CI on a pull request title, description, third-party, actionlint lint, third-party, plus the affected checks and builds, then gate
run_all.py --strict (Linux, 24.04 image): passed / failed / skipped 550 / 4 / 16 551 / 4 / 16
run_all.py --strict wall time 215 s 213 s
runs: main 550/4/16 216 s, PR 551/4/16 214 s, main 550/4/16 214 s, PR 551/4/16 212 s
the same 4 checks fail on both sides; test_pad_layout.py fails on main since #137 (hid.c can't find host/hold_toggle.h)

Rebased onto main aa95d35f after measuring: main has gained about 45 commits since (build, renderer, saves, release-script changes); tests/run_all.py still only adds --list, --junit and --exact on top of main's (all checks listed and --shard unchanged), and the CircleCI jobs were re-synced with main's Forgejo workflows (the renderer build check with the committed play profile, #574; the Windows package's third-party objects, #629).

Flags and settings

None. No game switch, menu row, ini key or build flag. CircleCI pipeline parameters (release_dry_run, vulkan_dlss, full) are in docs/ci.md.

Testing

Tested: circleci config validate on every event's merged config with magicbeandev/[email protected]; the orb's conftest policies (28 pass); one workflow per event with the tag null and empty; run_all.py --strict as measured above; tests/test_run_all_junit.py; mise run lint. The same orb and routing run live on the Site repo (PRs, main, fork PRs by label).

Not tested: a live CircleCI run of this repo (it starts once this is on main), the Wine VM job, publishing (dry run until cutover), the website deploy.

Must be tested:

  • CircleCI, after merge: the merge's main run goes green through the release chain in dry run, expecting no publish and ci/circleci: gate green on the commit (by hand: the pipeline and the commit's statuses).

Risks

What could break: a wrong path rule could skip a job a change needed (main runs everything, so main catches it); a CircleCI job could fail where Forgejo passes.

Turning it off: revert this commit; the Forgejo workflows keep running until cutover.

Compatibility: Nothing changes for players: no game, builder or launcher code changes, and releases still come from Forgejo until cutover.

Issues

See #283
See #394

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.
**Type:** ci · **Risk:** low · **Tested on:** Linux (devbox, Docker images of the CI jobs) · **Needs:** a live CircleCI run on main after merge ## Why Pull requests get only the title, description and third-party checks today. The game-free checks and the builders run by hand or at release, so a break is found after it merges. This moves CI and the release pipeline to CircleCI, which runs the checks and builders each pull request needs and nothing more. Until cutover, CircleCI's releases and website deploy are dry runs and Forgejo's workflows stay the live writers. ## What changed CircleCI runs the same checks and builders as the Forgejo workflows, chosen by what a PR changed, with one `ci/circleci: gate` status to require; releases and the website deploy are dry runs until cutover. - tests: `tests/run_all.py` gains `--list` (the checks a run would start, running none), `--junit FILE`, and an opt-in `--exact` (names must match exactly; nothing matching fails closed) that CircleCI's test splitter uses. `--shard`, `--write-times` and substring names are unchanged. Tests: `tests/test_run_all_junit.py`. - ci: `.circleci/config.yml` has one small setup job per event (pull request, push to main, `v*` tag, schedule, manual API run). Each one looks at which areas changed (`.circleci/mapping.conf`: tests, gpu, linux, windows, website, third-party, docs) and continues with `.circleci/shared.yml` plus that event's file in `.circleci/events/`. A docs-only PR runs lint, third-party and `gate`; a `gpu/` change adds the Vulkan builds and SPIR-V fixtures. - ci: the shared setup, fork handling, release sign/publish steps and gate come from one CircleCI orb, `magicbeandev/ci`, used by all three repos (source in [MagicBeanDev/Orb](https://git.magicbean.dev/MagicBeanDev/Orb)). This repo keeps only its own jobs. - ci: the game-free checks run with CircleCI Smarter Testing (`.circleci/test-suites.yml`), split across 4 containers by timing, with JUnit results. - ci: every push to main runs everything, then releases a nightly, as `release.yml` does: the release jobs start only after every check and build passes, so a red main ships nothing. `v*` tags rerun the core checks, then release. Release jobs run main's copy of `scripts/release.py`. Signing (`release-sign`, signing key only) and publishing (`release-publish`, Forgejo token only, one release at a time) are separate jobs. Until cutover, main, tags and the website refresh run as dry runs (throwaway key, nothing published or deployed), so Forgejo's `release.yml` and `website.yml` stay the only writers; the cutover PR turns CircleCI live and removes them. - ci: the Wine checks run on an Ubuntu 24.04 VM, like the Forgejo runner. The other jobs use CircleCI's Arm (Gen2) containers where the tools exist (`docs/ci.md` lists each job and why). - ci: website: a pull request's built site is saved as a CircleCI artifact and linked in one PR comment. Main deploys it with the production token. PR code never runs next to a Cloudflare token. - ci: fork pull requests: CircleCI doesn't build them from Forgejo, so a second CircleCI project on this repo runs `.circleci/forks.yml`, started by a Forgejo webhook. A maintainer adds the `ci: approved` label (Forgejo only lets people with write access do that), and it runs that exact commit with no secrets, then posts `ci/circleci: gate` on the PR. The label stays on, so new pushes keep running. After 14 quiet days it's removed. - ci: caches are saved from main only. The orb's conftest policies keep secrets off any job that runs PR code. Commit messages are checked against the Conventional Commit rule. PR title and description checks stay on Forgejo. - build(dev): `mise.toml` and `mise.lock` (pinned tools and the `deps`, `lint`, `test`, `ci:smoke`, `validate` tasks), pre-commit hooks (commit-msg included) and a Chunk sidecar config. - docs: `docs/ci.md` with flow diagrams; CONTRIBUTING.md's CI lines and a section on the tools (mise, the CircleCI CLI and MCP servers, Chunk). `mise run lint` checks what the branch changed, as CI does. - The Forgejo workflows stay until CircleCI has been green for a while. A later PR removes them. ## Measurements **Verdict:** no regression: `run_all.py --strict` has the same 4 failures on main and on this PR, one more check passes here, and the wall time is the same within noise. main: 9c9d69ed (2026-11-14) this PR: ci/circleci @ 02409403 (`tests/run_all.py` is the same in the head, b866a018) Hardware: devbox, x86_64 Linux, Docker; one Ubuntu 24.04 image with test-linux's packages (no game, no GPU) Scene: none: CI only Method: `run_all.py --strict`, main and this PR interleaved (main, PR, main, PR), 2 runs each, the median How to reproduce: the same image and `python3 -I -B tests/run_all.py --strict` on each checkout; `mise run validate` for the CI config | | Before: main @ 9c9d69ed | After: this PR @ 02409403 | |---|---|---| | CI on a pull request | title, description, third-party, actionlint | lint, third-party, plus the affected checks and builds, then `gate` | | `run_all.py --strict` (Linux, 24.04 image): passed / failed / skipped | 550 / 4 / 16 | 551 / 4 / 16 | | `run_all.py --strict` wall time | 215 s | 213 s | ``` runs: main 550/4/16 216 s, PR 551/4/16 214 s, main 550/4/16 214 s, PR 551/4/16 212 s the same 4 checks fail on both sides; test_pad_layout.py fails on main since #137 (hid.c can't find host/hold_toggle.h) ``` Rebased onto main aa95d35f after measuring: main has gained about 45 commits since (build, renderer, saves, release-script changes); `tests/run_all.py` still only adds `--list`, `--junit` and `--exact` on top of main's (all checks listed and `--shard` unchanged), and the CircleCI jobs were re-synced with main's Forgejo workflows (the renderer build check with the committed play profile, #574; the Windows package's third-party objects, #629). ### Flags and settings None. No game switch, menu row, ini key or build flag. CircleCI pipeline parameters (`release_dry_run`, `vulkan_dlss`, `full`) are in docs/ci.md. ## Testing **Tested:** `circleci config validate` on every event's merged config with `magicbeandev/[email protected]`; the orb's conftest policies (28 pass); one workflow per event with the tag null and empty; `run_all.py --strict` as measured above; `tests/test_run_all_junit.py`; `mise run lint`. The same orb and routing run live on the Site repo (PRs, main, fork PRs by label). **Not tested:** a live CircleCI run of this repo (it starts once this is on main), the Wine VM job, publishing (dry run until cutover), the website deploy. **Must be tested:** - CircleCI, after merge: the merge's main run goes green through the release chain in dry run, expecting no publish and `ci/circleci: gate` green on the commit (by hand: the pipeline and the commit's statuses). ## Risks **What could break:** a wrong path rule could skip a job a change needed (main runs everything, so main catches it); a CircleCI job could fail where Forgejo passes. **Turning it off:** revert this commit; the Forgejo workflows keep running until cutover. **Compatibility:** Nothing changes for players: no game, builder or launcher code changes, and releases still come from Forgejo until cutover. ## Issues See #283 See #394 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated.
docs: describe the CircleCI pipeline and how to contribute
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
6a8e365958
mooki left a comment

Looked over what runs when, at 6a8e3659. Read from the files; where I ran something it says so. Nothing here was seen in a live CircleCI run.

Stops every run as written

1. The fork condition is always true, and it guards the checkout. .circleci/shared.yml:80-105 (get-source) and .circleci/events/fork.yml:4 use greater: [<< pipeline.parameters.fork_pr >>, 0]. greater is not one of CircleCI's logic statements, and the compiler does not reject it: an unknown operator counts as true. I compiled a miniature of get-source through CircleCI's config compiler: with fork_pr at 0 the job gets the "Fork PR checkout" step and no checkout; at 7 the same. So circleci config validate passes, and in every pull request, main and tag pipeline each job that uses get-source (lint, meta, the tests, the builders, the website jobs, the release builds) fetches pull/0/head and fails. Forms that compiled correctly: at step level not: {equal: [0, << pipeline.parameters.fork_pr >>]}; at workflow level when: pipeline.parameters.fork_pr > 0 (inside a step's condition: that expression string also counted as true).

2. meta cannot find the description template. shared.yml:380-384 copies main's checker to /tmp/main-scripts/check_pr_description.py and unpacks the template under /tmp/main-scripts/.forgejo/. The checker looks one folder above itself (scripts/check_pr_description.py:41), that is in /tmp/.forgejo/.

3. website checks a site it has not built. shared.yml:803-804 runs npm ci and npm run check; website/scripts/check-site.ts starts by reading dist. Forgejo's website.yml builds first.

4. Lint installs from a lock file with no Linux Arm entries. The small executor is arm.medium.gen2, the install runs with MISE_LOCKED=1 (shared.yml:255-261), and mise.lock lists linux-x64 and macos-arm64 only. As I read mise's locked mode it refuses a platform the lock does not have; not run.

5. tests/run_all.py loses --shard, which the Forgejo checks still pass. On this head python -I -B tests/run_all.py --shard 1/2 --list ends with error: unrecognized arguments: --shard, and .forgejo/workflows/ci.yml:163 runs tests/run_all.py --strict --shard ... (so does the new scripts/ci/check.py:159-160). The Forgejo source checks would fail from the merge on, while they are meant to keep running alongside.

Differs from the description

6. The nightly does not wait for the checks. .circleci/events/main.yml has two workflows: ci (lines 3-43) and nightly-release (45-62). Signing requires the release builds and notes only, so a red ci does not stop a signed nightly. Each workflow also has its own gate.

7. Main does not run everything. In ci, linux-appimage, windows-builder, windows-vulkan and the website jobs keep their path filters (main.yml:14-30); docs/ci.md:20 says "All areas".

8. Fork approval. .circleci/approvers.txt has no login, so gatekeeper.py:220-224 refuses every label. The gatekeeper also takes the label as a one-time approval that must come after the latest push and removes it (:225-242), where the team summary and the launcher and site descriptions say the label stays and new pushes keep running. And it reads the first 50 open pull requests only (:94); there are over 170 open.

9. full. The description lists a full parameter; .circleci/config.yml:6-27 does not declare it and events/manual.yml has no route for it.

10. Paths. A .circleci/ change is "full" but the website jobs filter on website alone (events/pr.yml:29-41). Forgejo's website.yml also triggers on docs/tested-configurations.md and docs/tested/**; mapping.conf:33 has website/ only. mise.lock, ci/linux/ and .forgejo/ have no rule.

Release chain

11. Main's release.py is run from .ci/main-release/ (shared.yml:223-226), so its root is <checkout>/.ci (scripts/release.py:64). Loaded from there it finds 0 trusted keys and no cliff.toml; from scripts/ it finds 1 key and the file. The verify at shared.yml:1120 then has no key to trust.

12. A nightly's builder version is the word "nightly". shared.yml:1028-1036 turns nightly-<n>-<hash> into VERSION=nightly for dotnet publish -p:Version. Forgejo's build-windows.yml:103-104 takes a numeric version or 0.0.0.

13. Dry-run signing. shared.yml:1107-1108 uses /tmp/dry-sign.pem as the key file, but keygen makes a folder of that name holding release.pem and release.pub.pem (scripts/release.py:715-717), and verify gets no public key for it.

14. released-as ... || true (shared.yml:914) turns a failed check of an earlier release into "none found".

15. The release Windows build restores a compiler cache. release-windows uses the llvm-mingw command, which restores v1-llvm-mingw-* after the hash-checked install (shared.yml:196-207, :1017); docs/ci.md:21 says release builds restore no caches.

16. Two publishers. .forgejo/workflows/release.yml still publishes on main and on v* tags. Which one is the only writer while both run?

Smaller

  • shared.yml:878-886: the comment job, which holds the comment token, falls back to the pull request's copy of four helper scripts when main lacks them (it does today). Failing instead is safer.
  • .circleci/config.yml:79 and :95-97 put fork_sha, the branch and the tag into shell text; checking fork_sha as 40 hex digits and passing the others through the environment avoids surprises.
  • On main the lint range is HEAD..HEAD (shared.yml:322-323).
  • The game-free checks run on Arm (test-linux, arm.large.gen2); checks that skip off x86-64, such as tests/test_fpx_sticky.py:189, then do not run anywhere. Intended?
  • events/release.yml: release-version and gate have no tag filter while the jobs between them do. CircleCI's rule for tags asks for one on every job in the chain (https://circleci.com/docs/guides/orchestrate/workflows/#executing-workflows-for-a-git-tag).

Checked and fine

The three examples hold: a docs change adds nothing to lint, meta and third-party; gpu/ adds the Linux checks, SPIR-V fixtures and Windows Vulkan build; the builder adds its CLI checks and the Windows builder. With fork_pr set, the checkout compares the fetched commit with fork_sha before using it. Signing and publishing ask for separate contexts; the manual dry run has no publish job. run_all.py --list still finds every existing check, and the new JUnit test passes.

Questions

What turns Forgejo's events into the pull_request, push and api events the setup expects, and what posts a failed ci/circleci: gate when a required job fails and the gate job never starts? Which CircleCI settings limit the signing, bot and Cloudflare contexts to main and tags, given that a branch in this repository runs its own configuration?

Looked over what runs when, at `6a8e3659`. Read from the files; where I ran something it says so. Nothing here was seen in a live CircleCI run. ### Stops every run as written **1. The fork condition is always true, and it guards the checkout.** `.circleci/shared.yml:80-105` (`get-source`) and `.circleci/events/fork.yml:4` use `greater: [<< pipeline.parameters.fork_pr >>, 0]`. `greater` is not one of CircleCI's logic statements, and the compiler does not reject it: an unknown operator counts as true. I compiled a miniature of `get-source` through CircleCI's config compiler: with `fork_pr` at 0 the job gets the "Fork PR checkout" step and no `checkout`; at 7 the same. So `circleci config validate` passes, and in every pull request, main and tag pipeline each job that uses `get-source` (lint, meta, the tests, the builders, the website jobs, the release builds) fetches `pull/0/head` and fails. Forms that compiled correctly: at step level `not: {equal: [0, << pipeline.parameters.fork_pr >>]}`; at workflow level `when: pipeline.parameters.fork_pr > 0` (inside a step's `condition:` that expression string also counted as true). **2. `meta` cannot find the description template.** `shared.yml:380-384` copies main's checker to `/tmp/main-scripts/check_pr_description.py` and unpacks the template under `/tmp/main-scripts/.forgejo/`. The checker looks one folder above itself (`scripts/check_pr_description.py:41`), that is in `/tmp/.forgejo/`. **3. `website` checks a site it has not built.** `shared.yml:803-804` runs `npm ci` and `npm run check`; `website/scripts/check-site.ts` starts by reading `dist`. Forgejo's `website.yml` builds first. **4. Lint installs from a lock file with no Linux Arm entries.** The `small` executor is `arm.medium.gen2`, the install runs with `MISE_LOCKED=1` (`shared.yml:255-261`), and `mise.lock` lists `linux-x64` and `macos-arm64` only. As I read mise's locked mode it refuses a platform the lock does not have; not run. **5. `tests/run_all.py` loses `--shard`, which the Forgejo checks still pass.** On this head `python -I -B tests/run_all.py --shard 1/2 --list` ends with `error: unrecognized arguments: --shard`, and `.forgejo/workflows/ci.yml:163` runs `tests/run_all.py --strict --shard ...` (so does the new `scripts/ci/check.py:159-160`). The Forgejo source checks would fail from the merge on, while they are meant to keep running alongside. ### Differs from the description **6. The nightly does not wait for the checks.** `.circleci/events/main.yml` has two workflows: `ci` (lines 3-43) and `nightly-release` (45-62). Signing requires the release builds and notes only, so a red `ci` does not stop a signed nightly. Each workflow also has its own `gate`. **7. Main does not run everything.** In `ci`, `linux-appimage`, `windows-builder`, `windows-vulkan` and the website jobs keep their path filters (`main.yml:14-30`); `docs/ci.md:20` says "All areas". **8. Fork approval.** `.circleci/approvers.txt` has no login, so `gatekeeper.py:220-224` refuses every label. The gatekeeper also takes the label as a one-time approval that must come after the latest push and removes it (`:225-242`), where the team summary and the launcher and site descriptions say the label stays and new pushes keep running. And it reads the first 50 open pull requests only (`:94`); there are over 170 open. **9. `full`.** The description lists a `full` parameter; `.circleci/config.yml:6-27` does not declare it and `events/manual.yml` has no route for it. **10. Paths.** A `.circleci/` change is "full" but the website jobs filter on `website` alone (`events/pr.yml:29-41`). Forgejo's `website.yml` also triggers on `docs/tested-configurations.md` and `docs/tested/**`; `mapping.conf:33` has `website/` only. `mise.lock`, `ci/linux/` and `.forgejo/` have no rule. ### Release chain **11. Main's `release.py` is run from `.ci/main-release/`** (`shared.yml:223-226`), so its root is `<checkout>/.ci` (`scripts/release.py:64`). Loaded from there it finds 0 trusted keys and no `cliff.toml`; from `scripts/` it finds 1 key and the file. The `verify` at `shared.yml:1120` then has no key to trust. **12. A nightly's builder version is the word "nightly".** `shared.yml:1028-1036` turns `nightly-<n>-<hash>` into `VERSION=nightly` for `dotnet publish -p:Version`. Forgejo's `build-windows.yml:103-104` takes a numeric version or `0.0.0`. **13. Dry-run signing.** `shared.yml:1107-1108` uses `/tmp/dry-sign.pem` as the key file, but `keygen` makes a folder of that name holding `release.pem` and `release.pub.pem` (`scripts/release.py:715-717`), and `verify` gets no public key for it. **14. `released-as ... || true`** (`shared.yml:914`) turns a failed check of an earlier release into "none found". **15. The release Windows build restores a compiler cache.** `release-windows` uses the `llvm-mingw` command, which restores `v1-llvm-mingw-*` after the hash-checked install (`shared.yml:196-207`, `:1017`); `docs/ci.md:21` says release builds restore no caches. **16. Two publishers.** `.forgejo/workflows/release.yml` still publishes on main and on `v*` tags. Which one is the only writer while both run? ### Smaller - `shared.yml:878-886`: the comment job, which holds the comment token, falls back to the pull request's copy of four helper scripts when main lacks them (it does today). Failing instead is safer. - `.circleci/config.yml:79` and `:95-97` put `fork_sha`, the branch and the tag into shell text; checking `fork_sha` as 40 hex digits and passing the others through the environment avoids surprises. - On main the lint range is `HEAD..HEAD` (`shared.yml:322-323`). - The game-free checks run on Arm (`test-linux`, `arm.large.gen2`); checks that skip off x86-64, such as `tests/test_fpx_sticky.py:189`, then do not run anywhere. Intended? - `events/release.yml`: `release-version` and `gate` have no tag filter while the jobs between them do. CircleCI's rule for tags asks for one on every job in the chain (https://circleci.com/docs/guides/orchestrate/workflows/#executing-workflows-for-a-git-tag). ### Checked and fine The three examples hold: a docs change adds nothing to lint, meta and third-party; `gpu/` adds the Linux checks, SPIR-V fixtures and Windows Vulkan build; the builder adds its CLI checks and the Windows builder. With `fork_pr` set, the checkout compares the fetched commit with `fork_sha` before using it. Signing and publishing ask for separate contexts; the manual dry run has no publish job. `run_all.py --list` still finds every existing check, and the new JUnit test passes. ### Questions What turns Forgejo's events into the `pull_request`, `push` and `api` events the setup expects, and what posts a failed `ci/circleci: gate` when a required job fails and the gate job never starts? Which CircleCI settings limit the signing, bot and Cloudflare contexts to main and tags, given that a branch in this repository runs its own configuration?
Contributor

Thanks for this. It's the first ask in #394 (CI that gates merges), so I'm keen to see it land. mooki's review covers the config itself; these are the process points around the cutover. They apply to MagicBeanDev/LongshotLauncher#65 and MagicBeanDev/Site#10 too.

1. Make ci/circleci: gate a required check on main as part of the cutover. Today main's protection requires no status checks in Longshot or LongshotLauncher, and Site's main isn't protected at all, so a red gate wouldn't stop a merge. Until the gate is required it's advice only. It's also what Forgejo's "merge when checks pass" needs if we use that in place of a merge bot: with no required check, it merges straight away.

2. Get main green first. Once the gate is required and the nightly waits for it (mooki's point 6), a red main blocks every merge and every nightly. test_pad_layout.py (failing since #137) needs fixing before the switch. After that, when main goes red, reverting the PR that broke it should be the first move, so nightlies don't stall.

3. The fork label becomes a step before every merge. Most contributors work from forks. With the gate required, a fork PR can't merge until a maintainer adds ci: approved, and in this PR the label is needed again after every push. With approvers.txt empty, that's one maintainer's waking hours again, so fork PRs would wait overnight for a label instead of a merge. Two asks before the cutover:

  • Here, the behaviour the launcher and Site descriptions give: the label stays on until the PR has been idle for 14 days. The jobs that run fork code get no secrets either way.
  • People in approvers.txt spread across timezones.

4. Where the release key lives. The signing key moves from a Forgejo secret to a CircleCI context. Keeping it to the sign job is good, but it does mean an outside service holds the key that signs every build the launcher installs. That may well be the right call; I'd just like the team to make it on purpose, and note it in RELEASING.md, rather than have it arrive with the CI move. This goes with mooki's question about how the contexts are kept to main and tags.

Thanks for this. It's the first ask in #394 (CI that gates merges), so I'm keen to see it land. mooki's review covers the config itself; these are the process points around the cutover. They apply to MagicBeanDev/LongshotLauncher#65 and MagicBeanDev/Site#10 too. **1. Make `ci/circleci: gate` a required check on main as part of the cutover.** Today main's protection requires no status checks in Longshot or LongshotLauncher, and Site's main isn't protected at all, so a red gate wouldn't stop a merge. Until the gate is required it's advice only. It's also what Forgejo's "merge when checks pass" needs if we use that in place of a merge bot: with no required check, it merges straight away. **2. Get main green first.** Once the gate is required and the nightly waits for it (mooki's point 6), a red main blocks every merge and every nightly. `test_pad_layout.py` (failing since #137) needs fixing before the switch. After that, when main goes red, reverting the PR that broke it should be the first move, so nightlies don't stall. **3. The fork label becomes a step before every merge.** Most contributors work from forks. With the gate required, a fork PR can't merge until a maintainer adds `ci: approved`, and in this PR the label is needed again after every push. With `approvers.txt` empty, that's one maintainer's waking hours again, so fork PRs would wait overnight for a label instead of a merge. Two asks before the cutover: - Here, the behaviour the launcher and Site descriptions give: the label stays on until the PR has been idle for 14 days. The jobs that run fork code get no secrets either way. - People in `approvers.txt` spread across timezones. **4. Where the release key lives.** The signing key moves from a Forgejo secret to a CircleCI context. Keeping it to the sign job is good, but it does mean an outside service holds the key that signs every build the launcher installs. That may well be the right call; I'd just like the team to make it on purpose, and note it in RELEASING.md, rather than have it arrive with the CI move. This goes with mooki's question about how the contexts are kept to main and tags.
drcrow force-pushed ci/circleci from 6a8e365958
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to b866a018e3
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 16:47:33 +00:00
Compare
Author
Member

Thanks @mooki and @madebyjordan, both reviews were really useful. Since 6a8e3659 the branch has been reworked: the shared setup, fork handling, signing and publishing moved into one orb (magicbeandev/ci), and the branch is rebuilt on current main. So some points are fixed by the rework and some by specific changes. Point by point:

mooki

  1. greater: is always true: confirmed with circleci config process. The fork workflow now uses when: pipeline.parameters.fork_pr > 0, which is dropped at 0 and kept at 7. The old get-source step condition is gone: fork checkout is a separate orb command, used only in the fork workflow.
  2. meta template path: gone. PR title and description checks stay on Forgejo, so CircleCI no longer runs check_pr_description.py.
  3. website checks an unbuilt site: it now runs npm run build before npm run check.
  4. Arm lint with a linux-x64-only lock: the lock now has linux-arm64 entries.
  5. --shard: you were right, and it was worse than that: our change also made name selection exact-only, which broke run_all.py ub_. Redone as purely additive. --shard, --write-times and substring names are as on main; new are --list, --junit FILE, and an opt-in --exact (fail closed) that only CircleCI's splitter uses. tests/test_run_all_junit.py checks that --shard 1/2 and 2/2 cover every check exactly once.
  6. The nightly doesn't wait for the checks: it's now one workflow. release-version requires lint, third-party, test-linux, builder-cli, spirv-fixtures and the builds, so a red main signs nothing. A v* tag reruns the core checks before it builds anything.
  7. Main doesn't run everything: main is force-full now.
  8. Fork approval: approvers.txt and the gatekeeper are gone. The label is the approval (Forgejo only lets people with write access add labels), it's sticky, so new pushes keep running, and it's removed after 14 quiet days. The new doorbell is webhook-driven, so there's no 50-PR scan.
  9. full: declared in config.yml and used by main, tags and API runs.
  10. Paths: .circleci/ is full and includes the website jobs. The website jobs also run for docs/tested-configurations.md and docs/tested/**. mise.lock is full; .forgejo/ is lint only.
  11. release.py root: main's copy now goes to .mb-main/release.py in the checkout, so its root is the checkout: it finds the trusted key and cliff.toml. The orb's sign and publish jobs had the same bug and are fixed the same way.
  12. Nightly builder version: now numeric, or 0.0.0, as in build-windows.yml.
  13. Dry-run signing: the orb's release-sign uses keygen's release.pem and release.pub.pem and verifies against the public key.
  14. released-as ... || true: removed.
  15. Release Windows build cache: llvm-mingw takes a cache parameter, and release builds pass false.
  16. Two publishers: until cutover, CircleCI runs the main and tag chains and the website deploy as dry runs, so Forgejo's release.yml and website.yml stay the only writers. The cutover PR turns CircleCI live and removes those workflows in the same commit.

Smaller ones:

  • Comment-job fallback to the PR's scripts: gone; the preview comment is an orb command now.
  • fork_sha, branch and tag in shell text: they go through environment: now, and the orb's checkout refuses a fork_sha that isn't 40 hex digits.
  • Lint range on main: no longer HEAD..HEAD; it compares against HEAD~1.
  • Arm and x86-only checks: test-linux runs on x86 (large), so test_fpx_sticky and the other x86-only checks still run.
  • Tag filters: every job in events/release.yml has one now, and so do the tag setup and noop jobs.

Questions:

  • Events: CircleCI's Forgejo integration turns pushes and same-repo PRs into push and pull_request pipelines; api is a manual run. Fork PRs come in through a second CircleCI project on this repo, started by a Forgejo webhook.
  • A failed job and gate: each job posts its own ci/circleci: <job> status, so the cause shows red on the commit; gate never appears. Once gate is required, that blocks the merge. I'd rather not give gate an API token on PR pipelines just to turn it red.
  • How contexts are kept to main and tags: each context has an expression restriction, e.g. release-signing is pipeline.git.branch == "main" or pipeline.git.tag starts-with "v" and is limited to the Longshot and Launcher projects. Those values come from the event, not from the config, so a branch's own config can't change them.

Jordan

  1. Required gate: agreed. It's on the cutover checklist, along with protecting Site's main and v* tags, before "merge when checks pass" is turned on.
  2. Main green first: agreed. test_pad_layout.py needs fixing before the gate is required, and reverting first when main goes red makes sense.
  3. The fork label: sticky now, as you asked. With no approvers list, anyone with write access can approve, which spreads it across time zones.
  4. The signing key: fair, it should be a deliberate team decision. It's on the agenda with the owner, and RELEASING.md will say where the key lives and who can rotate it before CircleCI signs anything real. Until then, signing on CircleCI only ever uses a throwaway key.

The orb's source will be in MagicBeanDev/orb. The owner is creating it, and I'll link the first PR here as soon as it's up.

Thanks @mooki and @madebyjordan, both reviews were really useful. Since `6a8e3659` the branch has been reworked: the shared setup, fork handling, signing and publishing moved into one orb (`magicbeandev/ci`), and the branch is rebuilt on current main. So some points are fixed by the rework and some by specific changes. Point by point: **mooki** 1. **`greater:` is always true:** confirmed with `circleci config process`. The fork workflow now uses `when: pipeline.parameters.fork_pr > 0`, which is dropped at 0 and kept at 7. The old `get-source` step condition is gone: fork checkout is a separate orb command, used only in the fork workflow. 2. **`meta` template path:** gone. PR title and description checks stay on Forgejo, so CircleCI no longer runs `check_pr_description.py`. 3. **`website` checks an unbuilt site:** it now runs `npm run build` before `npm run check`. 4. **Arm lint with a linux-x64-only lock:** the lock now has `linux-arm64` entries. 5. **`--shard`:** you were right, and it was worse than that: our change also made name selection exact-only, which broke `run_all.py ub_`. Redone as purely additive. `--shard`, `--write-times` and substring names are as on main; new are `--list`, `--junit FILE`, and an opt-in `--exact` (fail closed) that only CircleCI's splitter uses. `tests/test_run_all_junit.py` checks that `--shard 1/2` and `2/2` cover every check exactly once. 6. **The nightly doesn't wait for the checks:** it's now one workflow. `release-version` requires lint, third-party, test-linux, builder-cli, spirv-fixtures and the builds, so a red main signs nothing. A `v*` tag reruns the core checks before it builds anything. 7. **Main doesn't run everything:** main is `force-full` now. 8. **Fork approval:** `approvers.txt` and the gatekeeper are gone. The label is the approval (Forgejo only lets people with write access add labels), it's sticky, so new pushes keep running, and it's removed after 14 quiet days. The new doorbell is webhook-driven, so there's no 50-PR scan. 9. **`full`:** declared in `config.yml` and used by main, tags and API runs. 10. **Paths:** `.circleci/` is full and includes the website jobs. The website jobs also run for `docs/tested-configurations.md` and `docs/tested/**`. `mise.lock` is full; `.forgejo/` is lint only. 11. **`release.py` root:** main's copy now goes to `.mb-main/release.py` in the checkout, so its root is the checkout: it finds the trusted key and `cliff.toml`. The orb's sign and publish jobs had the same bug and are fixed the same way. 12. **Nightly builder version:** now numeric, or `0.0.0`, as in `build-windows.yml`. 13. **Dry-run signing:** the orb's `release-sign` uses `keygen`'s `release.pem` and `release.pub.pem` and verifies against the public key. 14. **`released-as ... || true`:** removed. 15. **Release Windows build cache:** `llvm-mingw` takes a `cache` parameter, and release builds pass `false`. 16. **Two publishers:** until cutover, CircleCI runs the main and tag chains and the website deploy as dry runs, so Forgejo's `release.yml` and `website.yml` stay the only writers. The cutover PR turns CircleCI live and removes those workflows in the same commit. Smaller ones: - **Comment-job fallback to the PR's scripts:** gone; the preview comment is an orb command now. - **`fork_sha`, branch and tag in shell text:** they go through `environment:` now, and the orb's checkout refuses a `fork_sha` that isn't 40 hex digits. - **Lint range on main:** no longer `HEAD..HEAD`; it compares against `HEAD~1`. - **Arm and x86-only checks:** `test-linux` runs on x86 (`large`), so `test_fpx_sticky` and the other x86-only checks still run. - **Tag filters:** every job in `events/release.yml` has one now, and so do the tag setup and noop jobs. Questions: - **Events:** CircleCI's Forgejo integration turns pushes and same-repo PRs into `push` and `pull_request` pipelines; `api` is a manual run. Fork PRs come in through a second CircleCI project on this repo, started by a Forgejo webhook. - **A failed job and `gate`:** each job posts its own `ci/circleci: <job>` status, so the cause shows red on the commit; `gate` never appears. Once `gate` is required, that blocks the merge. I'd rather not give `gate` an API token on PR pipelines just to turn it red. - **How contexts are kept to main and tags:** each context has an expression restriction, e.g. `release-signing` is `pipeline.git.branch == "main" or pipeline.git.tag starts-with "v"` and is limited to the Longshot and Launcher projects. Those values come from the event, not from the config, so a branch's own config can't change them. **Jordan** 1. **Required gate:** agreed. It's on the cutover checklist, along with protecting Site's main and `v*` tags, before "merge when checks pass" is turned on. 2. **Main green first:** agreed. `test_pad_layout.py` needs fixing before the gate is required, and reverting first when main goes red makes sense. 3. **The fork label:** sticky now, as you asked. With no approvers list, anyone with write access can approve, which spreads it across time zones. 4. **The signing key:** fair, it should be a deliberate team decision. It's on the agenda with the owner, and RELEASING.md will say where the key lives and who can rotate it before CircleCI signs anything real. Until then, signing on CircleCI only ever uses a throwaway key. The orb's source will be in MagicBeanDev/orb. The owner is creating it, and I'll link the first PR here as soon as it's up.
drcrow force-pushed ci/circleci from b866a018e3
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 28e4c0f709
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 9s
Third-party parts / As their notes say (pull_request) Successful in 13s
PR description / Description follows the template (pull_request_target) Successful in 5s
2026-11-14 17:48:57 +00:00
Compare
drcrow changed title from WIP: ci: run checks, builds, releases and the website on CircleCI to ci: run checks, builds, releases and the website on CircleCI 2026-11-14 17:49:03 +00:00
Member

Looked over the rework at head 28e4c0f709ebf2d94b55690992b4489b5e460a16. It is the same six commits as b866a018 rebased on main 543846eb; git range-diff shows the patch did not change. I also read the orb at https://git.magicbean.dev/MagicBeanDev/Orb (tag v0.1.0, 4a5209a32b9f651197340514dd336a87cf5ffc0e). It became readable ten minutes after the answer, so nothing waits for its source any more. Read from the files; where I ran something it says so. Nothing here was seen in a live CircleCI run.

Settled at the new head

Points 1, 2, 3, 5, 7, 10, 11, 12, 14, 15, the five smaller points and Jordan's 3 hold.

  • 1: with CircleCI's compiler (circleci config process, orb packed in) the fork workflow is dropped at fork_pr 0, kept at 7, and a PR's lint starts with checkout.
    # Fork PR pipeline (fork_pr / fork_sha from mb/fork-doorbell). Docs: docs/ci.md.
    workflows:
    fork-pr:
    when: pipeline.parameters.fork_pr > 0
  • 5: the new test passes, --shard 1/2 plus 2/2 give all 556 checks, ub_ still picks its 5.
    def test_shards_cover_every_check_once(self):
    _, everything = listed()
    _, first = listed("--shard", "1/2")
    _, second = listed("--shard", "2/2")
    self.assertEqual(sorted(first + second), sorted(everything))
    self.assertEqual(len(set(first + second)), len(everything))
  • 11 and 13: main's release.py loaded from .mb-main/ finds the trusted key and cliff.toml; the orb's dry-run signing, run on stand-in files, signs and verifies with the public key.
    keydir=$(mktemp -d)
    trap 'rm -rf "$keydir"' EXIT
    if [ "${DRY_RUN:-false}" = true ]; then
    python3 -I "$rel" keygen "$keydir"
    else
    [ -n "${RELEASE_SIGNING_KEY:-}" ] || { echo "no RELEASE_SIGNING_KEY (release-signing context)" >&2; exit 1; }
    umask 077
    printf '%s\n' "$RELEASE_SIGNING_KEY" > "$keydir/release.pem"
    fi
    python3 -I "$rel" sign "$files/SHA256SUMS" --key "$keydir/release.pem" --out "$files/SHA256SUMS.sig"
    if [ "${DRY_RUN:-false}" = true ]; then
    python3 -I "$rel" verify "$files/SHA256SUMS" --sig "$files/SHA256SUMS.sig" --pub "$keydir/release.pub.pem" --files "$files"
    else
    python3 -I "$rel" verify "$files/SHA256SUMS" --files "$files"
    fi
  • 8 and Jordan's 3: the sticky label, the 14 quiet days and the missing 50-PR scan are in the orb's scripts, and its own bats tests pass here.
    #!/usr/bin/env bash
    # Stale sweep, for open fork PRs carrying the approval label. Fails closed: if the timeline or the
    # statuses cannot be read, the PR is skipped and nothing is changed.
    # 1. A gate status that is still `pending` after 6 h becomes `error`.
    # 2. If the newest of (last pull_push, last label add; the PR creation when neither exists) is older
    # than DAYS days, remove the label and comment once.
    # shellcheck disable=SC2016 # jq programs use $vars on purpose
    set -euo pipefail
    : "${REPO:?}" "${FORGEJO_URL:?}" "${FORGEJO_TOKEN:?}"
    LABEL="${LABEL:-ci: approved}"
    DAYS="${DAYS:-14}"
    # shellcheck disable=SC1090,SC1091 # forgejo-lib.sh is written to /tmp by the job first
    . "${FORGEJO_LIB:-/tmp/forgejo-lib.sh}"
    now=$(date +%s)
    cutoff=$((now - DAYS * 86400))
    msg="Approval expired after $DAYS days without new commits. A maintainer can re-add \`$LABEL\` to resume CI."
    ev() { jq -r "$@" | newest; }
    pulls=$(list "pulls?state=open") || { echo "cannot list PRs; nothing changed"; exit 0; }
    printf '%s' "$pulls" | jq -c --arg l "$LABEL" '.[] | select(.head.repo.id != null and .head.repo.id != .base.repo.id
    and ([.labels[].name] | index($l) != null))
    | {n: .number, sha: .head.sha, created: .created_at, lid: (.labels[] | select(.name == $l) | .id)}' |
    while read -r pr; do
    n=$(printf '%s' "$pr" | jq -r .n); sha=$(printf '%s' "$pr" | jq -r .sha)
    if ! statuses=$(list "statuses/$sha?") || ! timeline=$(list "issues/$n/timeline?"); then
    echo "PR $n: cannot read statuses or timeline, skipped"; continue
    fi
    stuck=$(printf '%s' "$statuses" | jq -r '[.[] | select(.context == "ci/circleci: gate")] | sort_by(.created_at)
    | last // empty | select(.state == "pending") | .created_at')
    if [ -n "$stuck" ] && [ "$(date -d "$stuck" +%s)" -lt $((now - 21600)) ]; then
    api -X POST -d "$(jq -nc '{context: "ci/circleci: gate", state: "error", description: "CI run did not finish; re-add ci: approved"}')" \
    "$FORGEJO_URL/api/v1/repos/$REPO/statuses/$sha" > /dev/null
    echo "PR $n: stuck pending gate set to error"
    fi
    at=$(printf '%s' "$timeline" | ev --arg l "$LABEL" '.[] | select(.type == "pull_push" or (.type == "label" and .body == "1" and .label.name == $l)) | .created_at')
    if [ "$at" -eq 0 ]; then at=$(date -d "$(printf '%s' "$pr" | jq -r .created)" +%s); fi
    if [ "$at" -lt "$cutoff" ]; then
    api -X DELETE "$FORGEJO_URL/api/v1/repos/$REPO/issues/$n/labels/$(printf '%s' "$pr" | jq -r .lid)" > /dev/null
    api -X POST -d "$(jq -nc --arg b "$msg" '{body: $b}')" "$FORGEJO_URL/api/v1/repos/$REPO/issues/$n/comments" > /dev/null
    echo "PR $n: approval expired, label removed"
    else
    echo "PR $n: fresh, kept"
    fi
    done
    The daily schedule that runs the sweep is a CircleCI setting I cannot see.

Only partly there

  • 4: mise.lock has linux-arm64 for six tools, not python (
    Lines 131 to 144 in 28e4c0f
    [[tools.python]]
    version = "3.12.15"
    backend = "core:python"
    specifiers = ["3.12.15"]
    [tools.python."platforms.linux-x64"]
    checksum = "sha256:6d7521138fd33afddc1ccd08abb007d9a2829e38117c2576f9efdc0ec73ce772"
    url = "https://github.com/astral-sh/python-build-standalone/releases/download/20261021/cpython-3.12.15+20261021-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
    provenance = "github-attestations"
    [tools.python."platforms.macos-arm64"]
    checksum = "sha256:7b8d00b751db21cea87dc551a8f8165147c5fbc5251d5537d7e5497dde00a219"
    url = "https://github.com/astral-sh/python-build-standalone/releases/download/20261021/cpython-3.12.15+20261021-aarch64-apple-darwin-install_only_stripped.tar.gz"
    provenance = "github-attestations"
    ), and the orb's install no longer sets MISE_LOCKED (
    echo "export PATH=\"$bin:\$HOME/.local/share/mise/shims:\$PATH\"" >> "${BASH_ENV:-/dev/null}"
    export PATH="$bin:$HOME/.local/share/mise/shims:$PATH"
    mise trust -qy "${MISE_DIR:-.}"
    mise install
    ), so the lock is not enforced.
  • 9: full is declared, but an API run with only full: true goes to setup-noop-api and does nothing (
    Lines 140 to 148 in 28e4c0f
    setup-noop-api:
    when:
    and:
    - equal: [api, << pipeline.event.name >>]
    - equal: [false, << pipeline.parameters.release_dry_run >>]
    - equal: [false, << pipeline.parameters.vulkan_dlss >>]
    jobs:
    - setup-noop:
    name: setup-noop-api
    ). The PR body says it forces everything.
  • 6 and 16 hold as graphs and dry-run switches, but see new problems 1 and 2.

New problems from the rework

  1. No release tag is ever made on main. setup-main and setup-manual pass nightly: false (
    {"nightly":false,"release_dry_run":true,"website_refresh":false,
    ), and release-version only makes a nightly tag when it is true (
    Lines 840 to 854 in 28e4c0f
    elif [ "<< pipeline.parameters.nightly >>" = true ]; then
    head=$(git rev-parse origin/main)
    if [ "$head" != "$ref" ]; then
    echo "main moved on: skip nightly for $ref"
    tag=""
    else
    last=$(git tag -l 'nightly-*' --sort=-version:refname | head -1)
    if [ -n "$last" ] && git diff --quiet "${last}^{commit}" HEAD -- . ':(exclude)docs/**' ':(exclude)*.md'; then
    echo "no shipped changes since $last"
    tag=""
    else
    tag=$(python3 -I "$MAIN_RELEASE_PY" nightly-name)
    fi
    fi
    fi
    ). I ran that step as compiled: release.env is TAG=. Every release job halts, no nightly ships after the cutover, and the dry run tests nothing.
  2. release-stage fails on every tag run. It runs cp -a tools.lock files/ in a job with no checkout, and no job saves tools.lock (
    Lines 993 to 1026 in 28e4c0f
    release-stage:
    executor: small
    steps:
    - attach_workspace:
    at: .
    - run:
    name: Collect release artifacts into files/
    command: |
    set -euo pipefail
    . release.env
    if [ -z "${TAG:-}" ]; then
    circleci-agent step halt
    fi
    rm -rf files
    mkdir -p files
    shopt -s nullglob
    for f in build-linux/Longshot-"$TAG"-x86_64.AppImage build-linux/*.AppImage; do
    if [ -f "$f" ]; then
    cp -a "$f" "files/Longshot-${TAG}-x86_64.AppImage"
    break
    fi
    done
    for f in dist/LongshotBuilder-"$TAG"-windows-x64.zip dist/*.zip; do
    if [ -f "$f" ]; then
    cp -a "$f" "files/LongshotBuilder-${TAG}-windows-x64.zip"
    break
    fi
    done
    cp -a tools.lock files/
    ls -la files/
    - persist_to_workspace:
    root: .
    paths:
    - files
    ). I ran the compiled step on a workspace holding the saved paths: cp: cannot stat 'tools.lock', exit 1, after two 90-minute builds.
  3. test-linux, fork-test-linux and spirv-fixtures cannot set up. They run on a bare ubuntu:24.04 (
    Lines 38 to 45 in 28e4c0f
    ubuntu2404-large:
    docker:
    - image: ubuntu:24.04@sha256:2198e4c65076e9cb0b3d695219df667c0e3b0072da64ca3c2648aca17dcecaf5
    resource_class: large
    ubuntu2404:
    docker:
    - image: ubuntu:24.04@sha256:2198e4c65076e9cb0b3d695219df667c0e3b0072da64ca3c2648aca17dcecaf5
    resource_class: medium
    ). test-deps-linux runs git first (through fetch_commit.sh), then pip and curl, but only installs g++, clang, spirv-tools, ccache and libdav1d (
    Lines 116 to 174 in 28e4c0f
    Preview has been truncated
    test-deps-linux:
    steps:
    - run:
    name: Pinned SPIR-V and Vulkan headers (.forgejo/workflows/ci.yml)
    command: |
    set -euo pipefail
    scripts/fetch_commit.sh https://github.com/KhronosGroup/SPIRV-Headers \
    04f10f650d514df88b76d25e83db360142c7b174 _deps/spirv-headers
    scripts/fetch_commit.sh https://github.com/KhronosGroup/Vulkan-Headers \
    74d8a6cb930c68ef617b202c3ff3c59d919e086b _deps/vulkan-headers
    - run:
    name: Compilers and SPIR-V validator (Forgejo ci.yml)
    command: |
    set -euo pipefail
    export DEBIAN_FRONTEND=noninteractive
    apt-get update -qq
    apt-get install -y --no-install-recommends g++ clang spirv-tools ccache libdav1d-dev
    - run:
    name: Python packages for the tests (Forgejo ci.yml)
    command: |
    set -euo pipefail
    python3 -m pip install --disable-pip-version-check --break-system-packages --no-deps \
    zstandard==0.25.0 pillow==13.0.0 numpy==2.5.4
    - run:
    name: Pinned glslang 16.6.0 (x86_64 release tarball)
    command: |
    set -euo pipefail
    tool_dir="$HOME/glslang-16.6.0"
    mkdir -p "$tool_dir"
    curl -fL --retry 3 -o "$tool_dir/glslang.tar.gz" \
    https://github.com/KhronosGroup/glslang/releases/download/16.6.0/glslang-16.6.0-linux-x86_64-release.tar.gz
    echo '2c34071f56ecf39d16233294d3739cf43f981fd39080f2fd9dccb782101191ce '"$tool_dir/glslang.tar.gz" | sha256sum -c --strict
    tar -xzf "$tool_dir/glslang.tar.gz" -C "$tool_dir"
    "$tool_dir/bin/glslangValidator" --version | head -1
    - run:
    name: Compiler cache and test environment
    command: |
    set -euo pipefail
    mkdir -p "$HOME/ccache" "$HOME/ccache-bin"
    for c in cc c++ gcc g++ clang clang++; do ln -sf "$(command -v ccache)" "$HOME/ccache-bin/$c"; done
    {
    echo "export LONGSHOT_SPIRV_HEADERS=$PWD/_deps/spirv-headers/include"
    echo "export LONGSHOT_VULKAN_HEADERS=$PWD/_deps/vulkan-headers/include"
    echo "export PATH=\"$HOME/glslang-16.6.0/bin:$HOME/ccache-bin:\$PATH\""
    echo 'export CCACHE_DIR=$HOME/ccache CCACHE_MAXSIZE=400M'
    } >> "$BASH_ENV"
    if ! command -v circleci >/dev/null; then
    if [ -n "${CIRCLECI:-}" ] && [ "${CIRCLECI_LOCAL_HARNESS:-}" != "1" ]; then
    echo "circleci CLI should be injected by the CircleCI agent in docker jobs"
    exit 1
    ). Canonical's package list for that image has no git, curl, python3 or ca-certificates, and that apt line pulls none in (I computed the dependency closure). The first head installed them. spirv-fixtures installs git but no ca-certificates before it fetches from github.com; that part is my inference.
  4. The policy check cannot fire. It looks for the step name get-source (
    job_uses_get_source(job) if {
    some s in job.steps
    s == "get-source"
    }
    job_uses_get_source(job) if {
    some s in top_level_step_names(job)
    s == "get-source"
    }
    job_uses_get_source(job) if {
    some s in nested_step_names(job)
    s == "get-source"
    }
    ); the jobs use mb/get-source. conftest on the real merged configs passes 32 tests, and still passes with release-signing or cloudflare-prod attached to lint or test-linux. The PR body says the policies keep secrets off any job that runs PR code.
  5. The gate's name is probably wrong. mb/gate has no name: (
    - mb/gate:
    ), and CircleCI names a status ci/circleci: <job_name> (https://circleci.com/docs/guides/integration/status-updates/). That gives ci/circleci: mb/gate, not the ci/circleci: gate that the docs, the orb and the fork reports use. The Forgejo integration is not documented, so this needs one live run.
  6. Manual API runs. release_dry_run and vulkan_dlss are declared in both the setup and the continued config and passed back in (
    Lines 113 to 117 in 28e4c0f
    {"full":true,"nightly":false,
    "release_dry_run":<< pipeline.parameters.release_dry_run >>,
    "website_refresh":false,
    "vulkan_dlss":<< pipeline.parameters.vulkan_dlss >>,
    "fork_pr":0,"fork_sha":""}
    ). CircleCI lists that as the "Conflicting pipeline parameters" error for API runs with a non-default value (https://circleci.com/docs/guides/orchestrate/dynamic-config/). Not run.

Still open from the first review and not in the answer: release-linux installs g++-12 only (

Lines 891 to 892 in 28e4c0f
apt-get update -qq && apt-get install -y --no-install-recommends git python3 curl ca-certificates xz-utils zstd \
build-essential g++-12 clang cmake ninja-build pkg-config

); the fresh release zip and AppImage get none of the Wine, PE or player-build checks; tags and nightlies share one serial group, so CircleCI skips the older pipeline's publish (

serial-group: << pipeline.project.slug >>/release

); preview links ignore page depth; website deploy has no check.

Waiting for settings, the registry or the cutover

What turns Forgejo events into pipelines (CircleCI's docs list no Forgejo), the status names, the context restrictions, the schedules, and whether the registry's version 0.1.0 of magicbeandev/ci is the tagged source. CircleCI says restricting contexts for custom webhook triggers is not supported (https://circleci.com/docs/guides/security/contexts/), and the expression quoted for release-signing lacks the not job.ssh.enabled and not (pipeline.config_source starts-with "api") parts of CircleCI's own example. The required gate, protection of Site's main and the v* tags, RELEASING.md on the signing key and test_pad_layout.py are promises; none is in the diff.

Questions

  1. Does the forgejo-comment context define FORGEJO_TOKEN? The orb's preview comment needs that name (
    : "${REPO:?}" "${FORGEJO_URL:?}" "${FORGEJO_TOKEN:?}"
    ); the first head's script read FORGEJO_COMMENT_TOKEN.
  2. How is forgejo-bot restricted in the forks project, given the custom webhook limit above?
Looked over the rework at head `28e4c0f709ebf2d94b55690992b4489b5e460a16`. It is the same six commits as `b866a018` rebased on main `543846eb`; `git range-diff` shows the patch did not change. I also read the orb at https://git.magicbean.dev/MagicBeanDev/Orb (tag v0.1.0, `4a5209a32b9f651197340514dd336a87cf5ffc0e`). It became readable ten minutes after the answer, so nothing waits for its source any more. Read from the files; where I ran something it says so. Nothing here was seen in a live CircleCI run. ### Settled at the new head Points 1, 2, 3, 5, 7, 10, 11, 12, 14, 15, the five smaller points and Jordan's 3 hold. * 1: with CircleCI's compiler (`circleci config process`, orb packed in) the fork workflow is dropped at `fork_pr` 0, kept at 7, and a PR's `lint` starts with `checkout`. https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/events/fork.yml#L1-L4 * 5: the new test passes, `--shard 1/2` plus `2/2` give all 556 checks, `ub_` still picks its 5. https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/tests/test_run_all_junit.py#L58-L63 * 11 and 13: main's `release.py` loaded from `.mb-main/` finds the trusted key and `cliff.toml`; the orb's dry-run signing, run on stand-in files, signs and verifies with the public key. https://git.magicbean.dev/MagicBeanDev/Orb/src/commit/4a5209a32b9f651197340514dd336a87cf5ffc0e/src/scripts/release-sign.sh#L10-L24 * 8 and Jordan's 3: the sticky label, the 14 quiet days and the missing 50-PR scan are in the orb's scripts, and its own bats tests pass here. https://git.magicbean.dev/MagicBeanDev/Orb/src/commit/4a5209a32b9f651197340514dd336a87cf5ffc0e/src/scripts/fork-stale.sh#L1-L45 The daily schedule that runs the sweep is a CircleCI setting I cannot see. ### Only partly there * 4: `mise.lock` has linux-arm64 for six tools, not python (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/mise.lock#L131-L144), and the orb's install no longer sets `MISE_LOCKED` (https://git.magicbean.dev/MagicBeanDev/Orb/src/commit/4a5209a32b9f651197340514dd336a87cf5ffc0e/src/scripts/setup-mise.sh#L20-L23), so the lock is not enforced. * 9: `full` is declared, but an API run with only `full: true` goes to `setup-noop-api` and does nothing (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/config.yml#L140-L148). The PR body says it forces everything. * 6 and 16 hold as graphs and dry-run switches, but see new problems 1 and 2. ### New problems from the rework 1. **No release tag is ever made on main.** `setup-main` and `setup-manual` pass `nightly: false` (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/config.yml#L55), and `release-version` only makes a nightly tag when it is true (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/shared.yml#L840-L854). I ran that step as compiled: `release.env` is `TAG=`. Every release job halts, no nightly ships after the cutover, and the dry run tests nothing. 2. **`release-stage` fails on every tag run.** It runs `cp -a tools.lock files/` in a job with no checkout, and no job saves `tools.lock` (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/shared.yml#L993-L1026). I ran the compiled step on a workspace holding the saved paths: `cp: cannot stat 'tools.lock'`, exit 1, after two 90-minute builds. 3. **`test-linux`, `fork-test-linux` and `spirv-fixtures` cannot set up.** They run on a bare `ubuntu:24.04` (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/shared.yml#L38-L45). `test-deps-linux` runs `git` first (through `fetch_commit.sh`), then pip and curl, but only installs g++, clang, spirv-tools, ccache and libdav1d (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/shared.yml#L116-L174). Canonical's package list for that image has no git, curl, python3 or ca-certificates, and that apt line pulls none in (I computed the dependency closure). The first head installed them. `spirv-fixtures` installs git but no ca-certificates before it fetches from github.com; that part is my inference. 4. **The policy check cannot fire.** It looks for the step name `get-source` (https://git.magicbean.dev/MagicBeanDev/Orb/src/commit/4a5209a32b9f651197340514dd336a87cf5ffc0e/policies/circleci.rego#L36-L49); the jobs use `mb/get-source`. conftest on the real merged configs passes 32 tests, and still passes with `release-signing` or `cloudflare-prod` attached to `lint` or `test-linux`. The PR body says the policies keep secrets off any job that runs PR code. 5. **The gate's name is probably wrong.** `mb/gate` has no `name:` (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/events/pr.yml#L40), and CircleCI names a status `ci/circleci: <job_name>` (https://circleci.com/docs/guides/integration/status-updates/). That gives `ci/circleci: mb/gate`, not the `ci/circleci: gate` that the docs, the orb and the fork reports use. The Forgejo integration is not documented, so this needs one live run. 6. **Manual API runs.** `release_dry_run` and `vulkan_dlss` are declared in both the setup and the continued config and passed back in (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/config.yml#L113-L117). CircleCI lists that as the "Conflicting pipeline parameters" error for API runs with a non-default value (https://circleci.com/docs/guides/orchestrate/dynamic-config/). Not run. Still open from the first review and not in the answer: `release-linux` installs g++-12 only (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/shared.yml#L891-L892); the fresh release zip and AppImage get none of the Wine, PE or player-build checks; tags and nightlies share one serial group, so CircleCI skips the older pipeline's publish (https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/28e4c0f709ebf2d94b55690992b4489b5e460a16/.circleci/events/release.yml#L40); preview links ignore page depth; website deploy has no check. ### Waiting for settings, the registry or the cutover What turns Forgejo events into pipelines (CircleCI's docs list no Forgejo), the status names, the context restrictions, the schedules, and whether the registry's version 0.1.0 of `magicbeandev/ci` is the tagged source. CircleCI says restricting contexts for custom webhook triggers is not supported (https://circleci.com/docs/guides/security/contexts/), and the expression quoted for `release-signing` lacks the `not job.ssh.enabled` and `not (pipeline.config_source starts-with "api")` parts of CircleCI's own example. The required gate, protection of Site's main and the `v*` tags, `RELEASING.md` on the signing key and `test_pad_layout.py` are promises; none is in the diff. ### Questions 1. Does the `forgejo-comment` context define `FORGEJO_TOKEN`? The orb's preview comment needs that name (https://git.magicbean.dev/MagicBeanDev/Orb/src/commit/4a5209a32b9f651197340514dd336a87cf5ffc0e/src/scripts/preview-comment.sh#L9); the first head's script read `FORGEJO_COMMENT_TOKEN`. 2. How is `forgejo-bot` restricted in the forks project, given the custom webhook limit above?
build(dev): lint what the branch changed, as CI does
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
d17e28aadc
drcrow force-pushed ci/circleci from d17e28aadc
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
to dbf8719733
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Third-party parts / As their notes say (pull_request) Successful in 29s
PR description / Description follows the template (pull_request_target) Successful in 14s
2026-11-14 18:23:28 +00:00
Compare
build(dev): drop the devcontainer
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 12s
CI / Workflow lint (actionlint) (pull_request) Successful in 20s
Third-party parts / As their notes say (pull_request) Successful in 29s
PR description / Description follows the template (pull_request_target) Successful in 15s
0ce7ebcd65
Author
Member

Removed the devcontainer here too, as you asked on Site#10 (mise is the dev setup). Config, policies and lint still pass.

Removed the devcontainer here too, as you asked on Site#10 (mise is the dev setup). Config, policies and lint still pass.
fix(ci): test git.tag null-safely and add a branch-push noop
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
1df6fa7b13
drcrow force-pushed ci/circleci from 1df6fa7b13
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
to 6314fa5c47
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 15s
CI / Workflow lint (actionlint) (pull_request) Successful in 22s
Third-party parts / As their notes say (pull_request) Successful in 32s
PR description / Description follows the template (pull_request_target) Successful in 17s
2026-11-14 18:34:57 +00:00
Compare
ci: use magicbeandev/ci 0.1.1
Some checks are pending
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
beanstalk/approval awaiting maintainer approval
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 7s
c5b77be088
@ -0,0 +6,4 @@
- third-party
- test-linux:
requires: [lint]
- builder-cli:
Owner

I think we can remove this as now both Windows and Linux builders are CLI-only?

I think we can remove this as now both Windows and Linux builders are CLI-only?
ci: use magicbeandev/ci 0.1.2
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
beanstalk/approval Test farm not requested
4a9707a1c0
Author
Member

Thanks @mooki and @HumanBean. All addressed, now at 4a9707a1, rebased on main, one commit per point:

  • Nightly on main: setup-main passes nightly: true (still release_dry_run: true until cutover), so the nightly chain gets its tag (efbd8e12).
  • tools.lock in release-stage: release-version now persists it (cabf41e7).
  • Bare Ubuntu images: git, curl, ca-certificates and python are installed before fetch_commit.sh in test-deps-linux and spirv-fixtures (43cf9afb).
  • Gate name: the PR and main gate job is named gate, so the status is ci/circleci: gate (332e3f5a). The PR gate doesn't wait for the preview comment (dbe52d0f).
  • API params: manual runs no longer pass release_dry_run/vulkan_dlss twice (eec860b2). An API run with full goes to a new setup-api-full route, the main chain as a dry run, instead of a no-op (02d7fa5f).
  • HumanBean, builder-cli on main: dropped; windows-builder only needs lint (d57c1563).
  • Policy and get-source: right again. The orb's policy didn't see mb/get-source called as a map step, so its "no secrets next to PR code" rule was checking nothing. Fixed in magicbeandev/[email protected], which this branch now uses. It then caught website-deploy (cloudflare-prod) using the fork-aware checkout; it's a plain checkout now, since it only ever runs on main (0b4c583d).

Also from Site's first live runs on CircleCI:

  • tag checks are null-safe: pipeline.git.tag is null, not "", on Forgejo;
  • every event maps to exactly one workflow (checked with the tag null and empty);
  • jobs that save caches keep them 5 days.

Not changed: the MISE_LOCKED and g++-12 notes are left as documentation. The RELEASING.md key note lands with the cutover PR.

Thanks @mooki and @HumanBean. All addressed, now at `4a9707a1`, rebased on main, one commit per point: - **Nightly on main:** `setup-main` passes `nightly: true` (still `release_dry_run: true` until cutover), so the nightly chain gets its tag (efbd8e12). - **`tools.lock` in `release-stage`:** `release-version` now persists it (cabf41e7). - **Bare Ubuntu images:** git, curl, ca-certificates and python are installed before `fetch_commit.sh` in `test-deps-linux` and `spirv-fixtures` (43cf9afb). - **Gate name:** the PR and main gate job is named `gate`, so the status is `ci/circleci: gate` (332e3f5a). The PR gate doesn't wait for the preview comment (dbe52d0f). - **API params:** manual runs no longer pass `release_dry_run`/`vulkan_dlss` twice (eec860b2). An API run with `full` goes to a new `setup-api-full` route, the main chain as a dry run, instead of a no-op (02d7fa5f). - **HumanBean, builder-cli on main:** dropped; `windows-builder` only needs `lint` (d57c1563). - **Policy and `get-source`:** right again. The orb's policy didn't see `mb/get-source` called as a map step, so its "no secrets next to PR code" rule was checking nothing. Fixed in `magicbeandev/[email protected]`, which this branch now uses. It then caught `website-deploy` (cloudflare-prod) using the fork-aware checkout; it's a plain `checkout` now, since it only ever runs on main (0b4c583d). Also from Site's first live runs on CircleCI: - tag checks are null-safe: `pipeline.git.tag` is null, not "", on Forgejo; - every event maps to exactly one workflow (checked with the tag null and empty); - jobs that save caches keep them 5 days. Not changed: the MISE_LOCKED and g++-12 notes are left as documentation. The RELEASING.md key note lands with the cutover PR.
drcrow force-pushed ci/circleci from 4a9707a1c0
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
beanstalk/approval Test farm not requested
to d7dbeb729a
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Guide screenshots / Guide images and local UI source review (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
2026-11-15 13:13:00 +00:00
Compare
The tag build installed a shorter package list (no toolchain PPA, nasm, meson or X11/Wayland/audio dev packages) than
build-linux.yml, which the Forgejo release builds the AppImage with, and skipped the player-build check a release needs to
pass. Share the package step between linux-appimage and release-linux.
build-windows.yml makes them with tools.lock's cpm and wgpu-native and passes --third-party-objects to the package step
(#629); the tag build shipped a zip without them.
No workflow runs release-notes: mb/release-publish writes the notes with main's scripts/release.py itself. The py312 and
x86-small executors were used by nothing else.
Mirror the Forgejo workflow added upstream: scripts/check_guide_screenshots.py --review-sources and its two regression
tests, on every pull request (forks too) and main push.
fix(ci): shellcheck only scripts/, which exists; ci/ does not
Some checks failed
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 6s
Third-party parts / As their notes say (pull_request) Successful in 11s
734a9ad24e
windows-builder (PE and Wine checks) and windows-vulkan now run in the v* workflow, and release-version waits for them.
ci(circleci): map the tools and third-party objects scripts to the builders
Some checks failed
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 7s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 7s
Third-party parts / As their notes say (pull_request) Successful in 12s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
cb71a46fd9
Drop the unused thirdparty and docs_only areas and the rules for files that do not exist.
mooki approved these changes 2026-11-15 13:38:36 +00:00
HumanBean deleted branch ci/circleci 2026-11-15 17:25:17 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!417
No description provided.