Skip to content

Release

Release #31

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
bump:
description: "Version bump type"
required: true
type: choice
options:
- patch
- minor
- major
permissions:
contents: write
id-token: write
jobs:
release:
name: Release
runs-on: depot-ubuntu-latest
timeout-minutes: 20
environment: pypi
outputs:
version: ${{ steps.versions.outputs.new }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
python-version: "3.13"
prune-cache: false
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.10.0
- name: Setup Node.js with pnpm cache
uses: actions/setup-node@v6
with:
cache: 'pnpm'
cache-dependency-path: 'packages/pnpm-lock.yaml'
- name: Install pnpm dependencies
working-directory: packages
run: pnpm install --frozen-lockfile
- name: Install Python dependencies
run: uv sync --all-extras --dev --group release
- name: Determine versions
id: versions
env:
GH_TOKEN: ${{ github.token }}
run: |
# Derive current version from the latest semver git tag so this
# stays correct even when the pyproject.toml version-bump commit
# hasn't been merged to main yet (e.g. partial previous run).
# `|| true` prevents pipefail exit when no tags match the pattern.
LATEST_TAG=$(git tag --sort=-creatordate | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)
if [ -z "$LATEST_TAG" ]; then
CURRENT="0.0.0"
echo "No semver tags found; starting from $CURRENT"
else
# If the latest tag has no GitHub release, a prior run failed
# after tagging. Retry that version instead of bumping again.
if ! gh release view "$LATEST_TAG" &>/dev/null; then
PREV_TAG=$(git tag --sort=-creatordate | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sed -n '2p' || true)
echo "::warning::Tag $LATEST_TAG exists without a release — retrying incomplete release."
{
echo "new=$LATEST_TAG"
echo "prev_tag=${PREV_TAG:-$LATEST_TAG}"
echo "retry=true"
} >> "$GITHUB_OUTPUT"
exit 0
fi
CURRENT="$LATEST_TAG"
fi
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
case "${{ inputs.bump }}" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW="${MAJOR}.${MINOR}.${PATCH}"
# Guard: abort if this tag already exists with a completed release
if git tag -l "$NEW" | grep -q "^${NEW}$"; then
echo "::error::Tag $NEW already exists — this release may have already run. Aborting."
exit 1
fi
{
echo "new=$NEW"
echo "prev_tag=$CURRENT"
echo "retry=false"
} >> "$GITHUB_OUTPUT"
echo "Bumping $CURRENT → $NEW (${{ inputs.bump }})"
- name: Generate release notes
env:
GH_TOKEN: ${{ github.token }}
run: |
uv run python scripts/generate_release_notes.py \
--tag-from "${{ steps.versions.outputs.prev_tag }}" \
--version "${{ steps.versions.outputs.new }}" \
--date "$(date +%Y-%m-%d)" \
--output-dir /tmp
- name: Tag and push
run: |
if [ "${{ steps.versions.outputs.retry }}" = "true" ]; then
echo "Retry mode — tag ${{ steps.versions.outputs.new }} already exists, checking out tagged commit."
git checkout "${{ steps.versions.outputs.new }}"
else
git tag "${{ steps.versions.outputs.new }}"
git push origin "${{ steps.versions.outputs.new }}"
fi
# Pin hatch-vcs to the exact release version for the build step
echo "SETUPTOOLS_SCM_PRETEND_VERSION=${{ steps.versions.outputs.new }}" >> "$GITHUB_ENV"
- name: Build wheel + sdist
run: |
./scripts/full_build.sh
uv build --sdist
- name: Capture release screenshots
run: |
cd packages/buckaroo-js-core
npx playwright install chromium
npx playwright test pw-tests/theme-screenshots.spec.ts --reporter=line || true
VERSION="${{ steps.versions.outputs.new }}"
mkdir -p /tmp/screenshots-staging/"$VERSION"
for f in screenshots/*.png; do
[ -f "$f" ] || continue
base=$(basename "$f" .png)
cp "$f" /tmp/screenshots-staging/"$VERSION"/"${base}--${VERSION}.png"
done
- name: Publish screenshots to GitHub Pages
run: |
VERSION="${{ steps.versions.outputs.new }}"
git fetch origin gh-pages
git worktree add /tmp/gh-pages gh-pages
mkdir -p /tmp/gh-pages/screenshots/"$VERSION"
cp /tmp/screenshots-staging/"$VERSION"/*.png /tmp/gh-pages/screenshots/"$VERSION"/
python scripts/generate_screenshots_index.py /tmp/gh-pages/screenshots
cd /tmp/gh-pages
git add screenshots/
git diff --cached --quiet && echo "No screenshot changes" && exit 0
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "screenshots: add release $VERSION screenshots"
git push origin gh-pages
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${{ steps.versions.outputs.new }}" \
--title "${{ steps.versions.outputs.new }}" \
--notes-file /tmp/release_notes.md
- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ steps.versions.outputs.new }}" dist/*.whl dist/*.tar.gz
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- name: Upload changelog entry artifact
# Download this artifact and apply it in your next dev PR:
# python scripts/update_changelog.py changelog_entry.md
# sed -i 's/^version = ".*"/version = "NEW"/' pyproject.toml
# uv lock
uses: actions/upload-artifact@v7
with:
name: changelog-${{ steps.versions.outputs.new }}
path: /tmp/changelog_entry.md
if-no-files-found: ignore
release-npm:
name: Publish buckaroo-js-core to npm
needs: release
if: needs.release.result == 'success'
# GitHub-hosted runner required: npm Trusted Publishing OIDC is only
# supported on github-hosted runners (Depot's depot-ubuntu-latest is
# treated as self-hosted and won't receive npm OIDC credentials).
runs-on: ubuntu-latest
timeout-minutes: 10
environment: npm
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.10.0
- name: Setup Node.js with npm registry
uses: actions/setup-node@v6
with:
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: 'packages/pnpm-lock.yaml'
- name: Install pnpm dependencies
working-directory: packages
run: pnpm install --frozen-lockfile
- name: Upgrade npm to a Trusted-Publishing-capable version
# Trusted Publishing requires npm >= 11.5.1. The global npm dir
# on github-hosted runners is root-owned, so sudo is needed.
run: sudo npm install -g npm@11.5.1
- name: OIDC diagnostics (compare against npm Trusted Publisher config)
# NOTE: the npm Trusted Publisher on npmjs.com must be updated to
# point to "release.yml" (not "release-npm.yml") now that this job
# lives here. See https://www.npmjs.com/package/buckaroo-js-core/access
run: |
echo "npm version: $(npm --version)"
echo "GITHUB_REPOSITORY: ${GITHUB_REPOSITORY}"
echo "GITHUB_WORKFLOW_REF: ${GITHUB_WORKFLOW_REF}"
echo "GITHUB_REF: ${GITHUB_REF}"
echo "GITHUB_REF_NAME: ${GITHUB_REF_NAME}"
echo "GITHUB_JOB: ${GITHUB_JOB}"
echo "Job environment (yaml): npm"
echo
echo "On npmjs.com the Trusted Publisher must match:"
echo " Owner: $(echo "${GITHUB_REPOSITORY}" | cut -d/ -f1)"
echo " Repository: $(echo "${GITHUB_REPOSITORY}" | cut -d/ -f2)"
echo " Workflow: $(basename "${GITHUB_WORKFLOW_REF%@*}")"
echo " Environment: npm (or leave blank if no environment configured)"
- name: Set buckaroo-js-core version to ${{ needs.release.outputs.version }}
working-directory: packages/buckaroo-js-core
run: npm version "${{ needs.release.outputs.version }}" --no-git-tag-version --allow-same-version
- name: Build buckaroo-js-core
working-directory: packages/buckaroo-js-core
run: pnpm run build
- name: Check if version already published
id: check
run: |
VERSION="${{ needs.release.outputs.version }}"
if npm view "buckaroo-js-core@${VERSION}" version >/dev/null 2>&1; then
echo "buckaroo-js-core@${VERSION} already on npm — skipping publish."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish to npm (Trusted Publishing + provenance)
if: steps.check.outputs.skip != 'true'
working-directory: packages/buckaroo-js-core
run: npm publish --access public --provenance --loglevel=verbose