Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 12 additions & 63 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ on:
- '.github/workflows/docs.yml'
- '.github/workflows/release.yml'
- '.github/workflows/dependabot.yml'
release:
types: [published]

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

jobs:
# ─── LINT + SECURITY ─────────────────────────────────────
Expand All @@ -34,14 +32,14 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
with:
ruby-version: '.ruby-version'
bundler-cache: true
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
Expand All @@ -62,18 +60,18 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install --frozen-lockfile
- name: Vitest with coverage
run: yarn test:unit --coverage
- name: Upload frontend coverage
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: coverage-frontend
Expand Down Expand Up @@ -140,14 +138,14 @@ jobs:
VULCAN_LDAP_ADMIN_PASS: GoodNewsEveryone

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1
with:
ruby-version: '.ruby-version'
bundler-cache: true
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
Expand All @@ -156,7 +154,7 @@ jobs:
run: sudo apt-get -yqq install libpq-dev

- name: Cache JS build
uses: actions/cache@v4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: js-cache
with:
path: app/assets/builds
Expand All @@ -178,7 +176,7 @@ jobs:
spec/

- name: Upload backend coverage
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: coverage-shard-${{ matrix.ci_node_index }}
Expand All @@ -202,52 +200,3 @@ jobs:
exit 1
fi
echo "All CI jobs passed"

# ─── DOCKER RELEASE (release only, multi-arch via Build Cloud) ───
docker-release:
needs: [lint, frontend, backend]
if: github.event_name == 'release'
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write # Required for anchore/sbom-action dependency-snapshot submission
packages: write
steps:
- uses: actions/checkout@v4

- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PAT }}

- name: Set up Docker Buildx (Build Cloud)
uses: docker/setup-buildx-action@v3
with:
driver: cloud
endpoint: "mitre/mitre-builder"

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: mitre/vulcan
tags: |
type=semver,pattern={{version}}
type=raw,value=latest

- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: SBOM scan and dependency submission
uses: anchore/sbom-action@v0
with:
image: mitre/vulcan:${{ github.event.release.tag_name }}
artifact-name: image.spdx.json
dependency-snapshot: true
2 changes: 1 addition & 1 deletion .github/workflows/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
pull-requests: write
contents: write
steps:
- uses: hmarr/auto-approve-action@v4
- uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --squash "$PR_URL"
env:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0 # Required: VitePress lastUpdated uses git log timestamps

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

- name: Setup Pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4

- name: Install and build docs
env:
Expand All @@ -48,7 +48,7 @@ jobs:
yarn build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: docs/.vitepress/dist

Expand All @@ -66,4 +66,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
53 changes: 53 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Release

on:
release:
types: [published]

jobs:
# ─── DOCKER RELEASE (multi-arch via Build Cloud) ───
docker-release:
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write # Required for anchore/sbom-action dependency-snapshot submission
packages: write
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Login to DockerHub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PAT }}

- name: Set up Docker Buildx (Build Cloud)
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
with:
driver: cloud
endpoint: "mitre/mitre-builder"

- name: Docker metadata
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: mitre/vulcan
tags: |
type=semver,pattern={{version}}
type=raw,value=latest

- name: Build and push multi-arch image
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: SBOM scan and dependency submission
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
image: mitre/vulcan:${{ steps.meta.outputs.version }}
artifact-name: image.spdx.json
dependency-snapshot: true
10 changes: 5 additions & 5 deletions spec/config/release_infrastructure_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
RSpec.describe 'release infrastructure' do
# Releases are created manually via the GitHub UI.
# Version must be consistent across all sources. Docker builds trigger
# on published GitHub releases (handled by ci.yml).
# on published GitHub releases (handled by release.yml).

describe 'changelog configuration' do
it 'cliff.toml exists with Keep a Changelog sections' do
Expand All @@ -29,10 +29,10 @@
end

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

Expand Down
Loading