-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate pipeline from CircleCI to GitHub Actions #213
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
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,178 @@ | ||
| name: Main | ||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Concurrency is JOB-level, on prerelease + release only (shared group), NOT | ||
| # workflow-level: a run waiting on the release approval counts as the group's | ||
| # in-progress occupant, so a workflow-level group would freeze ALL main CI | ||
| # (check/test/prerelease of later pushes) for up to the 30-day approval | ||
| # window. The approval itself lives on the slot-free release-gate job, so a | ||
| # pending approval holds no slot. queue: max keeps every queued publish (the | ||
| # default keeps only the newest pending) so close-together merges serialise | ||
| # FIFO on tag operations instead of cancelling each other. | ||
| # 'queue: max' — GA 2026-05-07: https://github.blog/changelog/2026-05-07-github-actions-concurrency-groups-now-allow-larger-queues/ | ||
| # Gate/slot split pattern: https://github.com/orgs/community/discussions/17401 | ||
|
|
||
| jobs: | ||
| skip-ci-check: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| 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 | ||
| timeout-minutes: 30 | ||
| 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 | ||
| timeout-minutes: 30 | ||
| 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 | ||
| # Pinned + checksum-verified: this runs as root on runners that later | ||
| # hold secrets; the checksum is from the release's own .sha256 asset. | ||
| run: | | ||
| if ! command -v docker-compose >/dev/null 2>&1; then | ||
| sudo curl -fsSL \ | ||
| "https://github.com/docker/compose/releases/download/v5.3.1/docker-compose-linux-x86_64" \ | ||
| -o /usr/local/bin/docker-compose | ||
| echo "f9ebc6ebdb19d769b793c245a736caaeb198c62587f13b25c660c13b4987f959 /usr/local/bin/docker-compose" \ | ||
| | sha256sum -c - | ||
| sudo chmod +x /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 | ||
| concurrency: | ||
| group: main-tags | ||
| cancel-in-progress: false | ||
| queue: max | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - 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 | ||
| - 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-gate: | ||
| # Approval-only job: carries the environment but NO concurrency group, so | ||
| # a pending approval never occupies the main-tags slot. Multiple pushes | ||
| # can await approval independently. | ||
| needs: [prerelease] | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| environment: release | ||
| steps: | ||
| - name: Release approved | ||
| run: "true" | ||
|
|
||
| release: | ||
| needs: [release-gate] | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| concurrency: | ||
| group: main-tags | ||
| cancel-in-progress: false | ||
| queue: max | ||
| 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 | ||
|
Comment on lines
+155
to
+160
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. 🔵 Correctness — plan concern (§4.2 prescribes this step and its comment verbatim) This Impact: no behavioural difference, so nothing is broken. It is dead code adding a small failure surface, and it runs before Suggestion: for the family plan, either drop the step ( |
||
| - 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 | ||
| - 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.
🔵 Security / Safety — plan concern (§4.1/§4.2 prescribe this verbatim)
To be clear up front: the pin itself checks out. I verified
docker/composev5.3.1 exists and that the release's published.sha256asset reads exactlyf9ebc6ebdb19d769b793c245a736caaeb198c62587f13b25c660c13b4987f959— the comment's provenance claim is accurate. This note is purely about ordering.curlwrites directly to/usr/local/bin/docker-composeand only then pipes the digest tosha256sum -c -. Verify-after-install inverts the safe idiom: the unverified artefact occupies the final,sudo-owned install location onPATHduring the verification window.Impact: low in practice —
chmod +xnever runs on a failed verification and runners are ephemeral — but any future reordering, retry, or addedPATHconsumer between the two commands turns a checksum failure into an executed artefact. This step runs as root on runners that later holdENCRYPTION_PASSPHRASE.Suggestion: download → verify → install, fleet-wide in the family plan (the same block is in
pr.yaml):