Skip to content

Commit cd14016

Browse files
committed
Merge GitHub workflow work.
Initial workflow emitted by Claude, then cleaned up by me, cross-checking with Claude.
2 parents 7be3bae + f933ccc commit cd14016

File tree

3 files changed

+238
-0
lines changed

3 files changed

+238
-0
lines changed

.github/workflows/ci.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# CI testing pipeline
2+
#
3+
# This workflow MUST be audited with zizmor.
4+
# This workflow MUST be using pinned action refs.
5+
# This workflow's pinned action refs SHOULD be updated using 'pinact'
6+
#
7+
# Security notes:
8+
# - Top-level permissions are empty; each job declares only what it needs.
9+
# - All third-party actions are SHA-pinned. Run `pinact run` to reverify.
10+
#
11+
# Non-GitHub actions (add to repo Settings > Actions > Allowed actions):
12+
# - go-task/setup-task
13+
14+
name: CI
15+
16+
on:
17+
push:
18+
branches: [main]
19+
pull_request:
20+
workflow_dispatch: # let someone trigger the CI on their branch without a PR
21+
22+
permissions: {}
23+
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
26+
cancel-in-progress: true
27+
28+
jobs:
29+
30+
test:
31+
name: Test
32+
runs-on: ubuntu-slim
33+
34+
permissions:
35+
contents: read # we clone the repo
36+
37+
steps:
38+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
39+
with:
40+
persist-credentials: false
41+
42+
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
43+
with:
44+
go-version-file: go.mod
45+
cache: false
46+
47+
- uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
48+
with:
49+
version: 3.x
50+
51+
- name: Run checks
52+
run: task check

.github/workflows/release.yml

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Release pipeline — triggered by semver tags.
2+
#
3+
# This workflow MUST be audited with zizmor.
4+
# This workflow MUST be using pinned action refs.
5+
# This workflow's pinned action refs SHOULD be updated using 'pinact'
6+
#
7+
# Security notes:
8+
# - Top-level permissions are empty; each job declares only what it needs.
9+
# - The release job runs in the "release" environment (configure protection
10+
# rules in GitHub repo settings: require approval, restrict to tags, etc.).
11+
# - Cosign keyless signing uses GitHub OIDC — no long-lived keys.
12+
# - All third-party actions are SHA-pinned. Run `pinact run` to reverify.
13+
#
14+
# Non-GitHub actions (add to repo Settings > Actions > Allowed actions):
15+
# - goreleaser/goreleaser-action
16+
# - sigstore/cosign-installer
17+
# - go-task/setup-task
18+
19+
name: Release
20+
21+
on:
22+
push:
23+
tags:
24+
- "v[0-9]+.[0-9]+.[0-9]+"
25+
- "v[0-9]+.[0-9]+.[0-9]+-*"
26+
workflow_dispatch:
27+
inputs:
28+
tag:
29+
# The tag should still exist, but use this if some transient error means that
30+
# we failed to start, or complete.
31+
#
32+
# If we did start and it was just a failure to upload, you should consider
33+
# re-running from the failed jobs in the existing action run, instead
34+
# of a new run.
35+
description: "The tag to release (e.g., v1.0.0)"
36+
required: true
37+
38+
permissions: {}
39+
40+
# We don't want to interrupt an existing flow, and we do (or should!) have a
41+
# tag protection ruleset preventing updates/deletions/force-pushes for v*
42+
#
43+
# But if something goes wrong where I want to manually create a release, we do
44+
# have workflow_dispatch. For that, we don't cancel and we don't guard on a
45+
# release existing. My assumption is that the draft release will have been created
46+
# but a transient error blocked the release (hosting or announcement service outage or somesuch).
47+
#
48+
concurrency:
49+
group: ${{ github.workflow }}-${{ github.ref }}
50+
cancel-in-progress: false
51+
52+
jobs:
53+
54+
test:
55+
name: Test
56+
runs-on: ubuntu-slim
57+
58+
permissions:
59+
contents: read # we clone the repo
60+
61+
steps:
62+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
63+
with:
64+
persist-credentials: false
65+
ref: ${{ inputs.tag || github.ref }}
66+
67+
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
68+
with:
69+
go-version-file: go.mod
70+
cache: false
71+
72+
- uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
73+
with:
74+
version: 3.x
75+
76+
- name: Run checks
77+
run: task check
78+
79+
80+
release:
81+
name: Release
82+
needs: test
83+
runs-on: ubuntu-slim
84+
85+
environment: release
86+
permissions:
87+
contents: write # we create a Release and attach assets to it; step id==goreleaser
88+
id-token: write # we use OIDC token exchange as part of cosign keyless signing
89+
attestations: write # We ... write attestations
90+
91+
steps:
92+
- name: Checkout
93+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
94+
with:
95+
fetch-depth: 0
96+
persist-credentials: false
97+
ref: ${{ inputs.tag || github.ref }}
98+
99+
- name: Assert expected tag is current
100+
id: tag-confirm
101+
env:
102+
EXPECTED: ${{ github.ref_name }}
103+
shell: bash
104+
run: |
105+
ACTUAL="$(git describe --tags --abbrev=0)"
106+
if [[ "$ACTUAL" != "$EXPECTED" ]]; then
107+
echo "::error::Tag mismatch — git sees ${ACTUAL@Q}, workflow triggered on ${EXPECTED@Q}"
108+
exit 1
109+
fi
110+
111+
- name: "Install tooling: Golang"
112+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
113+
with:
114+
go-version-file: go.mod
115+
cache: false
116+
117+
- name: "Install tooling: cosign"
118+
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
119+
120+
- name: Run GoReleaser
121+
id: goreleaser
122+
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
123+
with:
124+
version: "~> v2"
125+
args: release --clean
126+
env:
127+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
# Guard against multiple tags, such as a final RC tag and the actual release tag,
129+
# in the repo. The tag-confirm step should have ensured we are not fooling ourselves.
130+
GORELEASER_CURRENT_TAG: ${{ inputs.tag || github.ref_name }}
131+
132+
- name: Attest build provenance
133+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
134+
# Uses ACTIONS_RUNTIME_TOKEN for the uploads, but GITHUB_TOKEN for the API calls
135+
with:
136+
subject-path: |
137+
dist/aifr_*.tar.gz
138+
dist/checksums.txt

goreleaser.yaml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,53 @@ archives:
3333
checksum:
3434
name_template: checksums.txt
3535

36+
# Cosign keyless signing — signs the checksums file, which transitively
37+
# covers all release archives. Requires id-token: write in the workflow.
38+
#
39+
# To verify:
40+
# cosign verify-blob \
41+
# --certificate checksums.txt.pem \
42+
# --signature checksums.txt.sig \
43+
# --certificate-identity-regexp 'https://github.com/PennockTech/aifr/' \
44+
# --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
45+
# checksums.txt
46+
signs:
47+
- cmd: cosign
48+
artifacts: checksum
49+
output: true
50+
args:
51+
- sign-blob
52+
- "--yes"
53+
- "--output-signature=${signature}"
54+
- "--output-certificate=${certificate}"
55+
- "${artifact}"
56+
57+
release:
58+
footer: |
59+
## Verification
60+
61+
All artifacts are checksummed and the checksums file is signed with
62+
[cosign](https://github.com/sigstore/cosign) keyless signing (GitHub OIDC).
63+
64+
```sh
65+
# Download the release assets, then verify:
66+
cosign verify-blob \
67+
--certificate checksums.txt.pem \
68+
--signature checksums.txt.sig \
69+
--certificate-identity-regexp 'https://github.com/PennockTech/aifr/' \
70+
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
71+
checksums.txt
72+
73+
sha256sum -c checksums.txt
74+
```
75+
76+
changelog:
77+
sort: asc
78+
filters:
79+
exclude:
80+
- "^docs:"
81+
- "^test:"
82+
- "^ci:"
83+
3684
snapshot:
3785
version_template: "{{ incpatch .Version }}-next"

0 commit comments

Comments
 (0)