Skip to content

Commit 6f8fa18

Browse files
committed
feat: Enhance workflows with release tagging and semver support
1 parent a0cfad1 commit 6f8fa18

2 files changed

Lines changed: 76 additions & 16 deletions

File tree

.github/workflows/semantic-release.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ on:
1212
release-created:
1313
description: 'Whether semantic-release created a new release/tag'
1414
value: ${{ jobs.release.outputs.release-created }}
15+
release-tag:
16+
description: 'The tag created by semantic-release (empty if no release was created)'
17+
value: ${{ jobs.release.outputs.release-tag }}
1518

1619
permissions:
1720
contents: write
@@ -21,16 +24,21 @@ permissions:
2124
jobs:
2225
release:
2326
runs-on: ubuntu-latest
27+
# Prevent concurrent releases from the same repository. A queued run will wait
28+
# rather than being cancelled, ensuring no release commit is ever skipped.
2429
concurrency:
2530
group: semantic-release-${{ github.repository }}
2631
cancel-in-progress: false
2732
outputs:
2833
release-created: ${{ steps.release-check.outputs.release-created }}
34+
release-tag: ${{ steps.release-check.outputs.release-tag }}
2935

3036
steps:
3137
- name: Checkout code
3238
uses: actions/checkout@v4
3339
with:
40+
# Full history is required — semantic-release reads all commits since the last
41+
# tag to determine the next version and generate the changelog.
3442
fetch-depth: 0
3543

3644
- name: Set up Node.js
@@ -47,6 +55,8 @@ jobs:
4755
@semantic-release/changelog \
4856
@semantic-release/github
4957
58+
# Snapshot the current tags before running semantic-release so we can diff
59+
# afterwards to find exactly which tag (if any) was newly created.
5060
- name: Capture tags before release
5161
run: |
5262
git fetch --tags --force
@@ -62,20 +72,39 @@ jobs:
6272
git fetch --tags --force
6373
git tag -l | sort > /tmp/tags-after.txt
6474
75+
# semantic-release does not reliably expose whether it created a release via exit
76+
# codes or stdout — behaviour varies across plugin configurations. Instead we diff
77+
# the tag snapshots taken before and after, then check whether any new tag points
78+
# at HEAD. This approach works regardless of the release.config.js in the caller repo.
6579
- name: Determine whether release was created
6680
id: release-check
6781
run: |
6882
head_sha=$(git rev-parse HEAD)
83+
84+
# comm -13 outputs lines present in the second file but not the first,
85+
# i.e. tags that did not exist before semantic-release ran.
6986
new_tags=$(comm -13 /tmp/tags-before.txt /tmp/tags-after.txt)
7087
release_created=false
88+
release_tag=""
89+
90+
# Short-circuit if no new tags were created at all.
91+
if [ -z "$new_tags" ]; then
92+
echo "release-created=false" >> "$GITHUB_OUTPUT"
93+
echo "release-tag=" >> "$GITHUB_OUTPUT"
94+
exit 0
95+
fi
7196
7297
while IFS= read -r tag; do
98+
# Lightweight tags point directly to a commit; annotated tags point to a tag
99+
# object which in turn points to a commit. The '^{}' suffix dereferences an
100+
# annotated tag to its underlying commit SHA so both cases are handled uniformly.
73101
tag_sha=$(git rev-parse "${tag}^{}" 2>/dev/null || git rev-parse "$tag")
74102
if [ "$tag_sha" = "$head_sha" ]; then
75103
release_created=true
104+
release_tag="$tag"
76105
break
77106
fi
78107
done <<< "$new_tags"
79108
80109
echo "release-created=$release_created" >> "$GITHUB_OUTPUT"
81-
110+
echo "release-tag=$release_tag" >> "$GITHUB_OUTPUT"

.github/workflows/semver-container.yml

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ on:
1212
required: false
1313
default: 'ghcr.io'
1414
type: string
15+
tag:
16+
description: 'The release tag to derive semver from (e.g. 1.2.3 or v1.2.3)'
17+
required: true
18+
type: string
1519

1620
env:
1721
image-name: ${{ inputs.image-name }}
@@ -22,47 +26,74 @@ jobs:
2226
runs-on: ubuntu-latest
2327
permissions:
2428
packages: write # container images
25-
contents: write # releases
2629
steps:
27-
- name: Check out the repo
28-
uses: actions/checkout@v4
29-
30-
# some docker actions need all lowercase
30+
# docker/metadata-action and akhilerm/tag-push-action require lowercase registry paths.
31+
# GITHUB_REPOSITORY_OWNER can contain uppercase letters (e.g. "Health-Informatics-UoN"),
32+
# so we normalise it here and store it in GITHUB_ENV for all subsequent steps.
3133
- name: downcase repo-owner
3234
run: |
3335
echo "REPO_OWNER_LOWER=${GITHUB_REPOSITORY_OWNER,,}" >>${GITHUB_ENV}
3436
3537
- name: Parse version from tag
3638
id: version
37-
uses: release-kit/semver@97491c46500b6e758ced599794164a234b8aa08c # v2.0.7
39+
run: |
40+
# The tag input may optionally be prefixed with 'v' (e.g. v1.2.3).
41+
# Strip it so all subsequent steps work with a bare semver string.
42+
version="${{ inputs.tag }}"
43+
version="${version#v}"
44+
45+
# Extract the major and minor components for floating tags (e.g. '1' and '1.2').
46+
# Floating tags let consumers pin to a major or minor version without knowing
47+
# the exact patch, following standard container image tagging conventions.
48+
major=$(echo "$version" | cut -d. -f1)
49+
minor=$(echo "$version" | cut -d. -f2)
50+
echo "version=$version" >> "$GITHUB_OUTPUT"
51+
echo "major=$major" >> "$GITHUB_OUTPUT"
52+
echo "major-minor=$major.$minor" >> "$GITHUB_OUTPUT"
53+
54+
# A hyphen in the version string indicates a pre-release (e.g. 1.0.0-beta.1).
55+
# Floating major/minor tags must NOT be applied to pre-releases — those tags
56+
# should always point at the latest stable release, not a pre-release build.
57+
if [[ "$version" == *-* ]]; then
58+
echo "is-prerelease=true" >> "$GITHUB_OUTPUT"
59+
else
60+
echo "is-prerelease=false" >> "$GITHUB_OUTPUT"
61+
fi
3862
39-
# check image exists for commit
40-
- uses: tyriis/docker-image-tag-exists@71a750a41aa78e4efb0842f538140c5df5b8166f # v2.1.0
63+
# Verify that the edge image built from this commit SHA actually exists in the registry
64+
# before attempting to retag it. Fails fast rather than producing a misleading error
65+
# from the tag-push step if the publish-container workflow hasn't run yet.
66+
- name: Check image exists for commit
67+
uses: tyriis/docker-image-tag-exists@71a750a41aa78e4efb0842f538140c5df5b8166f # v2.1.0
4168
with:
4269
registry: ${{ env.registry }}
4370
repository: ${{ env.REPO_OWNER_LOWER }}/${{ env.image-name }}
4471
tag: ${{ github.sha }}
4572

46-
# standard login to the container registry
4773
- name: Docker Login
4874
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
4975
with:
5076
registry: ${{ env.registry }}
5177
username: ${{github.actor}}
5278
password: ${{secrets.GITHUB_TOKEN}}
5379

54-
# We still use the metadata action to help build out our tags from the Workflow Run
80+
# Build the list of tags to apply. We use type=raw because the version comes from
81+
# the 'tag' input rather than from GITHUB_REF — this workflow is triggered via
82+
# workflow_call on a branch push, so GITHUB_REF is a branch ref, not a tag ref.
83+
# The major and minor floating tags are gated on is-prerelease so they are only
84+
# moved forward for stable releases.
5585
- name: Docker Metadata action
5686
id: meta
5787
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
5888
with:
5989
images: ${{ env.registry }}/${{ env.REPO_OWNER_LOWER }}/${{ env.image-name }}
60-
tags: | # new tags only
61-
type=semver,pattern={{version}}
62-
type=semver,pattern={{major}}
63-
type=semver,pattern={{major}}.{{minor}}
90+
tags: |
91+
type=raw,value=${{ steps.version.outputs.version }}
92+
type=raw,value=${{ steps.version.outputs.major }},enable=${{ steps.version.outputs.is-prerelease == 'false' }}
93+
type=raw,value=${{ steps.version.outputs.major-minor }},enable=${{ steps.version.outputs.is-prerelease == 'false' }}
6494
65-
# apply the new tags to the existing images
95+
# Rather than rebuilding the image, we retag the existing edge image (tagged with
96+
# the commit SHA by publish-container) with the semver tags computed above.
6697
- name: Push updated image tags
6798
uses: akhilerm/tag-push-action@f35ff2cb99d407368b5c727adbcc14a2ed81d509 # v2.2.0
6899
with:

0 commit comments

Comments
 (0)