Skip to content

Commit 749e470

Browse files
pranishnepalclaude
andcommitted
ci(gha): automate semantic release flow for Docker app
Previously, releases required a manual package.json version bump commit to trigger the workflow. This was error-prone and relied on a fragile diff-detection heuristic. Going forward, merging a feat: or fix: commit to master automatically triggers semantic-release, which determines the next version from commit history, creates a git tag (e.g. v3.1.0) and GitHub Release, then gates the Trivy scan and Docker build on the release output. chore:/test:/ci: commits produce no release. No human ever touches the version number. Changes: - package.json version set to "0.0.0-semantically-released" placeholder; git tags are now the authoritative version source - .releaserc.json: add @semantic-release/github plugin to create git tags and GitHub Releases natively - release-to-ghcr.yaml: replace get-context + create-release jobs with a single semantic-release step; add concurrency group to prevent overlapping releases Note: v3.0.0 tag already exists on master and will serve as the baseline for semantic-release on first run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6157a5e commit 749e470

4 files changed

Lines changed: 498 additions & 216 deletions

File tree

.github/workflows/release-to-ghcr.yaml

Lines changed: 23 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -5,90 +5,28 @@ on:
55
push:
66
branches:
77
- master
8-
workflow_dispatch:
9-
inputs:
10-
commit_sha:
11-
description: 'Git commit SHA to build and deploy'
12-
required: true
13-
type: string
8+
9+
concurrency:
10+
group: ${{ github.workflow }}
11+
cancel-in-progress: false
1412

1513
permissions:
1614
contents: write # Needed to create new releases
1715
packages: write # Needed to push to GHCR
1816
id-token: write # Needed to create an ephemeral cross-repo token
1917

2018
jobs:
21-
get-context:
22-
name: Generate release context
23-
runs-on: ubuntu-latest
24-
outputs:
25-
new-version: ${{ steps.compute-context.outputs.new-version }}
26-
current-version: ${{ steps.compute-context.outputs.current-version }}
27-
version-changed: ${{ steps.compute-context.outputs.version-changed }}
28-
steps:
29-
- name: Checkout
30-
uses: actions/checkout@v4
31-
with:
32-
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.commit_sha || github.sha }}
33-
fetch-depth: 0 # Fetch all history for git describe to work
34-
35-
- name: Setup Node.js
36-
uses: actions/setup-node@v4
37-
with:
38-
node-version: '22'
39-
40-
- name: Install dependencies
41-
run: npm ci
42-
43-
- name: Compute the context for this release
44-
id: compute-context
45-
run: |
46-
current_version=$(cat package.json | jq -r .version)
47-
48-
# Check if the version in package.json is using semantic versioning
49-
if [[ "$current_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
50-
echo "Current version is a valid semantic version: $current_version"
51-
else
52-
echo "Current version format is not a standard semantic version: $current_version"
53-
exit 1
54-
fi
55-
56-
echo "current-version=$current_version" >> "$GITHUB_OUTPUT"
57-
58-
# Check if the version in package.json was changed in the last commit
59-
previous_commit=$(git rev-parse HEAD~1)
60-
previous_version=$(git show $previous_commit:package.json 2>/dev/null | jq -r .version || echo "")
61-
62-
echo "Previous version: $previous_version"
63-
echo "Current version: $current_version"
64-
65-
if [ "$current_version" != "$previous_version" ] && [[ "$current_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
66-
echo "Version changed from $previous_version to $current_version in the last commit"
67-
echo "version-changed=true" >> $GITHUB_OUTPUT
68-
echo "new-version=$current_version" >> $GITHUB_OUTPUT
69-
elif [ -n "${{ github.event.inputs.commit_sha }}" ] && [[ "$current_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
70-
echo "Workflow was manually triggered, trying to publish $current_version"
71-
echo "version-changed=true" >> $GITHUB_OUTPUT
72-
echo "new-version=$current_version" >> $GITHUB_OUTPUT
73-
else
74-
echo "Version unchanged or not following semantic versioning format"
75-
echo "version-changed=false" >> $GITHUB_OUTPUT
76-
echo "new-version=$current_version" >> $GITHUB_OUTPUT
77-
fi
78-
79-
create-release:
80-
name: Create GitHub release
81-
needs: get-context
82-
if: ${{ needs.get-context.outputs.version-changed == 'true' }}
19+
release:
20+
name: Release
8321
runs-on: ubuntu-latest
22+
# Expose semantic-release outputs so downstream jobs can gate on and read the version
8423
outputs:
85-
release-id: ${{ steps.create-release.outputs.id }}
86-
release-url: ${{ steps.create-release.outputs.html_url }}
24+
new-release-published: ${{ steps.release.outputs.new_release_published }}
25+
new-release-version: ${{ steps.release.outputs.new_release_version }}
8726
steps:
8827
- name: Checkout
8928
uses: actions/checkout@v4
9029
with:
91-
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.commit_sha || github.sha }}
9230
fetch-depth: 0
9331

9432
- name: Setup Node.js
@@ -99,45 +37,24 @@ jobs:
9937
- name: Install dependencies
10038
run: npm ci
10139

102-
- name: Generate release notes
103-
id: generate-notes
104-
run: |
105-
# Generate release notes using the existing .releaserc.json configuration
106-
notes=$(npx semantic-release --dry-run --no-ci --plugins @semantic-release/release-notes-generator 2>/dev/null | grep -A 1000 "Release note for version" | tail -n +2)
107-
108-
echo "notes<<EOF" >> $GITHUB_OUTPUT
109-
echo "$notes" >> $GITHUB_OUTPUT
110-
echo "EOF" >> $GITHUB_OUTPUT
111-
112-
- name: Create release
113-
id: create-release
114-
uses: actions/github-script@v7
115-
with:
116-
script: |
117-
const release = await github.rest.repos.createRelease({
118-
owner: context.repo.owner,
119-
repo: context.repo.repo,
120-
tag_name: `v${process.env.VERSION}`,
121-
name: `v${process.env.VERSION}`,
122-
body: process.env.RELEASE_NOTES,
123-
draft: false,
124-
prerelease: false
125-
});
126-
return release.data;
40+
- name: Release
41+
id: release
42+
run: npx semantic-release
12743
env:
128-
VERSION: ${{ needs.get-context.outputs.new-version }}
129-
RELEASE_NOTES: ${{ steps.generate-notes.outputs.notes }}
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
46+
- name: Report skipped release
47+
if: steps.release.outputs.new_release_published != 'true'
48+
run: echo "No releasable commits found — skipping Docker build"
13049

13150
trivy-scan:
13251
name: Security - Trivy Scan
133-
needs: [get-context]
134-
if: ${{ needs.get-context.outputs.version-changed == 'true' }}
52+
needs: release
53+
if: ${{ needs.release.outputs.new-release-published == 'true' }}
13554
runs-on: ubuntu-latest
13655
steps:
13756
- name: Checkout
13857
uses: actions/checkout@v4
139-
with:
140-
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.commit_sha || github.sha }}
14158

14259
- name: Setup Node.js
14360
uses: actions/setup-node@v4
@@ -160,14 +77,12 @@ jobs:
16077

16178
build-and-push:
16279
name: Build and push image to GHCR
163-
needs: [get-context, create-release, trivy-scan]
164-
if: ${{ needs.get-context.outputs.version-changed == 'true' }}
80+
needs: [release, trivy-scan]
81+
if: ${{ needs.release.outputs.new-release-published == 'true' }}
16582
runs-on: ubuntu-latest
16683
steps:
16784
- name: Checkout
16885
uses: actions/checkout@v4
169-
with:
170-
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.commit_sha || github.sha }}
17186

17287
- name: Set up Docker Buildx
17388
uses: docker/setup-buildx-action@v3
@@ -185,10 +100,10 @@ jobs:
185100
context: .
186101
push: true
187102
tags: |
188-
ghcr.io/bitgo/advanced-wallets:${{ needs.get-context.outputs.new-version }}
103+
ghcr.io/bitgo/advanced-wallets:${{ needs.release.outputs.new-release-version }}
189104
ghcr.io/bitgo/advanced-wallets:latest
190105
build-args: |
191-
BUILD_VERSION=${{ needs.get-context.outputs.new-version }}
106+
BUILD_VERSION=${{ needs.release.outputs.new-release-version }}
192107
BUILD_DATE=${{ github.event.repository.updated_at }}
193108
VCS_REF=${{ github.sha }}
194109
cache-from: type=gha

.releaserc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
]
6767
}
6868
}
69-
]
69+
],
70+
"@semantic-release/github"
7071
]
7172
}

0 commit comments

Comments
 (0)