Skip to content

Release

Release #149

Workflow file for this run

name: Release
# workflow_run is restricted to the main branch and guarded at the job level
# with `github.event.workflow_run.head_repository.full_name == github.repository`
# to prevent fork PRs from triggering the release job.
on:
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows:
- CI
types:
- completed
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions: {}
# Skips the pre-commit hook — see ../CONTRIBUTING.md, "Workflows That Commit".
env:
LEFTHOOK: 0
jobs:
release:
name: Release
environment: release-npm
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository }}
runs-on: ubuntu-latest
permissions:
contents: write # required to push release commits and tags
pull-requests: write # required to create and update release PRs
id-token: write # required for npm OIDC trusted publishing
outputs:
published: ${{ steps.changesets.outputs.published }}
publishedPackages: ${{ steps.changesets.outputs['published-packages'] }}
steps:
# A GitHub App token, not GITHUB_TOKEN: events from the default token do not
# start other workflows, so CI would never run on the release PR.
- name: Setup release helper
id: release-helper
uses: marcalexiei/github-actions/setup-github-app-user-bot@e9655755a6dc0040e650a931ee5d958c7d7a9f22 # v2.3.1
with:
app-id: ${{ vars.RELEASE_HELPER_APP_ID }}
private-key: ${{ secrets.RELEASE_HELPER_PRIVATE_KEY }}
# Pinned to the commit CI passed on, after two quick merges had both runs release the
# later one. changesets/action resets to `github.sha` regardless — main's tip under
# `workflow_run` — which a shallow clone cannot resolve once the two differ.
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
fetch-depth: 0
- name: Install Dependencies
uses: marcalexiei/github-actions/setup-node-and-pnpm@e9655755a6dc0040e650a931ee5d958c7d7a9f22 # v2.3.1
# `pnpm release` builds, then `changeset publish` publishes every package whose
# version is not yet on npm, in dependency order.
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2
with:
github-token: ${{ steps.release-helper.outputs.token }}
commit-message: 'chore: release'
pr-title: 'chore: release'
pr-base-branch: main
publish-script: 'pnpm release'
# One release, three channels: npm above, the image here, the site below.
image:
name: Image
needs: release
# The server is tagged like any other package, so its own entry is the gate.
if: ${{ contains(fromJSON(needs.release.outputs.publishedPackages).*.name, '@stats-forge/github-stats-server') }}
uses: $/.github/workflows/publish-image.yml
permissions:
contents: read
packages: write
with:
ref: ${{ github.event.workflow_run.head_sha }}
# Changesets writes the changelog and nothing else, so nothing on the release says which image
# it is. Runs after the push, so the reference it adds is one that already resolves.
annotate:
name: Annotate
needs: [release, image]
runs-on: ubuntu-latest
permissions:
contents: write # required to edit the release body
steps:
- name: Name the image on the release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
PACKAGES: ${{ needs.release.outputs.publishedPackages }}
IMAGE: ghcr.io/${{ github.repository }}-server
PACKAGE: '@stats-forge/github-stats-server'
run: |
version=$(jq -r --arg n "$PACKAGE" '.[] | select(.name == $n) | .version' <<< "$PACKAGES")
if [ -z "$version" ]; then
echo "No entry for $PACKAGE; nothing to annotate."
exit 0
fi
tag="$PACKAGE@$version"
reference="$IMAGE:$version"
body=$(gh release view "$tag" --json body --jq '.body')
# Re-running the release must not append it twice.
if [ "${body#*"$reference"}" != "$body" ]; then
echo "$tag already names $reference."
exit 0
fi
# The bare reference in a fenced block: it pastes into a compose `image:` as readily as
# into a `docker run`, and GitHub puts a copy button on a block but not on inline code.
cat > notes.md <<EOF
$body
---
This release ships as a container image:
\`\`\`
$reference
\`\`\`
EOF
gh release edit "$tag" --notes-file notes.md
# The site documents every released package, the server included, so it goes out with a release.
docs:
name: Docs
needs: release
if: ${{ needs.release.outputs.published == 'true' }}
uses: $/.github/workflows/deploy-docs.yml
permissions:
contents: read
pages: write
id-token: write
with:
ref: ${{ github.event.workflow_run.head_sha }}
# Its own job, so a comment that fails is a red job to re-run rather than an error swallowed
# inside a green release: the packages are already on npm by the time it runs.
comment:
name: Comment
needs: release
if: ${{ needs.release.outputs.published == 'true' }}
runs-on: ubuntu-latest
environment: release-npm # where the release helper's credentials live
permissions:
contents: read # required to check out the repository
steps:
# The app's token, not the default one: it carries issues:write, and the comments
# then come from the same bot that opened the release PR.
- name: Setup release helper
id: release-helper
uses: marcalexiei/github-actions/setup-github-app-user-bot@e9655755a6dc0040e650a931ee5d958c7d7a9f22 # v2.3.1
with:
app-id: ${{ vars.RELEASE_HELPER_APP_ID }}
private-key: ${{ secrets.RELEASE_HELPER_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
fetch-depth: 0 # the commenter reads the history before the release commit
- name: Comment on shipped PRs and issues
uses: marcalexiei/changesets-release-commenter@ec778ccda5a45d2eff60cb43c58f4e2e144cd2ea # v0.3.0
with:
github-token: ${{ steps.release-helper.outputs.token }}
published-packages: ${{ needs.release.outputs.publishedPackages }}
on-ci-fail:
name: 'Failure'
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- run: echo "CI failed. Release will not run"