Repository navigation
ci + release: gate main on all five suites; one version, tag and draf… #1
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
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Every version that lands on main gets a tag and a draft release, and dev is | |
| # brought up to date with main (the sync-dev job at the end). | |
| # | |
| # The version gate (ci.yml) makes a pull request that reaches users bump the | |
| # version. When that bump merges, this workflow tags the merge commit v<version> | |
| # and opens a DRAFT GitHub release with notes generated from the pull requests | |
| # since the previous tag. Nothing is published here. | |
| # | |
| # Publishing is a person's decision: review the draft, then press Publish. That | |
| # `release: published` event is what runs publish-npm.yml, which builds the | |
| # engines, checks that the tag and every manifest agree, publishes to npm and | |
| # attaches the tarballs to the release. | |
| # | |
| # A push that does not move the version finds its tag already present and does | |
| # nothing, so this runs on every push to main without a path filter. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| name: release | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: release-main | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| tag: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: every manifest carries the same version | |
| run: node .github/scripts/version.mjs check | |
| - name: tag and draft the release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| version="$(node .github/scripts/version.mjs get)" | |
| tag="v$version" | |
| if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null; then | |
| echo "$tag already exists — this push did not change the version" | |
| exit 0 | |
| fi | |
| prerelease="" | |
| case "$version" in *-*) prerelease="--prerelease";; esac | |
| # The previous tag bounds the generated notes; the first release has none. | |
| prev="$(git tag --list 'v*' --sort=-v:refname | head -1)" | |
| start=""; [ -n "$prev" ] && start="--notes-start-tag $prev" | |
| # The tag is pushed here rather than left to the release: a DRAFT release | |
| # does not create its tag until it is published, so the check above would | |
| # never see it and every later push would draft again. A tag pushed with | |
| # GITHUB_TOKEN starts no workflow, which is intended: publishing waits for | |
| # a person to publish the draft. | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$tag" -m "$tag" "$GITHUB_SHA" | |
| git push origin "refs/tags/$tag" | |
| gh release create "$tag" --draft --verify-tag --title "$tag" \ | |
| --generate-notes $start $prerelease | |
| echo "::notice::drafted $tag — review it under Releases and publish it to ship to npm" | |
| # ── keep dev on top of main ───────────────────────────────────────────────── | |
| # main only takes squash merges, so a promotion from dev lands on main as a NEW | |
| # commit that dev does not have; without this, the next dev→main pull request | |
| # shows the old work again. Merging main back into dev records that it is | |
| # already there. A promotion merges cleanly (same content both sides); a hotfix | |
| # that went straight to main and touches lines dev has since changed does not, | |
| # and then nothing is pushed: an issue says how to resolve it by hand. | |
| sync-dev: | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: dev | |
| fetch-depth: 0 | |
| - name: merge main into dev | |
| id: merge | |
| run: | | |
| set -uo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git fetch origin main | |
| if git merge-base --is-ancestor origin/main HEAD; then | |
| echo "dev already contains main"; exit 0 | |
| fi | |
| if git merge --no-edit -m "Merge main into dev (${GITHUB_SHA::8})" origin/main; then | |
| git push origin HEAD:dev | |
| echo "dev now contains main at ${GITHUB_SHA::8}" | |
| else | |
| git diff --name-only --diff-filter=U > /tmp/conflicts.txt | |
| git merge --abort | |
| echo "conflict=1" >> "$GITHUB_OUTPUT" | |
| echo "::error::main does not merge cleanly into dev" | |
| exit 1 | |
| fi | |
| # One open issue at most: a later conflicting push comments on it. | |
| - name: say how to resolve it | |
| if: failure() && steps.merge.outputs.conflict == '1' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -uo pipefail | |
| title="main does not merge cleanly into dev" | |
| { | |
| echo "Merging main (${GITHUB_SHA::8}) into dev conflicts in:" | |
| echo; echo '```'; cat /tmp/conflicts.txt; echo '```'; echo | |
| echo "Resolve it locally (dev takes direct pushes):" | |
| echo; echo '```' | |
| echo "git fetch origin && git checkout dev && git pull" | |
| echo "git merge origin/main" | |
| echo "# fix the files above, then" | |
| echo "git add -A && git commit && git push origin dev" | |
| echo '```'; echo | |
| echo "Close this once dev is pushed; the next push to main retries on its own." | |
| echo; echo "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" | |
| } > /tmp/body.md | |
| existing="$(gh issue list --state open --search "\"$title\" in:title" --json number --jq '.[0].number' || true)" | |
| if [ -n "$existing" ]; then | |
| gh issue comment "$existing" --body-file /tmp/body.md | |
| else | |
| gh issue create --title "$title" --body-file /tmp/body.md | |
| fi |