WIP: ci: run checks, signed releases and fork PR checks on CircleCI #65
No reviewers
Labels
No labels
area/build
area/install
area/saves
area/ui
area/updates
good first issue
gpu/amd
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
platform/linux
platform/steam-deck
platform/windows
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/LongshotLauncher!65
Loading…
Reference in a new issue
No description provided.
Delete branch "drcrow/LongshotLauncher:ci/circleci"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Pull requests get only a title check today; the build and tests run on main alone, so a broken PR is found after it merges. This moves CI and the release pipeline to CircleCI, which builds and tests every PR and only does the work a change needs.
What changed
.circleci/config.ymlhas one small setup job per event (pull request, push to main,v*tag, nightly schedule, manual API run). Each one looks at which areas changed (.circleci/mapping.conf: app, core, tests, packaging, release scripts, docs) and continues with.circleci/shared.ymlplus that event's file in.circleci/events/. A docs-only PR runs lint andgate; a Core change builds and tests; packaging changes also package and run the Wine self-check.magicbeandev/ci, used by all three repos (source in a newMagicBeanDev/orbrepo). This repo keeps only its own jobs.release.yml: version check from main's copy ofscripts/release.py,package.sh, SHA256SUMS, a sign job that holds only the signing key, then a publish job that holds only the Forgejo token (notes, publish, prune old nightlies). Main pushes publish the nightly only after everything passes;v*tags do rc or stable.release_dry_runruns it all with a throwaway key and stops before publishing.ci/circleci: gate, the one check branch protection needs. Build and test jobs run on CircleCI's Arm (Gen2) containers, the cheapest option; packaging stays on x86 for Wine and the AppImage tools. Caches are saved from main only.ci: approvedlabel (Forgejo only lets people with write access do that). A separate CircleCI project then runs the checks on that exact commit with no secrets, and posts the gate status. The label stays on, so new pushes keep running. If a fork PR goes 14 days without activity, the label is removed automatically.tests/longshot.refpins the Longshot commit the contract checks run against; a nightly also checks Longshot main. The release tests run under pytest with JUnit output, from hash-pinned requirements.global.jsonpins the .NET SDK;mise.tomlpins the tools and haslint,testandvalidatetasks. Also adds pre-commit hooks (Conventional Commit messages included), a devcontainer, a Chunk sidecar config, anddocs/ci.md.Testing
Tested:
circleci config validateon every event's merged config with the orb inlined; the orb's conftest policies (24 pass);mise run lintandmise run test(build plus 28 release tests) in a clean Ubuntu 24.04 container and on a fresh Chunk sidecar.Not tested: a live CircleCI run (it starts once this is on main and the orb is published), publishing (needs the signing key and bot token in CircleCI), and the fork flow end to end (needs the org webhook).
Feedback very welcome, especially on the path areas in
.circleci/mapping.confand on whether the release chain matches how you cut releases today.d3ab541e52c2c0d02de7c2c0d02de7a5837a0b18a5837a0b18e615da0fbaLooked over what runs when, at
e615da0f(read ata5837a0b; the push since then adds diagrams todocs/ci.mdand changes nothing else). Three things would stop the flow as written, then points that depend on the orb or on settings I cannot see.1. The fork workflow's condition is always true.
.circleci/events/fork.yml:4-5haswhen: greater: [<< pipeline.parameters.fork_pr >>, 0].greateris not one of CircleCI's logic statements (and, or, not, equal, matches), and the compiler does not reject it: an unknown operator counts as true. I compiled a small config with exactly that condition through CircleCI's config compiler: it is valid, and the workflow is kept withfork_prat 0 as well as at 7. With the expressionwhen: pipeline.parameters.fork_pr > 0it is dropped at 0 and kept at 7. Socircleci config validatepasses, and the fork jobs run whenever this file is continued into, whateverfork_pris. The same line is in Longshot and Site, and in Longshot the same operator guards the checkout (noted there). The orb'sget-sourceis worth checking for it too. One caution for step-level conditions: insidewhen:/unless:condition:the expression string was also taken as true in my test;not: {equal: [0, << pipeline.parameters.fork_pr >>]}compiled correctly there.2. A
v*tag never reaches the release..circleci/config.yml:58-68: thereleaseworkflow has a tag condition, but itsmb/setupjob has no tag filter. CircleCI does not run a job for a tag unless that job has a tag filter (https://circleci.com/docs/guides/orchestrate/workflows/#executing-workflows-for-a-git-tag), so by that rule the setup job that would continue intoevents/release.ymldoes not start, and the filters insideevents/release.ymlcome too late. I have not seen a live tag run.noop-non-release-tag(lines 90-100) has the same gap. Longshot'sconfig.ymlhasfilters: pipeline.git.tag starts-with "v"on its tag job; this one needs the same.3. The version job runs main's
release.pyfrom/tmp, where it cannot find the repository..circleci/shared.yml:338-348writesorigin/main:scripts/release.pyto/tmp/release-main.pyand runs it there.scripts/release.py:64takes the repository root from the script's own location, so the root becomes/. Run that way from a checkout of this branch:nightly-namefails withgit rev-list --count ...: fatal: not a git repository;check-tag v1.2.3-rc.1is refused as "not vX.Y.Z or vX.Y.Z-rc.N of this product's numbers (from v3.0.0)", because with noCore/ReleaseTrust.csunder that root the script takes the launcher for Longshot.In place,
nightly-nameprintsnightly-54-a5837a0b. So every nightly on main and every launcher tag stops atversion. The copy has to sit one folder below the checkout's root (asscripts/does), or the script needs a root argument. Longshot's pipeline writes its copy to.ci/main-release/, which keeps git working but makes the root<checkout>/.ci.Depends on the orb or on settings:
4. Two publishers.
.forgejo/workflows/release.ymlstill publishes and prunes on every push to main and onv*tags (lines 25-28 and 188-191), andevents/main.yml:19-29publishes for real as well. Once 2 and 3 are fixed, both write the same nightly and both prune. Which one is the only writer while they run side by side?5. Paths with no rule.
mapping.confhas no rule forscripts/translations.py(imported bytests/test_launcher_core.py:118-123),mise.lock,.actionlint.ymlor.forgejo/. Unless the orb treats an unmapped path as "full", a change to only those gets lint and the gate. What does the orb do with an unmapped path, and what does it compare against (merge base, previous push)?6. "Main runs everything, the nightly ships only if it all passes." On main,
versionrequires lint, build-test and release-scripts.contract-longshot-mainis required neither byversionnor bygate(events/main.yml:10-11and30-37), and its check ends in|| true(shared.yml:295). If that is meant as advice only, the wording could say so.7. A failed check and the gate. The gates use plain
requires, so when a required job fails the gate job does not run. Does something else then post a failedci/circleci: gateon the commit, or does the check stay missing? Either blocks the merge; only one tells the author why.8. "Never has secrets." The new text in
docs/ci.mdsays the forks project never has secrets..circleci/forks.yml:20-23and:29-32give the doorbell and the stale sweep theforgejo-botcontext, andevents/fork.ymlgives it to the two report jobs. That may be the intended split (the jobs that run fork code ask for no context), in which case the sentence could say so.9. The manual dry run asks for the real contexts.
events/manual.yml:14-23givessignandpublishtherelease-signingandforgejo-botcontexts although the dry run is meant to use a throwaway key and not publish.Checked and fine: the orb is pinned to an exact version in all three files; a docs-only change selects lint and gate; docs plus Core keeps the Core checks; packaging stays on x86 with the Wine check; signing and publishing ask for separate contexts and the build jobs for none;
tests/longshot.refis a full commit that exists in Longshot; the pytest requirements are hash-pinned.One request: https://git.magicbean.dev/MagicBeanDev/orb is not readable (404), and the setup, fork handling, signing, publishing and gate all live in the orb. Those parts of the flow cannot be reviewed from this pull request until its source can be read.
Thanks @mooki, great review. Fixed in
e615da0f..c2eb0f4:greater:is always true: confirmed withcircleci config process. The fork workflow now useswhen: pipeline.parameters.fork_pr > 0, which is dropped at 0 and kept at 7 (7b916d2).v*never reaching the release: the tag setup job andsetup-noopnow have tag filters, and every job inevents/release.ymlalready had one (e6699df).release.pyfrom/tmp: main's copy now goes to.mb-main/release.pyin the checkout, so its root is the checkout.nightly-nameprintsnightly-56-e6699df1from there (2652b3a). The orb's sign and publish jobs had the same bug and are fixed the same way.Orb and settings:
release.ymlstays the only publisher (e78c02d,c1f4d3a). The cutover PR turns CircleCI live and removesrelease.ymlin the same commit.gaterun. The diff is against the merge base withorigin/mainon PRs, andHEAD~1on main. I added rules forscripts/translations.py(core and tests),mise.lock(full), and.actionlint.ymland.forgejo/(lint) (8c63d70).contract-longshot-main: it's meant as advice only, anddocs/ci.mdnow says so (c2eb0f4).ci/circleci: <job>status, so the cause shows red on the commit.gatedoesn't appear, and once it's required that blocks the merge. I'd rather not put an API token on PR pipelines just to turngatered.4a31ce1).Orb source: MagicBeanDev/orb is being created now, and the first PR there will have the full source.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.