Skip to content

Commit 0af1c87

Browse files
committed
ci: Publish release branch previews
Create Version Packages updates on the release branch and dispatch CI because GitHub token branch updates cannot start another workflow. Green runs publish the package preview and update the GHCR next tag while main continues to update its own mutable image.
1 parent c512ecd commit 0af1c87

6 files changed

Lines changed: 144 additions & 48 deletions

File tree

.github/changeset-version.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
2-
// Called by the changesets action in release.yml as the `version`
3-
// command. It consumes pending changesets, then applies release-time
4-
// references that must be committed with the Version Packages PR.
2+
// Called by the version path in release.yml. It consumes pending changesets,
3+
// then applies release-time references that must be committed with the Version
4+
// Packages PR.
55
//
66
// Changesets owns package versions and changelogs, including private
77
// packages (`privatePackages.version` is enabled in .changeset/config.json).

.github/workflows/ci.yml

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
# build siblings whose dist/ this package's typecheck depends on,
55
# then run biome + typecheck + tests scoped to that package.
66
#
7-
# Runs on every PR and on every push to main or next, so a merge
8-
# that slipped through (admin override, branch protection off,
9-
# etc.) still gets the same lint/build/typecheck/test signal.
7+
# Runs on every PR and on pushes to the long-lived branches. The release
8+
# workflow also dispatches CI for the release branch because its GITHUB_TOKEN
9+
# push cannot start another workflow.
1010
#
11-
# Path filter skips pure documentation changes — they have no
12-
# influence on any package's lint, typecheck, or test surface.
11+
# Path filters skip pure documentation changes — they have no influence on
12+
# any package's lint, typecheck, or test surface.
1313

1414
name: CI
1515

@@ -20,21 +20,22 @@ on:
2020
- "docs/**"
2121
- ".github/**/*.md"
2222
push:
23-
branches: [main, next]
23+
branches: [main, next, release]
2424
paths-ignore:
2525
- "**/*.md"
2626
- "docs/**"
2727
- ".github/**/*.md"
28+
workflow_dispatch: {}
2829

2930
permissions:
3031
contents: read
3132

3233
concurrency:
33-
# PRs key on the PR number so successive pushes cancel the prior
34-
# run; pushes to main key on the ref so we never cancel a main
35-
# build (those are the historical signal).
34+
# PRs key on the PR number so successive pushes cancel the prior run. The
35+
# version branch also keeps only its newest run, while main builds remain a
36+
# historical signal and are never canceled.
3637
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
37-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
38+
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/release' }}
3839

3940
jobs:
4041
package:
@@ -190,7 +191,11 @@ jobs:
190191

191192
preview:
192193
name: preview package
193-
if: github.event_name == 'pull_request'
194+
if: >-
195+
${{
196+
(github.event_name == 'pull_request' && github.head_ref != 'release') ||
197+
github.ref == 'refs/heads/release'
198+
}}
194199
needs: [package, example]
195200
runs-on: ubuntu-24.04
196201
timeout-minutes: 15

.github/workflows/main-computerd-image.yml

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
# Build the computerd image from the commit that passed CI on main. The
2-
# mutable :main tag gives tests and examples that follow main a fresh binary
3-
# without waiting for the changesets release flow.
1+
# Build computerd images from commits that passed CI. The mutable :main tag
2+
# follows main, while :next follows the changesets Version Packages branch.
43

5-
name: Main computerd image
4+
name: Computerd branch image
65

76
on:
87
workflow_run:
98
workflows: ["CI"]
10-
branches: [main]
9+
branches: [main, release]
1110
types: [completed]
1211
workflow_dispatch: {}
1312

1413
concurrency:
15-
group: ${{ github.workflow }}
16-
# If main advances while an image is building, only the newest commit
17-
# should be allowed to publish the mutable :main tag.
14+
# Keep the main and next publishers independent while allowing newer commits
15+
# on either branch to cancel stale image builds for the same tag.
16+
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.ref }}
1817
cancel-in-progress: true
1918

2019
permissions:
@@ -30,11 +29,23 @@ jobs:
3029
(
3130
github.event_name == 'workflow_run' &&
3231
github.event.workflow_run.conclusion == 'success' &&
33-
github.event.workflow_run.event == 'push' &&
34-
github.event.workflow_run.head_branch == 'main' &&
32+
(
33+
github.event.workflow_run.event == 'push' ||
34+
github.event.workflow_run.event == 'workflow_dispatch'
35+
) &&
36+
(
37+
github.event.workflow_run.head_branch == 'main' ||
38+
github.event.workflow_run.head_branch == 'release'
39+
) &&
3540
github.event.workflow_run.head_repository.full_name == github.repository
3641
) ||
37-
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
42+
(
43+
github.event_name == 'workflow_dispatch' &&
44+
(
45+
github.ref == 'refs/heads/main' ||
46+
github.ref == 'refs/heads/release'
47+
)
48+
)
3849
)
3950
}}
4051
runs-on: ubuntu-24.04
@@ -47,6 +58,26 @@ jobs:
4758
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref }}
4859
fetch-depth: 1
4960

61+
- name: Select the mutable image tag
62+
id: image
63+
env:
64+
SOURCE_BRANCH: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.ref_name }}
65+
run: |
66+
case "$SOURCE_BRANCH" in
67+
main)
68+
tag=main
69+
;;
70+
release)
71+
tag=next
72+
;;
73+
*)
74+
echo "Unsupported source branch: $SOURCE_BRANCH" >&2
75+
exit 1
76+
;;
77+
esac
78+
echo "branch=$SOURCE_BRANCH" >> "$GITHUB_OUTPUT"
79+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
80+
5081
- uses: ./.github/actions/install
5182

5283
- name: Install system dependencies
@@ -86,26 +117,30 @@ jobs:
86117
username: ${{ github.actor }}
87118
password: ${{ secrets.GITHUB_TOKEN }}
88119

89-
# A slower CI run for an older main commit can finish after a newer
90-
# one. Do not let that run replace the newer image.
91-
- name: Verify main has not advanced
92-
id: main-sha
120+
# A slower CI run for an older commit can finish after a newer one. Do
121+
# not let that run replace the newer branch image.
122+
- name: Verify the source branch has not advanced
123+
id: branch-sha
124+
env:
125+
SOURCE_BRANCH: ${{ steps.image.outputs.branch }}
93126
run: |
94-
git fetch --no-tags --depth=1 origin main
95-
if [[ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]]; then
96-
echo "main advanced while this image was building; skipping the push"
127+
git fetch --no-tags --depth=1 origin "$SOURCE_BRANCH"
128+
if [[ "$(git rev-parse HEAD)" != "$(git rev-parse FETCH_HEAD)" ]]; then
129+
echo "$SOURCE_BRANCH advanced while this image was building; skipping the push"
97130
echo "current=false" >> "$GITHUB_OUTPUT"
98131
else
99132
echo "current=true" >> "$GITHUB_OUTPUT"
100133
fi
101134
102-
- name: Build and push the main image
103-
if: steps.main-sha.outputs.current == 'true'
135+
- name: Build and push the branch image
136+
if: steps.branch-sha.outputs.current == 'true'
137+
env:
138+
IMAGE_TAG: ${{ steps.image.outputs.tag }}
104139
run: |
105140
docker buildx build \
106141
--platform linux/amd64 \
107142
--push \
108143
--provenance=false \
109-
--tag ghcr.io/cloudflare/computer-computerd-linux-x64:main \
144+
--tag "ghcr.io/cloudflare/computer-computerd-linux-x64:$IMAGE_TAG" \
110145
--file packages/computer-computerd-linux-x64/Dockerfile \
111146
packages/computer-computerd-linux-x64

.github/workflows/release.yml

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# Changesets-driven release. A green CI run on main either opens/updates the
2-
# changesets Version Packages PR, or publishes the release after that PR merges.
2+
# Version Packages PR, or publishes the release after that PR merges.
33
#
44
# Version PR path:
55
# - contributors commit `.changeset/*.md` files;
6-
# - changesets/action runs `.github/changeset-version.mjs`;
7-
# - the Version Packages PR commits package versions, changelogs, lockfile
8-
# updates, and the Dockerfile/docs pins for the computerd image.
6+
# - `.github/changeset-version.mjs` applies the pending versions;
7+
# - create-pull-request commits those changes to the `release` branch;
8+
# - the Version Packages PR includes package versions, changelogs, lockfile
9+
# updates, and the Dockerfile/docs pins for the computerd image;
10+
# - this workflow dispatches CI on `release`, which publishes the package
11+
# preview and lets the branch image workflow publish `:next`.
912
#
1013
# Publish path:
1114
# - no pending changesets remain on main;
@@ -34,6 +37,8 @@ jobs:
3437
runs-on: ubuntu-24.04
3538
timeout-minutes: 30
3639
permissions:
40+
# The version path dispatches CI for the release branch.
41+
actions: write
3742
# Trusted publishing on npm requires the OIDC id-token.
3843
id-token: write
3944
# changesets/action creates the version PR, release commit, and tags.
@@ -84,6 +89,43 @@ jobs:
8489
echo "present=false" >> "$GITHUB_OUTPUT"
8590
fi
8691
92+
- name: Version packages
93+
if: steps.pending-changesets.outputs.present == 'true'
94+
run: node .github/changeset-version.mjs
95+
96+
# changesets/action fixes its branch name to changeset-release/<base>.
97+
# Use create-pull-request so the stable Version Packages branch is the
98+
# shorter `release` name.
99+
- name: Create or update the Version Packages PR
100+
id: version-pr
101+
if: steps.pending-changesets.outputs.present == 'true'
102+
uses: peter-evans/create-pull-request@v8
103+
with:
104+
branch: release
105+
base: main
106+
commit-message: Version Packages
107+
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
108+
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
109+
title: Version Packages
110+
body: |
111+
This pull request applies the pending changesets to package versions,
112+
changelogs, the lockfile, and computerd image pins. Merge it to publish
113+
the release.
114+
115+
- name: Close the old Version Packages PR
116+
if: steps.version-pr.outputs.pull-request-number
117+
env:
118+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119+
VERSION_PR: ${{ steps.version-pr.outputs.pull-request-number }}
120+
run: |
121+
old_pr="$(gh pr list --repo "$GITHUB_REPOSITORY" --state open \
122+
--base main --head changeset-release/main --json number \
123+
--jq '.[0].number // empty')"
124+
if [[ -n "$old_pr" ]]; then
125+
gh pr close "$old_pr" --repo "$GITHUB_REPOSITORY" --delete-branch \
126+
--comment "Superseded by #$VERSION_PR after the release branch was renamed."
127+
fi
128+
87129
# The following Docker setup is only needed on the publish path. The
88130
# Version Packages PR path should not need registry credentials.
89131
- name: Set up buildx
@@ -110,10 +152,18 @@ jobs:
110152
-u "$CF_REGISTRY_USERNAME" --password-stdin
111153
112154
- id: changesets
155+
if: steps.pending-changesets.outputs.present == 'false'
113156
uses: changesets/action@v1
114157
with:
115-
version: node .github/changeset-version.mjs
116158
publish: node .github/changeset-publish.mjs
117159
env:
118160
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119161
NPM_CONFIG_PROVENANCE: "true"
162+
163+
# A branch update made with GITHUB_TOKEN cannot trigger a push workflow.
164+
# workflow_dispatch is exempt from that recursion guard.
165+
- name: Run CI on the version branch
166+
if: steps.pending-changesets.outputs.present == 'true'
167+
env:
168+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
169+
run: gh workflow run ci.yml --ref release

COLLABORATORS.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,15 @@ Once your pull request merges to `main`, the release workflow takes
144144
over in two steps:
145145

146146
1. It gathers the pending changesets into a "Version Packages" pull
147-
request that bumps package versions, rewrites changelogs, and updates
148-
Dockerfile and documentation pins for the `computerd` image. Private
149-
packages such as `@cloudflare/dofs`, `@cloudflare/computer-rpc`, and
147+
request from the `release` branch. That pull request bumps package
148+
versions, rewrites changelogs, and updates Dockerfile and documentation
149+
pins for the `computerd` image. Private packages such as
150+
`@cloudflare/dofs`, `@cloudflare/computer-rpc`, and
150151
`@cloudflare/computerd` are versioned and get changelogs, but are not
151-
published to npm.
152+
published to npm. Each update publishes a package preview through
153+
`pkg.pr.new` and the mutable
154+
`ghcr.io/cloudflare/computer-computerd-linux-x64:next` image after CI
155+
passes.
152156
2. Merging that pull request first builds and pushes the `computerd`
153157
binary image to `ghcr.io` and `registry.cloudflare.com`, then publishes
154158
public npm packages. Rerunning a failed publish is safe: existing image

packages/computer-computerd-linux-x64/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ ENTRYPOINT ["/usr/local/bin/computerd"]
3535
```
3636

3737
Pin the image version explicitly. `latest` is fine for experimentation but
38-
can bite when wire-protocol changes land. The `Main computerd image` workflow
39-
also publishes `ghcr.io/cloudflare/computer-computerd-linux-x64:main` after
40-
successful CI on `main`; use that mutable tag only for tests and examples
41-
that intentionally track the branch.
38+
can bite when wire-protocol changes land. The `Computerd branch image`
39+
workflow publishes `ghcr.io/cloudflare/computer-computerd-linux-x64:main`
40+
after successful CI on `main` and
41+
`ghcr.io/cloudflare/computer-computerd-linux-x64:next` after successful CI on
42+
the `release` branch. Use these mutable tags only for tests and examples that
43+
intentionally track their branches.
4244

4345
## Configuration
4446

0 commit comments

Comments
 (0)