Skip to content

Release

Release #11

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
release:
name: Build and publish release
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
cache: "npm"
cache-dependency-path: "**/package-lock.json"
node-version-file: ".nvmrc"
- name: Install dependencies
run: npm ci
- name: Validate
run: npm run check
- name: Resolve release tag
id: release-tag
env:
GH_REF: ${{ github.ref }}
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
if [[ "$GH_REF" == refs/tags/* ]]; then
TAG="${GITHUB_REF_NAME}"
else
TODAY="v$(date -u +%Y%m%d)"
# Find the highest run number for today's date prefix
LATEST=$(gh api "repos/$GH_REPO/git/matching-refs/tags/${TODAY}." \
--jq '.[].ref' 2>/dev/null \
| sed 's|refs/tags/||' \
| sort -t. -k2 -n \
| tail -1)
if [[ -n "$LATEST" ]]; then
LAST_RUN="${LATEST##*.}"
NEXT_RUN=$((LAST_RUN + 1))
else
NEXT_RUN=1
fi
TAG="${TODAY}.${NEXT_RUN}"
fi
# Validate tag format
if [[ ! "$TAG" =~ ^v[0-9]{8}\.[0-9]+$ ]]; then
echo "::error::Invalid tag format: '$TAG'. Expected v<YYYYMMDD>.<run> (e.g. v20260324.1)"
exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Create tag
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ steps.release-tag.outputs.tag }}
run: |
gh api "repos/$GH_REPO/git/refs" \
-f ref="refs/tags/$RELEASE_TAG" \
-f sha="$GITHUB_SHA"
- name: Build
run: npm run build
env:
BUILD_ID: ${{ steps.release-tag.outputs.tag }}
- name: Upload build artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist
path: dist/
- name: Create release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.release-tag.outputs.tag }}
run: |
TIMESTAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
# Build a Deprecations section from any manifest entries flagged with
# `deprecated: true`. Empty when no deprecations are present.
DEPRECATED_LIST=$(jq -r '
[
.maps | to_entries[] as $map
| $map.value | to_entries[]
| select(.value.deprecated == true)
| "- `\($map.key) \(.key)` (`\(.value.filename)`)"
] | join("\n")
' dist/manifest.json)
if [[ -n "$DEPRECATED_LIST" ]]; then
DEPRECATIONS_SECTION="
## Deprecations
The following schema versions are marked deprecated in this release and have entered their end-of-life support window. Expect them to be removed in a future release; consumers using them should plan to migrate:
${DEPRECATED_LIST}
"
else
DEPRECATIONS_SECTION=""
fi
gh release create "$RELEASE_TAG" \
--title "${RELEASE_TAG}" \
--notes "$(cat <<EOF
## ${RELEASE_TAG}
**Timestamp**: ${TIMESTAMP}
**Git SHA**: ${GITHUB_SHA}
${DEPRECATIONS_SECTION}
See \`manifest.json\` for build metadata and per-map schema versions.
See \`manifest.schema.json\` for the manifest's contract.
See \`checksums.sha256\` to verify asset integrity.
EOF
)" \
--latest \
dist/manifest.json \
dist/manifest.schema.json \
dist/checksums.sha256 \
dist/*.v*.json