Skip to content

Commit ef64241

Browse files
authored
feat: split CI and release into separate workflows (#715)
Closes #710 - Move docker-release job to dedicated release.yml, triggered only on release published events without re-running the test suite - Remove release trigger from ci.yml (push/PR only) - Pin all GitHub Actions to full commit SHAs for supply chain safety - Fix SBOM tag mismatch (v2.3.4 vs 2.3.4) by using metadata output Signed-off-by: Will <will@dower.dev>
1 parent 7c0d6bc commit ef64241

File tree

5 files changed

+76
-74
lines changed

5 files changed

+76
-74
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ on:
1919
- '.github/workflows/docs.yml'
2020
- '.github/workflows/release.yml'
2121
- '.github/workflows/dependabot.yml'
22-
release:
23-
types: [published]
2422

2523
concurrency:
2624
group: ci-${{ github.event.pull_request.number || github.ref }}
27-
cancel-in-progress: ${{ github.event_name != 'release' }}
25+
cancel-in-progress: true
2826

2927
jobs:
3028
# ─── LINT + SECURITY ─────────────────────────────────────
@@ -34,14 +32,14 @@ jobs:
3432
permissions:
3533
contents: read
3634
steps:
37-
- uses: actions/checkout@v4
35+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
3836
with:
3937
fetch-depth: 1
40-
- uses: ruby/setup-ruby@v1
38+
- uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
4139
with:
4240
ruby-version: '.ruby-version'
4341
bundler-cache: true
44-
- uses: actions/setup-node@v4
42+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
4543
with:
4644
node-version-file: '.nvmrc'
4745
cache: 'yarn'
@@ -62,18 +60,18 @@ jobs:
6260
permissions:
6361
contents: read
6462
steps:
65-
- uses: actions/checkout@v4
63+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
6664
with:
6765
fetch-depth: 1
68-
- uses: actions/setup-node@v4
66+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
6967
with:
7068
node-version-file: '.nvmrc'
7169
cache: 'yarn'
7270
- run: yarn install --frozen-lockfile
7371
- name: Vitest with coverage
7472
run: yarn test:unit --coverage
7573
- name: Upload frontend coverage
76-
uses: actions/upload-artifact@v4
74+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
7775
if: always()
7876
with:
7977
name: coverage-frontend
@@ -140,14 +138,14 @@ jobs:
140138
VULCAN_LDAP_ADMIN_PASS: GoodNewsEveryone
141139

142140
steps:
143-
- uses: actions/checkout@v4
141+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
144142
with:
145143
fetch-depth: 1
146-
- uses: ruby/setup-ruby@v1
144+
- uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
147145
with:
148146
ruby-version: '.ruby-version'
149147
bundler-cache: true
150-
- uses: actions/setup-node@v4
148+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
151149
with:
152150
node-version-file: '.nvmrc'
153151
cache: 'yarn'
@@ -156,7 +154,7 @@ jobs:
156154
run: sudo apt-get -yqq install libpq-dev
157155

158156
- name: Cache JS build
159-
uses: actions/cache@v4
157+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
160158
id: js-cache
161159
with:
162160
path: app/assets/builds
@@ -178,7 +176,7 @@ jobs:
178176
spec/
179177
180178
- name: Upload backend coverage
181-
uses: actions/upload-artifact@v4
179+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
182180
if: always()
183181
with:
184182
name: coverage-shard-${{ matrix.ci_node_index }}
@@ -202,52 +200,3 @@ jobs:
202200
exit 1
203201
fi
204202
echo "All CI jobs passed"
205-
206-
# ─── DOCKER RELEASE (release only, multi-arch via Build Cloud) ───
207-
docker-release:
208-
needs: [lint, frontend, backend]
209-
if: github.event_name == 'release'
210-
runs-on: ubuntu-24.04
211-
timeout-minutes: 20
212-
permissions:
213-
contents: write # Required for anchore/sbom-action dependency-snapshot submission
214-
packages: write
215-
steps:
216-
- uses: actions/checkout@v4
217-
218-
- name: Login to DockerHub
219-
uses: docker/login-action@v3
220-
with:
221-
username: ${{ vars.DOCKER_USER }}
222-
password: ${{ secrets.DOCKER_PAT }}
223-
224-
- name: Set up Docker Buildx (Build Cloud)
225-
uses: docker/setup-buildx-action@v3
226-
with:
227-
driver: cloud
228-
endpoint: "mitre/mitre-builder"
229-
230-
- name: Docker metadata
231-
id: meta
232-
uses: docker/metadata-action@v5
233-
with:
234-
images: mitre/vulcan
235-
tags: |
236-
type=semver,pattern={{version}}
237-
type=raw,value=latest
238-
239-
- name: Build and push multi-arch image
240-
uses: docker/build-push-action@v6
241-
with:
242-
context: .
243-
platforms: linux/amd64,linux/arm64
244-
push: true
245-
tags: ${{ steps.meta.outputs.tags }}
246-
labels: ${{ steps.meta.outputs.labels }}
247-
248-
- name: SBOM scan and dependency submission
249-
uses: anchore/sbom-action@v0
250-
with:
251-
image: mitre/vulcan:${{ github.event.release.tag_name }}
252-
artifact-name: image.spdx.json
253-
dependency-snapshot: true

.github/workflows/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
pull-requests: write
1414
contents: write
1515
steps:
16-
- uses: hmarr/auto-approve-action@v4
16+
- uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4
1717
- name: Enable auto-merge for Dependabot PRs
1818
run: gh pr merge --auto --squash "$PR_URL"
1919
env:

.github/workflows/docs.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ jobs:
2222
contents: read
2323
steps:
2424
- name: Checkout
25-
uses: actions/checkout@v4
25+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
2626
with:
2727
fetch-depth: 0 # Required: VitePress lastUpdated uses git log timestamps
2828

2929
- name: Setup Node.js
30-
uses: actions/setup-node@v4
30+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
3131
with:
3232
# Docs use Node 24 (same as app). The docs/ subdirectory has its own
3333
# package.json with VitePress/Vue 3 — isolated from the main app's Vue 2.
@@ -36,7 +36,7 @@ jobs:
3636
cache-dependency-path: docs/yarn.lock
3737

3838
- name: Setup Pages
39-
uses: actions/configure-pages@v4
39+
uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4
4040

4141
- name: Install and build docs
4242
env:
@@ -48,7 +48,7 @@ jobs:
4848
yarn build
4949
5050
- name: Upload artifact
51-
uses: actions/upload-pages-artifact@v3
51+
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
5252
with:
5353
path: docs/.vitepress/dist
5454

@@ -66,4 +66,4 @@ jobs:
6666
steps:
6767
- name: Deploy to GitHub Pages
6868
id: deployment
69-
uses: actions/deploy-pages@v4
69+
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4

.github/workflows/release.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
# ─── DOCKER RELEASE (multi-arch via Build Cloud) ───
9+
docker-release:
10+
runs-on: ubuntu-24.04
11+
timeout-minutes: 20
12+
permissions:
13+
contents: write # Required for anchore/sbom-action dependency-snapshot submission
14+
packages: write
15+
steps:
16+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
17+
18+
- name: Login to DockerHub
19+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
20+
with:
21+
username: ${{ vars.DOCKER_USER }}
22+
password: ${{ secrets.DOCKER_PAT }}
23+
24+
- name: Set up Docker Buildx (Build Cloud)
25+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
26+
with:
27+
driver: cloud
28+
endpoint: "mitre/mitre-builder"
29+
30+
- name: Docker metadata
31+
id: meta
32+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
33+
with:
34+
images: mitre/vulcan
35+
tags: |
36+
type=semver,pattern={{version}}
37+
type=raw,value=latest
38+
39+
- name: Build and push multi-arch image
40+
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
41+
with:
42+
context: .
43+
platforms: linux/amd64,linux/arm64
44+
push: true
45+
tags: ${{ steps.meta.outputs.tags }}
46+
labels: ${{ steps.meta.outputs.labels }}
47+
48+
- name: SBOM scan and dependency submission
49+
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
50+
with:
51+
image: mitre/vulcan:${{ steps.meta.outputs.version }}
52+
artifact-name: image.spdx.json
53+
dependency-snapshot: true

spec/config/release_infrastructure_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
RSpec.describe 'release infrastructure' do
66
# Releases are created manually via the GitHub UI.
77
# Version must be consistent across all sources. Docker builds trigger
8-
# on published GitHub releases (handled by ci.yml).
8+
# on published GitHub releases (handled by release.yml).
99

1010
describe 'changelog configuration' do
1111
it 'cliff.toml exists with Keep a Changelog sections' do
@@ -29,10 +29,10 @@
2929
end
3030

3131
describe 'Docker release trigger' do
32-
it 'ci.yml triggers docker-release on published releases' do
33-
ci = Rails.root.join('.github/workflows/ci.yml').read
34-
expect(ci).to match(/release:.*\n.*types:.*published/m),
35-
'ci.yml must trigger on release published events for Docker builds'
32+
it 'release.yml triggers docker-release on published releases' do
33+
release = Rails.root.join('.github/workflows/release.yml').read
34+
expect(release).to match(/release:.*\n.*types:.*published/m),
35+
'release.yml must trigger on release published events for Docker builds'
3636
end
3737
end
3838

0 commit comments

Comments
 (0)