-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate pipeline from CircleCI to GitHub Actions #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ea9b1aa
87e6fba
fd22322
7fbdd6e
03e8be8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| name: Main | ||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Every run publishes an rc image and tags the repo; serialise so | ||
| # close-together merges cannot race on version tags. queue: max keeps every | ||
| # queued run (the default keeps only the newest pending), so a slow release | ||
| # approval delays later prereleases instead of cancelling them. | ||
| # 'queue: max' — GA 2026-05-07: https://github.blog/changelog/2026-05-07-github-actions-concurrency-groups-now-allow-larger-queues/ | ||
| concurrency: | ||
| group: main | ||
| cancel-in-progress: false | ||
| queue: max | ||
|
Comment on lines
+15
to
+18
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 Plan concern (not a defect — this is the plan's exact YAML) | Flagged independently by: correctness, safety The 1. Queued prereleases can fail on a rejected push. With 2. A pending approval freezes all main-branch CI. While Suggested plan amendment: scope serialisation to the jobs that actually race on version tags rather than the whole workflow — drop the workflow-level |
||
|
|
||
| jobs: | ||
| skip-ci-check: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| should_skip_ci: ${{ steps.skip_ci_check.outputs.should_skip_ci }} | ||
| steps: | ||
| - id: skip_ci_check | ||
| env: | ||
| HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} | ||
| run: | | ||
| if [[ "$HEAD_COMMIT_MESSAGE" == *"[no ci]"* ]] \ | ||
| || [[ "$HEAD_COMMIT_MESSAGE" == *"[skip ci]"* ]] \ | ||
| || [[ "$HEAD_COMMIT_MESSAGE" == *"[ci skip]"* ]]; then | ||
| echo "should_skip_ci=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "should_skip_ci=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| check: | ||
| needs: [skip-ci-check] | ||
| if: needs.skip-ci-check.outputs.should_skip_ci != 'true' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install tools | ||
| uses: infrablocks/github-actions/asdf_install@v1 | ||
| - name: Check | ||
| run: ./go test:code:check | ||
| - name: Notify Slack | ||
| if: ${{ !cancelled() }} | ||
| continue-on-error: true | ||
| run: ./go "slack:notify[${{ job.status }}]" | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
|
|
||
| test: | ||
| needs: [skip-ci-check] | ||
| if: needs.skip-ci-check.outputs.should_skip_ci != 'true' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Install tools | ||
| uses: infrablocks/github-actions/asdf_install@v1 | ||
| - name: Ensure docker-compose is available | ||
| run: | | ||
| if ! command -v docker-compose >/dev/null 2>&1; then | ||
| sudo curl -fsSL \ | ||
| "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64" \ | ||
| -o /usr/local/bin/docker-compose | ||
| sudo chmod +x /usr/local/bin/docker-compose | ||
| fi | ||
|
Comment on lines
+65
to
+72
Comment on lines
+65
to
+72
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 Plan concern (not a defect — plan §4.1/§4.2 specify this exact step) | Flagged independently by: security, safety, standards, code quality Four lenses converged. The guard is documented as deliberate; the concerns are about properties of the guard that the plan doesn't address: Supply chain — Reproducibility — every other tool in this pipeline is pinned via Duplication — the block is repeated verbatim in Suggested plan amendments (in preference order):
env:
COMPOSE_VERSION: v2.32.4
COMPOSE_SHA256: <sha>
run: |
if ! command -v docker-compose >/dev/null 2>&1; then
curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-linux-x86_64" -o /tmp/docker-compose
echo "${COMPOSE_SHA256} /tmp/docker-compose" | sha256sum -c -
sudo install -m 0755 /tmp/docker-compose /usr/local/bin/docker-compose
fi |
||
| - name: Test | ||
| run: ./go test:integration | ||
| - name: Notify Slack | ||
| if: ${{ !cancelled() }} | ||
| continue-on-error: true | ||
| run: ./go "slack:notify[${{ job.status }}]" | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
|
|
||
| prerelease: | ||
| needs: [check, test] | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Install tools | ||
| uses: infrablocks/github-actions/asdf_install@v1 | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 PLAN CONCERN — §1/D6 explicitly accept tracking the moving
A push to the Suggested follow-up: pin to a commit SHA ( |
||
| - name: Install secrets tools | ||
| run: sudo apt-get update && sudo apt-get install -y git-crypt gnupg | ||
| - name: Unlock git-crypt | ||
| run: ./go git_crypt:unlock_with_encrypted_gpg_key | ||
| env: | ||
| ENCRYPTION_PASSPHRASE: ${{ secrets.ENCRYPTION_PASSPHRASE }} | ||
| - name: Set CI git author | ||
| run: ./go repository:set_ci_author | ||
| - name: Prerelease | ||
| run: ./go "version:bump[rc]" && ./go image:publish | ||
| - name: Notify Slack of release hold | ||
| continue-on-error: true | ||
| run: ./go "slack:notify[success,on_hold]" | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
| - name: Notify Slack | ||
| if: ${{ !cancelled() }} | ||
| continue-on-error: true | ||
| run: ./go "slack:notify[${{ job.status }}]" | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
|
|
||
| release: | ||
| needs: [prerelease] | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| environment: release | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 Plan concern (not a defect — plan §7 covers provisioning) | Lens: safety The approval gate depends entirely on the Impact: if The plan's §7 verification ( - name: Assert release was approved
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
rules=$(gh api "repos/${{ github.repository }}/environments/release" \
--jq '.protection_rules | length')
[ "$rules" -gt 0 ] || { echo "release environment has no protection rules"; exit 1; }For this PR specifically: please confirm §7 provisioning was run and that |
||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: main | ||
| fetch-depth: 0 | ||
| - name: Pull latest main | ||
| # Approval can land long after the run starts; release publishes main | ||
| # as of approval time, not the tested SHA — parity with the old | ||
| # release.sh, which also pulled. prerelease.sh never pulled, so the | ||
| # prerelease job deliberately has no pull. | ||
| run: git pull | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 PLAN CONCERN (flagged by both security and safety) — documented as deliberate parity with the old
The security framing worth adding to the plan's note: this is a time-of-check/time-of-use gap in the only human authorisation control on the release path. Anyone who can land on The workflow-level Suggested fix (post-migration): |
||
| - name: Install tools | ||
| uses: infrablocks/github-actions/asdf_install@v1 | ||
| - name: Install secrets tools | ||
| run: sudo apt-get update && sudo apt-get install -y git-crypt gnupg | ||
| - name: Unlock git-crypt | ||
| run: ./go git_crypt:unlock_with_encrypted_gpg_key | ||
| env: | ||
| ENCRYPTION_PASSPHRASE: ${{ secrets.ENCRYPTION_PASSPHRASE }} | ||
| - name: Set CI git author | ||
| run: ./go repository:set_ci_author | ||
|
Comment on lines
+128
to
+143
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔵 Plan concern (not a defect — this step order is the plan's exact YAML) | Lens: correctness
This inverts the old CircleCI ordering, where Impact: a rare but real failure mode where the release job dies on the pull with a confusing identity error — precisely when Suggested plan amendment: move |
||
| - name: Release | ||
| run: ./go version:release && ./go image:publish | ||
| - name: Notify Slack | ||
| if: ${{ !cancelled() }} | ||
| continue-on-error: true | ||
| run: ./go "slack:notify[${{ job.status }}]" | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 PLAN CONCERN (flagged independently by the correctness and safety lenses) — this block is mandated verbatim by plan §4.2, so it is a plan-level issue, not a defect of this diff. Not a blocker for this PR, but the single item most worth resolving before the fleet rollout.
concurrencyis declared at workflow level, so the group is held for the whole run — including whilereleasesits inwaitingon itsenvironment: releaseapproval. A release is only approved when someone deliberately wants to ship, so the modal outcome of a push tomainis that the run parks inwaitingand holdsgroup: mainuntil the approval expires (default 30 days).With
cancel-in-progress: falseandqueue: max, every subsequent push tomainqueues behind it and never starts: no tests, no rc image, no tag — silently, with no failure signal. The comment's framing ("a slow release approval delays later prereleases instead of cancelling them") describes a bounded delay; the unbounded case looks like the normal one.This is also a regression from CircleCI rather than parity: CircleCI doesn't serialise workflows, so its
holdjob blocked only its own workflow — pushes 2, 3, 4 each rantest+prereleaseindependently.Safety lens adds the 3am version: a release proposed on Friday that nobody approves means main has no CI feedback by Monday, plus a queue of runs that will each publish an rc image and push a tag the moment someone approves or cancels.
Suggested fix (for the plan): scope serialisation to the jobs that actually race on version tags, so the approval wait can't hold it — drop the workflow-level block and put
concurrency: {group: main-prerelease, cancel-in-progress: false}onprerelease. Alternatively splitreleaseinto aworkflow_dispatchworkflow so the approval wait lives outside the group entirely.