Repository navigation
fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners - #2
Conversation
fork-release.yml builds a fork release from a commit: upstream's tests (fork-tests.yml, the release.yml test jobs on hosted runners), the JS bundle with this repository as the release repository, the Linux x64 server archive and the desktop apps through upstream's release-desktop.yml, unsigned unless signing secrets exist. Publishing is opt-in, makes a normal release marked latest, and stays a draft until every file is uploaded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
lens: ops+security DI-1 · reviewer: opus high (t3lens-opsDI1) · read at: 48526ae
|
|
lens: ops+security DI-2 · reviewer: opus high (opsdi2) · read at: 48526ae
|
…blishes - A version that already has a tag or a published release is refused before the draft is created and again right before it is published, so "Re-run failed jobs" cannot replace a live release's files. - Leftover drafts are removed under pipefail and each removal is logged; a failed release listing fails the step instead of reading as "no drafts". - Only released upstream code: refuses any commit between the base tag and the build that is on upstream's default branch, a base tag that is not the newest stable tag in the history (a stray tag on an old commit), and a base above upstream's latest release. - Marked latest only when above every published fork release. - The CLI archives (the `t3` executable bundles the server again) must name this repository, not only apps/server/dist. - Actions pinned by commit; fail_on_unmatched_files; named signing secrets instead of secrets: inherit; persist-credentials: false. Files are collected in a read-only assemble job; the publish job holds the write token, runs no package code, and only its three release steps see the token. - The workflow's `published` output used jobs.publish.result, which workflow_call outputs cannot read; it now comes from the publish step. Tests: .github/scripts/fork-release.test.sh (local git repos, fake gh; also run in preflight) and fork-release-workflow.test.py (workflow shape). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
fix round 1: every major and minor from DI-1 and DI-2, at head 0da2ccd The guards now live in Majors
Minors
Also found: actionlint reported that the Run: both test files, actionlint 1.7.12 with shellcheck 0.10.0, shellcheck on the scripts, Unproven: no workflow run (dispatch needs the default branch). Whether the real |
|
lens: ops+security r2 · reviewer: opus high (t3lens-2-r2) · read at: 0da2ccd
|
…blished release - check-archives reads only <stem>/t3 (t3.exe in the zip), not client/, and fails when the program is missing. The web client names this repository on every Actions build, so it passed while the server still named upstream. - check-bundle (new) checks apps/server/dist without dist/client, for the same reason. - resolve and publish refuse a version that is not above every published release on the server's stable channel: the server updater takes the newest-created release, so an older build published later would read as a downgrade. Published releases are always latest. - Preflight runs fork-release-workflow.test.py too. - The build sets T3CODE_DESKTOP_UPDATE_REPOSITORY (the name release-repository.ts reads); the former T3CODE_RELEASE_REPOSITORY is gone. The publish job takes its guard script from github.workflow_sha, not from the commit being built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
fix round 2: head a1bc1cd, on top of 0da2ccd. Answers the r2 lens (comment 6822149545).
Results: |
|
lens: ops+security r3 · reviewer: opus high (t3lens-1-2-r) · read at: a1bc1cd
|
main now carries #1, #2 and a newer upstream. fork-tests.yml keeps this branch's version (it replaces #2's copy) and ports what the drift check listed: test-delta.test.cjs in the release checks, Vitest's module cache on the server shards (saved only by pushes), and no test-cache report upload. Web was already split in two; the built-client boot stays in the Build job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds this fork's releases on GitHub-hosted runners. Upstream's
release.ymlonly runs in the upstream repository and uses runners a fork does not have.What it adds
fork-release.yml(run by hand, or called by another workflow) builds a commit asv<upstream>-fork.<n>:<upstream>is the newest stable upstream tag the commit contains, read from the fork's parent repository.<n>counts the fork's earlier builds of that tag..github/scripts/fork-release.sh, one subcommand each. Preflight runs its tests (fork-release.test.shandfork-release-workflow.test.py) before it resolves the version. Each refusal stops the run with an::error::line.mainwhile it still equals upstream'smainfails. This is the same rule as fork: follow each stable upstream release with the patch series #4's guard.v1.0.0on an old commit). It also refuses a base above the release upstream marks latest.cliRelease.tsnewestCliReleaseVersion) takes the newest-created release on its channel, not the highest version, so an older build published later would show every server a downgrade. Resolve refuses such a build, and publish checks again. To go back, install the older release witht3 update --version X --allow-downgrade.fork-tests.ymlruns upstream's release checks and tests (release.ymlquality, test, test_web and the 8 test_server shards) onubuntu-latest. Only the runners and time limits differ.T3CODE_DESKTOP_UPDATE_REPOSITORY(the setting fork: the server and CLI update from the desktop app's repository #1 reads) set to this repository. A step fails the build if the server bundle outsidedist/clientdoes not name it. The web client names this repository on every Actions build anyway (upstream'sapps/web/vite.config.ts), so it does not count.release-desktop.yml: Linux x64 (AppImage plus the server archive), Windows x64 (embeds the Linux archive for WSL) and macOS arm64 and x64. Each call gets only the 13 signing secretsrelease-desktop.ymldeclares (nosecrets: inherit).fork-release.ymldeclares the same secrets for callers.FORK_MACOS_RUNNERvariable (defaultmacos-26).SHA256SUMSand merges the updater manifests.latest.yml,latest-mac.ymlandlatest-linux.ymlexist.t3program (t3.exein the zip) at the top of every CLI archive names this repository;client/does not count. A missing program fails. The program bundles the server again, so this is the code that updates.publish: trueis passed. The publish job is the only one withcontents: write. It checks out only the guard script, from the workflow's own commit (github.workflow_sha), and runs no package code. Only its three release steps get the token.fail_on_unmatched_files: true) and only then publishes that draft by id as a normal release.persist-credentials: false.Depends on #1. Without it, builds still name upstream as their release repository, and the bundle and archive checks fail.
Tests on pull requests: not run yet. No PR workflow runs these two test files; only preflight does.
fork-tests.ymlbelongs to #6 andfork-checks.ymlto #3/#4, so this PR does not touch them. #6 (or whichever lands the fork's PR checks) must runbash .github/scripts/fork-release.test.shandpython3 .github/scripts/fork-release-workflow.test.py.Tested (locally; no workflow run yet)
bash .github/scripts/fork-release.test.sh: 34 cases against local git repositories and a fakegh(resolve, re-run refusal, drafts, older-than-published refusal, archives, server bundle, version order). All pass on this head. Against the 0da2ccd script, 12 fail, including the review's repro (t3names upstream,client/names the fork).python3 .github/scripts/fork-release-workflow.test.py: 22 checks on the workflow's shape (pins, secrets, checkouts, write job, step order, pipefail, bundle check, setting name, guard script commit). All pass; 5 fail on 0da2ccd.vp fmt --checkon the workflow: clean. actionlint also found that the oldpublishedoutput readjobs.publish.result, whichworkflow_calloutputs cannot read; it now comes from the publish step.Not tested
workflow_dispatchworkflow only runs once it is on the default branch.t3program stores the repository name as plain quoted text is shown only by a real run. If it does not, the check fails the run; it does not pass quietly.github.workflow_shaunderworkflow_callis the caller's commit. A caller in this repository has the guard script there; a caller elsewhere is not supported.release-desktop.yml's fixed 30-minute limit on 4-vCPU hosted runners. Upstream uses larger runners.macos-26label is available.app-update.ymlinside the AppImage, dmg and exe) is not checked for the repository name; only the JS bundle and the CLI archives are.🤖 Generated with Claude Code