Skip to content

Release

Release #2

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
package:
description: Language package to release
required: true
type: choice
options:
- js
- python
- go
version:
description: Stable SemVer version without a leading v, for example 0.3.1
required: true
type: string
dry_run:
description: Validate release without publishing or creating a tag
required: true
type: boolean
default: true
permissions:
contents: read
concurrency:
group: release-${{ inputs.package }}-${{ inputs.version }}
cancel-in-progress: false
jobs:
validate:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.meta.outputs.tag }}
title: ${{ steps.meta.outputs.title }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate release inputs
id: meta
env:
PACKAGE: ${{ inputs.package }}
VERSION: ${{ inputs.version }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
set -euo pipefail
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version must be a stable SemVer value like 0.3.1"
exit 1
fi
if [[ "$DRY_RUN" != "true" && "$GITHUB_REF" != "refs/heads/main" && "$GITHUB_REF" != "refs/heads/master" ]]; then
echo "Real releases must be run from main or master"
exit 1
fi
case "$PACKAGE" in
js) TAG="js/v$VERSION" ;;
python) TAG="python/v$VERSION" ;;
go) TAG="go/v$VERSION" ;;
*)
echo "Unknown package: $PACKAGE"
exit 1
;;
esac
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag already exists: $TAG"
exit 1
fi
{
echo "tag=$TAG"
echo "title=$PACKAGE v$VERSION"
} >> "$GITHUB_OUTPUT"
js-check:
needs: validate
if: ${{ inputs.package == 'js' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Check JS package version
env:
VERSION: ${{ inputs.version }}
run: |
node -e "const pkg = require('./js/package.json'); if (pkg.version !== process.env.VERSION) { throw new Error(`js/package.json version ${pkg.version} does not match ${process.env.VERSION}`); }"
- run: pnpm run verify:fixtures
- run: pnpm -C js test
- run: pnpm -C js build
- run: npm pack --dry-run
working-directory: js
js-publish:
needs:
- validate
- js-check
if: ${{ inputs.package == 'js' && inputs.dry_run == false }}
runs-on: ubuntu-latest
environment: npm
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- run: pnpm -C js build
- run: npm publish --access public
working-directory: js
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.validate.outputs.tag }}
TITLE: ${{ needs.validate.outputs.title }}
run: |
gh release create "$TAG" --target "$GITHUB_SHA" --title "$TITLE" --notes "Published $TITLE to npm."
python-check:
needs: validate
if: ${{ inputs.package == 'python' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pnpm install --frozen-lockfile
- run: pnpm run verify:fixtures
- name: Check Python package version
env:
VERSION: ${{ inputs.version }}
run: |
python - <<'PY'
import os
import pathlib
import tomllib
pyproject = tomllib.loads(pathlib.Path("python/pyproject.toml").read_text())
actual = pyproject["project"]["version"]
expected = os.environ["VERSION"]
if actual != expected:
raise SystemExit(f"python/pyproject.toml version {actual} does not match {expected}")
PY
- run: python -m pip install -e 'python[dev]'
- run: python -m pytest python/tests
- run: python -m ruff check python/src python/tests
- run: python -m build python
- run: python -m twine check python/dist/*
- uses: actions/upload-artifact@v4
with:
name: python-dist
path: python/dist/*
if-no-files-found: error
python-publish:
needs:
- validate
- python-check
if: ${{ inputs.package == 'python' && inputs.dry_run == false }}
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: python-dist
path: python/dist
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.validate.outputs.tag }}
TITLE: ${{ needs.validate.outputs.title }}
run: |
gh release create "$TAG" --target "$GITHUB_SHA" --title "$TITLE" --notes "Published $TITLE to PyPI."
go-check:
needs: validate
if: ${{ inputs.package == 'go' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- uses: actions/setup-go@v5
with:
go-version: "1.22"
cache-dependency-path: go/go.mod
- run: pnpm install --frozen-lockfile
- run: pnpm run verify:fixtures
- run: go test ./...
working-directory: go
- run: go vet ./...
working-directory: go
- run: go mod tidy
working-directory: go
- run: git diff --exit-code -- go/go.mod go/go.sum
- run: test -z "$(git status --porcelain -- go/go.mod go/go.sum)"
go-release:
needs:
- validate
- go-check
if: ${{ inputs.package == 'go' && inputs.dry_run == false }}
runs-on: ubuntu-latest
environment: go-release
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Push Go module tag
env:
TAG: ${{ needs.validate.outputs.tag }}
run: |
git tag "$TAG" "$GITHUB_SHA"
git push origin "$TAG"
- name: Request Go module indexing
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
for attempt in {1..6}; do
if GOPROXY=proxy.golang.org go list -m "github.com/algoux/standard-ranklist-utils/go@v$VERSION"; then
exit 0
fi
echo "Go proxy has not indexed the module yet; retry $attempt/6"
sleep 10
done
GOPROXY=proxy.golang.org go list -m "github.com/algoux/standard-ranklist-utils/go@v$VERSION"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.validate.outputs.tag }}
TITLE: ${{ needs.validate.outputs.title }}
run: |
gh release create "$TAG" --title "$TITLE" --notes "Published $TITLE as a Go module tag."