Skip to content

Tag a Release

Tag a Release #351

Workflow file for this run

# Tag a new release using https://github.com/marketplace/actions/conventional-commits-versioner-action
#
# This is easier than having to run manual `git` operations on a local clone.
# It also runs on a schedule so we don't leave commits unreleased indefinitely
# (avoiding users having to ping "hey could someone cut a release").
name: Tag a Release
on:
# Allow devs to tag manually through the GitHub UI.
# For example after landing a fix that customers are waiting for.
workflow_dispatch:
schedule:
- cron: "0 15 * * *" # Daily at 3PM UTC
jobs:
tag:
permissions:
contents: write # allow create tag
runs-on: ubuntu-latest
outputs:
new-tag: ${{ steps.ccv.outputs.new-tag }}
new-tag-version: ${{ steps.ccv.outputs.new-tag-version }}
new-tag-version-type: ${{ steps.ccv.outputs.new-tag-version-type }}
recently-tagged: ${{ steps.recent-tag.outputs.recently-tagged }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-tags: true
- name: Check if there is a recent tag
id: recent-tag
# Only skip on cron trigger, not manual trigger
if: github.event_name == 'schedule'
shell: bash
run: |
# Skip bumping the tag if the latest tag is more recent than 2 weeks.
# This is a trade-off between making too many releases,
# which overwhelms BCR maintainers and over-notifies users,
# and releasing too infrequently which delays delivery of bugfixes and features.
set -Eeuo pipefail
: "${GITHUB_OUTPUT:?}"
human_readable_duration() {
local secs=$1
local days=$((secs/86400))
local hours=$((secs%86400/3600))
local mins=$((secs%3600/60))
if ((days > 0)); then
printf '%dd %dh %dm' "$days" "$hours" "$mins"
elif ((hours > 0)); then
printf '%dh %dm' "$hours" "$mins"
elif ((mins > 0)); then
printf '%dm' "$mins"
else
printf '%ds' "$((secs%60))"
fi
}
# 2 weeks minus 12 hours to ensure reliable 2 weeks timing of the daily cron job
MAX_AGE=$((14 * 24 * 3600 - 12 * 3600))
# Ensure tags are fetched (checkout must have fetch-depth: 0, fetch-tags: true)
TAG=$(git describe --tags --match 'v[0-9]*.[0-9]*.[0-9]*' --abbrev=0 2>/dev/null) || {
echo "No matching tag — continue workflow."
echo "recently-tagged=false" >> "$GITHUB_OUTPUT"
exit 0
}
TAG_TIME=$(git log -1 --format=%ct "$TAG")
NOW=$(date +%s)
AGE=$((NOW - TAG_TIME))
HUMAN_AGE=$(human_readable_duration "$AGE")
echo "Latest tag: $TAG (${HUMAN_AGE} ago, ${AGE}s total)"
if [ "$AGE" -lt "$MAX_AGE" ]; then
echo "recently-tagged=true" >> "$GITHUB_OUTPUT"
echo "A recent tag exists, skip tagging."
else
echo "recently-tagged=false" >> "$GITHUB_OUTPUT"
echo "No recent tag exists, a new tag will be created."
fi
- name: Bump tag if necessary
id: ccv
if: github.event_name != 'schedule' || steps.recent-tag.outputs.recently-tagged != 'true'
uses: smlx/ccv@7318e2f25a52dcd550e75384b84983973251a1f8 # v0.10.0
release:
needs: tag
uses: ./.github/workflows/release.yaml
with:
tag_name: ${{ needs.tag.outputs.new-tag-version }}
secrets:
publish_token: ${{ secrets.BCR_PUBLISH_TOKEN }}
if: needs.tag.outputs.new-tag == 'true' && needs.tag.outputs.new-tag-version-type != 'major'
permissions:
contents: write
id-token: write
attestations: write