Skip to content

Release

Release #37

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
tag:
description: "Exact release tag, for example v2.3.0"
required: true
type: string
action:
description: "Create a new tag first, or retry an existing tagged release"
required: true
default: prepare-and-release
type: choice
options:
- prepare-and-release
- retry-release
push:
tags:
- "v*"
permissions:
contents: write
id-token: write
concurrency:
group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
cancel-in-progress: false
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
jobs:
prepare_tag:
if: github.event_name == 'workflow_dispatch' && inputs.action == 'prepare-and-release'
runs-on: ubuntu-latest
steps:
- name: Validate requested tag
shell: bash
run: |
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid release tag: $RELEASE_TAG"
exit 1
fi
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: 24
package-manager-cache: false
- name: Validate main and ensure the tag is new
shell: bash
run: |
git fetch origin main --tags --force
git checkout --detach origin/main
expected_tag="v$(node -p 'require("./package.json").version')"
if [ "$RELEASE_TAG" != "$expected_tag" ]; then
echo "::error::$RELEASE_TAG does not match package.json version tag $expected_tag"
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
echo "::error::$RELEASE_TAG already exists. Use action=retry-release instead"
exit 1
fi
- run: npm run check
- run: npm test
- run: npm run test:package
- name: Validate release metadata before tagging
env:
RELEASE_REF_TYPE: tag
RELEASE_REF_NAME: ${{ env.RELEASE_TAG }}
run: node scripts/release-metadata.js --notes "$RUNNER_TEMP/release-notes.md"
- name: Create and push the validated tag
shell: bash
run: |
validated_commit="$(git rev-parse HEAD)"
main_commit="$(git rev-parse origin/main)"
if [ "$validated_commit" != "$main_commit" ]; then
echo "::error::Refusing to tag a commit that is not the exact current main commit"
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
echo "::error::$RELEASE_TAG appeared during validation; refusing to move or overwrite it"
exit 1
fi
git tag "$RELEASE_TAG" "$validated_commit"
git push origin "refs/tags/$RELEASE_TAG"
remote_commit="$(git ls-remote --tags origin "refs/tags/$RELEASE_TAG" | awk '{print $1}')"
if [ "$remote_commit" != "$validated_commit" ]; then
echo "::error::Remote tag verification failed for $RELEASE_TAG"
exit 1
fi
echo "Created $RELEASE_TAG at $validated_commit. Publication starts only after this job completes."
release:
needs: prepare_tag
if: >-
always() &&
github.repository == 'Honguan/codex-model-router' &&
(
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'retry-release') ||
needs.prepare_tag.result == 'success'
)
runs-on: ubuntu-latest
steps:
- name: Validate release request
shell: bash
run: |
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid release tag: $RELEASE_TAG"
exit 1
fi
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "${{ inputs.action }}" = "prepare-and-release" ] && [ "${{ needs.prepare_tag.result }}" != "success" ]; then
echo "::error::Tag preparation did not complete successfully"
exit 1
fi
- uses: actions/checkout@v6
with:
ref: ${{ env.RELEASE_TAG }}
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: 24
package-manager-cache: false
registry-url: https://registry.npmjs.org
- name: Use an OIDC-capable npm CLI
run: npm install --global npm@11.18.0
- name: Verify the existing release tag
shell: bash
run: |
git fetch origin main --tags --force
if ! git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
echo "::error::$RELEASE_TAG does not exist on the remote; publication is forbidden"
exit 1
fi
tag_commit="$(git rev-list -n 1 "$RELEASE_TAG")"
remote_commit="$(git ls-remote --tags origin "refs/tags/$RELEASE_TAG" | awk '{print $1}')"
if [ "$tag_commit" != "$remote_commit" ]; then
echo "::error::Local and remote tag commits differ for $RELEASE_TAG"
exit 1
fi
if [ "$(git rev-parse HEAD)" != "$tag_commit" ]; then
echo "::error::Checkout is not at the exact $RELEASE_TAG commit"
exit 1
fi
if ! git merge-base --is-ancestor "$tag_commit" origin/main; then
echo "::error::$RELEASE_TAG is not reachable from main"
exit 1
fi
expected_tag="v$(node -p 'require("./package.json").version')"
if [ "$RELEASE_TAG" != "$expected_tag" ]; then
echo "::error::$RELEASE_TAG does not match package.json version tag $expected_tag"
exit 1
fi
- run: npm run check
- run: npm test
- run: npm run test:package
- name: Validate release metadata and extract notes
id: metadata
env:
RELEASE_REF_TYPE: tag
RELEASE_REF_NAME: ${{ env.RELEASE_TAG }}
run: node scripts/release-metadata.js --notes "$RUNNER_TEMP/release-notes.md"
- name: Inspect npm package state
id: npm
env:
PACKAGE_NAME: ${{ steps.metadata.outputs.name }}
PACKAGE_VERSION: ${{ steps.metadata.outputs.version }}
shell: bash
run: |
existing_name="$(npm view "$PACKAGE_NAME" name 2>/dev/null || true)"
if [ "$existing_name" != "$PACKAGE_NAME" ]; then
echo "::error::$PACKAGE_NAME does not exist on npm. Trusted Publishing must be configured on an existing package"
exit 1
fi
published="$(npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version 2>/dev/null || true)"
if [ "$published" = "$PACKAGE_VERSION" ]; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} already exists on npm"
else
echo "publish=true" >> "$GITHUB_OUTPUT"
fi
- name: Publish to npm with Trusted Publishing and provenance
if: steps.npm.outputs.publish == 'true'
run: npm publish --access public --provenance
- name: Wait for npm registry propagation
env:
PACKAGE_NAME: ${{ steps.metadata.outputs.name }}
PACKAGE_VERSION: ${{ steps.metadata.outputs.version }}
shell: bash
run: |
for attempt in 1 2 3 4 5 6 7 8 9 10 11 12; do
published="$(npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version 2>/dev/null || true)"
if [ "$published" = "$PACKAGE_VERSION" ]; then
echo "Verified ${PACKAGE_NAME}@${PACKAGE_VERSION} on npm"
exit 0
fi
echo "Waiting for npm registry propagation (${attempt}/12)"
sleep 10
done
echo "::error::Unable to verify ${PACKAGE_NAME}@${PACKAGE_VERSION} on npm"
exit 1
- name: Verify the public package end to end
env:
PACKAGE_NAME: ${{ steps.metadata.outputs.name }}
PACKAGE_VERSION: ${{ steps.metadata.outputs.version }}
run: node scripts/verify-published.js "$PACKAGE_NAME" "$PACKAGE_VERSION"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_NOTES: ${{ steps.metadata.outputs.notes }}
shell: bash
run: |
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
echo "$RELEASE_TAG already exists"
else
gh release create "$RELEASE_TAG" --verify-tag --title "$RELEASE_TAG" --notes-file "$RELEASE_NOTES"
fi