Repository navigation
Merge pull request #1946 from AxiomCodeAI/dev #30
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). | |
| # | |
| # A push to main whose version has no tag yet is a release. CI on that push builds | |
| # the engines on all five platforms and runs the five-platform e2e; only when that | |
| # run is GREEN does this workflow tag the commit it tested v<version> and open 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 runs publish-npm.yml, which ships the binaries and | |
| # the tarball of that same green CI run: nothing is compiled twice (#1350). | |
| # | |
| # A version that is already tagged finds its tag present and does nothing. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| name: release | |
| on: | |
| push: | |
| branches: [main] # sync-dev | |
| workflow_run: # tag: after CI on main finished | |
| workflows: [CI] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: release-main | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| tag: | |
| # the CI run of a PUSH to main, and only a green one: a red release build drafts nothing | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'workflow_run' && github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.conclusion == 'success') | |
| runs-on: ubuntu-24.04 | |
| env: | |
| SHA: ${{ github.event.workflow_run.head_sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.SHA }} | |
| fetch-depth: 0 | |
| - name: the bot this job writes as | |
| id: bot | |
| uses: ./.github/actions/bot | |
| with: | |
| app-id: ${{ vars.AXIOMCODE_BOT_APP_ID }} | |
| private-key: ${{ secrets.AXIOMCODE_BOT_PRIVATE_KEY }} | |
| - 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: ${{ steps.bot.outputs.token }} | |
| BOT_NAME: ${{ steps.bot.outputs.name }} | |
| BOT_EMAIL: ${{ steps.bot.outputs.email }} | |
| 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 | |
| # the bot's token starts no workflow here either: nothing runs on a tag push, and | |
| # publishing waits for a person to publish the draft. | |
| git config user.name "$BOT_NAME" | |
| git config user.email "$BOT_EMAIL" | |
| git tag -a "$tag" -m "$tag" "$SHA" | |
| git -c http.https://github.com/.extraheader= push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "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 ───────────────────────────────────────────────── | |
| # A promotion from dev lands on main as a merge commit that dev does not have | |
| # yet, and a hotfix straight to main is a commit dev lacks too; merging main | |
| # back into dev records that they are 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: | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: dev | |
| fetch-depth: 0 | |
| - name: the bot this job writes as | |
| id: bot | |
| uses: ./.github/actions/bot | |
| with: | |
| app-id: ${{ vars.AXIOMCODE_BOT_APP_ID }} | |
| private-key: ${{ secrets.AXIOMCODE_BOT_PRIVATE_KEY }} | |
| - name: merge main into dev | |
| id: merge | |
| env: | |
| BOT_TOKEN: ${{ steps.bot.outputs.token }} | |
| BOT_NAME: ${{ steps.bot.outputs.name }} | |
| BOT_EMAIL: ${{ steps.bot.outputs.email }} | |
| run: | | |
| set -uo pipefail | |
| git config user.name "$BOT_NAME" | |
| git config user.email "$BOT_EMAIL" | |
| 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 -c http.https://github.com/.extraheader= push "https://x-access-token:${BOT_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" 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: ${{ steps.bot.outputs.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 |