Repository navigation
Release #149
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |