UNPKG

auspice

Version:

Web app for visualizing pathogen evolution

275 lines (248 loc) 11.6 kB
on: workflow_dispatch: inputs: bump: description: >- On master branch: major | feat | minor, or "promote" to ship the current prerelease as stable (e.g. 3.0.0-alpha.2 -> 3.0.0). Other branches: major | feat | minor to start a new prerelease series, or "continue" to increment the existing one. type: choice options: [feat, major, minor, continue, promote] default: feat label: description: Prerelease label. Must be "none" on master; required elsewhere. type: choice options: [none, alpha, beta, rc] default: none dry-run: description: Compute and validate everything, push/publish nothing. type: boolean default: false name: release # The branch being released is the ref this workflow was dispatched against, i.e. # github.ref_name, and this file is read from that branch. There is deliberately no # "branch" input: the workflow and the code it releases are then always the same commit. # Consequence: release.yaml must exist on any branch you want to release from. concurrency: group: release cancel-in-progress: false jobs: prepare-and-tag: runs-on: ubuntu-latest permissions: contents: write outputs: version: ${{ steps.compute.outputs.version }} tag: ${{ steps.compute.outputs.tag }} dist-tag: ${{ steps.compute.outputs.dist-tag }} steps: - uses: actions/checkout@v7 with: # Tags and origin/master are both needed below, by the existing-tag check and # the drift warning respectively. fetch-depth: 0 - uses: actions/setup-node@v7 with: node-version: 24 # Note: --ignore-scripts skips an otherwise unnecessary Auspice build. We only need # node_modules here for semver. - run: npm ci --loglevel verbose --ignore-scripts - name: Check the dispatched ref is a releasable branch run: | if [[ "$GITHUB_REF_TYPE" != branch ]]; then echo "This workflow releases a branch, but was dispatched against a $GITHUB_REF_TYPE ($GITHUB_REF_NAME)." >&2 exit 1 fi if [[ "$GITHUB_REF_NAME" == release ]]; then echo "The 'release' branch is an output of this workflow, not an input. Dispatch against master instead." >&2 exit 1 fi - name: Check package.json and src/version.js agree run: | package_version="$(node -p 'require("./package.json").version')" src_version="$(node -p 'require("./src/version.js").version')" if [[ "$package_version" != "$src_version" ]]; then echo "package.json version ($package_version) doesn't match src/version.js version ($src_version)." >&2 exit 1 fi echo "Current version: $package_version" - name: Check CI passed for this commit # Explicit `shell: bash` for pipefail, so a failing `gh api` can't be mistaken for # "no runs found" below. shell: bash env: GITHUB_TOKEN: ${{ github.token }} run: | # Note that pushes to branches other than master and release don't trigger ci, # so there may legitimately be no runs to check (e.g. a long-lived v3 branch # with no open PR). We only fail on a run we can see which didn't succeed. conclusions="$(gh api --paginate "repos/$GITHUB_REPOSITORY/actions/runs?head_sha=$GITHUB_SHA" \ --jq '.workflow_runs[] | select(.name == "ci") | .conclusion // "in-progress"' \ | sort -u)" if [[ -z "$conclusions" ]]; then echo "::warning::No 'ci' workflow run found for $GITHUB_SHA; releasing without a green CI signal." elif [[ "$conclusions" != success ]]; then echo "The 'ci' workflow for $GITHUB_SHA concluded: $(echo "$conclusions" | paste -sd' ' -)." >&2 echo "Fix CI (or re-run it) before releasing." >&2 exit 1 else echo "ci passed for $GITHUB_SHA" fi - name: Compute the new version id: compute run: | release="$(node scripts/compute-release-version.js \ --bump="$BUMP" --label="$LABEL" --branch="$GITHUB_REF_NAME" --json)" echo "$release" jq -r ' "version=\(.version)", "tag=\(.tag)", "dist-tag=\(.distTag)", "description=\(.description)" ' <<<"$release" >> "$GITHUB_OUTPUT" env: BUMP: ${{ inputs.bump }} LABEL: ${{ inputs.label }} - name: Check the new version isn't already taken env: TAG: ${{ steps.compute.outputs.tag }} VERSION: ${{ steps.compute.outputs.version }} run: | if git rev-parse -q --verify "refs/tags/$TAG" > /dev/null; then echo "Tag $TAG already exists." >&2 exit 1 fi if [[ -n "$(npm view "auspice@$VERSION" version 2> /dev/null)" ]]; then echo "auspice@$VERSION is already published to npm." >&2 exit 1 fi - name: Warn if the release tooling has drifted from master run: | # release.yaml is read from the branch being released. We may make changes to the script # from time-to-time, so flag this as a warning not as a fatal error. for file in .github/workflows/release.yaml scripts/compute-release-version.js; do if ! git diff --quiet origin/master -- "$file"; then echo "::warning file=$file::$file differs from origin/master; this release is using $GITHUB_REF_NAME's copy." echo "⚠️ \`$file\` differs from \`origin/master\`." >> "$GITHUB_STEP_SUMMARY" fi done - name: Bump the version env: VERSION: ${{ steps.compute.outputs.version }} run: | # Updates package.json and package-lock.json; src/version.js is mirrored by hand. current="$(node -p 'require("./package.json").version')" npm version "$VERSION" --no-git-tag-version perl -pi -e "s/version = \"\Q$current\E\";/version = \"$VERSION\";/" src/version.js if [[ "$(node -p 'require("./src/version.js").version')" != "$VERSION" ]]; then echo "Failed to rewrite the version in src/version.js." >&2 exit 1 fi git diff --stat - name: Add a CHANGELOG heading if: ${{ github.ref_name == 'master' }} env: VERSION: ${{ steps.compute.outputs.version }} run: | # Prepend an h2 for this release while preserving the h1. Both the YYYY/MM/DD # format and the lowercase word "version" are relied upon by # scripts/extract-release-notes.js, which produces the GitHub release body. # Prereleases don't touch the CHANGELOG at all. today="$(date +'%Y/%m/%d')" echo -e "# Changelog\n\n## version ${VERSION} - ${today}\n\n$(tail -n +2 CHANGELOG.md)" > CHANGELOG.md git diff -- CHANGELOG.md - name: Summarise env: VERSION: ${{ steps.compute.outputs.version }} DESCRIPTION: ${{ steps.compute.outputs.description }} DIST_TAG: ${{ steps.compute.outputs.dist-tag }} run: | { echo "## Auspice $VERSION" echo echo "| | |" echo "| --- | --- |" echo "| branch | \`$GITHUB_REF_NAME\` |" echo "| description | $DESCRIPTION |" echo "| npm dist-tag | \`$DIST_TAG\` |" echo "| dry run | ${{ inputs.dry-run }} |" } >> "$GITHUB_STEP_SUMMARY" - name: Commit, tag and push if: ${{ !inputs.dry-run }} env: VERSION: ${{ steps.compute.outputs.version }} TAG: ${{ steps.compute.outputs.tag }} DESCRIPTION: ${{ steps.compute.outputs.description }} run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git add package.json package-lock.json src/version.js CHANGELOG.md git commit -m "version bump to $VERSION for release" git tag -a "$TAG" -m "$DESCRIPTION" git push origin "HEAD:refs/heads/$GITHUB_REF_NAME" git push origin "refs/tags/$TAG" - name: Fast-forward the release branch # nextstrain/docker-base installs Auspice from this branch rather than from npm, # so it must stay a fast-forward of master. Prereleases never touch it. if: ${{ !inputs.dry-run && github.ref_name == 'master' }} run: git push origin HEAD:refs/heads/release publish-npm: needs: [prepare-and-tag] runs-on: ubuntu-latest permissions: id-token: write # Required for OIDC contents: read steps: - uses: actions/checkout@v7 with: # Publish exactly what was tagged. Under a dry run there is no tag, so fall back # to the dispatched ref (i.e. the branch) and apply the version bump locally. ref: ${{ inputs.dry-run && github.ref || needs.prepare-and-tag.outputs.tag }} - uses: actions/setup-node@v7 with: node-version: 24 registry-url: https://registry.npmjs.org/ - run: node --version - if: ${{ inputs.dry-run }} run: npm version "${{ needs.prepare-and-tag.outputs.version }}" --no-git-tag-version # Don't use `--ignore-scripts` -- the "prepare" life cycle script builds what we publish - run: npm ci --loglevel verbose # --tag is always passed explicitly, because `npm publish` would otherwise point # `latest` at a prerelease and serve it to every plain `npm install auspice`. - run: npm publish --tag "$DIST_TAG" ${{ inputs.dry-run && '--dry-run' || '' }} env: DIST_TAG: ${{ needs.prepare-and-tag.outputs.dist-tag }} github-release: needs: [prepare-and-tag, publish-npm] # Prereleases get no GitHub release; the tag is enough. Their notes would have to be # generated by GitHub, since prereleases don't touch the CHANGELOG. if: ${{ !inputs.dry-run && github.ref_name == 'master' }} runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v7 with: ref: ${{ needs.prepare-and-tag.outputs.tag }} - name: Create the GitHub release # Explicit `shell: bash` for pipefail, so a failure to extract the release notes # doesn't quietly produce an empty release. shell: bash env: GITHUB_TOKEN: ${{ github.token }} VERSION: ${{ needs.prepare-and-tag.outputs.version }} TAG: ${{ needs.prepare-and-tag.outputs.tag }} run: | # Takes the topmost CHANGELOG section, i.e. the one prepended above. node scripts/extract-release-notes.js \ | gh release create "$TAG" -t "Auspice $VERSION" --notes-file - rebuild-docker-image: needs: [github-release] # docker-base builds from the release branch, which only master releases move. if: ${{ !inputs.dry-run && github.ref_name == 'master' }} runs-on: ubuntu-latest steps: - run: gh workflow run ci.yml --repo nextstrain/docker-base env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_WORKFLOW_DISPATCH }}