From 6d45de90f8f8a1506dffde8a4c85d1c71bb0c559 Mon Sep 17 00:00:00 2001 From: Armando Navarro Date: Fri, 2 Oct 2026 11:49:11 -0700 Subject: [PATCH 1/2] fix(ci): keep the canary dist-tag from moving back to an older build Every canary publish moved the `canary` dist-tag, whatever commit it was built from. Two merges close together could publish out of order, and a re-run of an older run could publish its build last. The publish job now clones the repository's commit history and publishes a canary only when its commit comes after the commit of the canary on npm, or is the same commit under a new version. A build of an earlier commit is skipped with a warning. Commits are compared rather than versions, because a version can be higher for an older commit. Canary publishes also run one at a time, queued, so each check reads what the previous publish left on npm. Release publishes are unchanged. A scheduled run on an unchanged main now skips with a notice instead of failing on the duplicate version. Fixes #3783 --- .github/workflows/test.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f52ae128e..83523db4d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -163,6 +163,11 @@ jobs: name: Publish (NPM) needs: ['build', 'test', 'browser'] if: ${{ github.ref == 'refs/heads/main' || github.event_name == 'release' }} + # One canary publish at a time, so the check below reads the canary the previous one published. + concurrency: + group: ${{ github.event_name == 'release' && github.run_id || 'canary-publish' }} + cancel-in-progress: false + queue: max steps: - name: Setup node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -175,7 +180,36 @@ jobs: with: name: angularfire-${{ github.run_id }} path: dist + - name: Skip a canary that is not newer than npm's + id: canary_check + run: | + VERSION=$(node -p "require('./dist/packages-dist/package.json').version") + if [[ $VERSION == *-canary.* ]]; then + # The dist-tags endpoint is not CDN-cached, unlike the package data `npm view` reads. + DIST_TAGS=$(curl -fsS --retry 3 --max-time 30 https://registry.npmjs.org/-/package/@angular/fire/dist-tags) + NPM_CANARY=$(node -p "JSON.parse(process.argv[1]).canary" "$DIST_TAGS") + # Order by position on main, not by version, which can be higher for an older commit. + git clone --quiet --bare --filter=tree:0 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" history.git + if ! NPM_CANARY_COMMIT=$(git -C history.git rev-parse --verify --quiet "${NPM_CANARY##*[.-]}^{commit}"); then + echo "::error::Could not match the canary on npm, $NPM_CANARY, to a single commit in this repository." + exit 1 + fi + # `npm publish` always moves a dist-tag, so a canary that is not newer must not publish at all. + if [[ $NPM_CANARY_COMMIT == "$GITHUB_SHA" ]]; then + if [[ $VERSION == "$NPM_CANARY" ]]; then + echo "::notice::Not publishing $VERSION, because it is already the canary on npm." + echo "skip=true" >> "$GITHUB_OUTPUT" + fi + elif git -C history.git merge-base --is-ancestor "$GITHUB_SHA" "$NPM_CANARY_COMMIT"; then + echo "::warning::Not publishing $VERSION, because the canary on npm, $NPM_CANARY, is from a later commit on main." + echo "skip=true" >> "$GITHUB_OUTPUT" + elif ! git -C history.git merge-base --is-ancestor "$NPM_CANARY_COMMIT" "$GITHUB_SHA"; then + echo "::error::Not publishing $VERSION, because its commit and the commit of the canary on npm, $NPM_CANARY, are not on the same line of history. One of them is not on main." + exit 1 + fi + fi - name: Publish + if: steps.canary_check.outputs.skip != 'true' run: | cd ./dist/packages-dist chmod +x publish.sh From 89d173a44d276b7289a07b34295c199254fcdc5f Mon Sep 17 00:00:00 2001 From: Armando Navarro Date: Sun, 4 Oct 2026 16:54:35 -0700 Subject: [PATCH 2/2] ci: say how to recover when the canary check fails The two error messages now end with the consequence and the fix: every canary publish fails until the canary dist-tag points at a build of a commit on main, and `npm dist-tag add` is how someone with publish rights points it there. --- .github/workflows/test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83523db4d..666b0d126 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -190,8 +190,9 @@ jobs: NPM_CANARY=$(node -p "JSON.parse(process.argv[1]).canary" "$DIST_TAGS") # Order by position on main, not by version, which can be higher for an older commit. git clone --quiet --bare --filter=tree:0 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" history.git + HOW_TO_FIX="Every canary publish fails until the canary dist-tag points at a build of a commit on main. Publish rights are required to fix it with: npm dist-tag add @angular/fire@ canary" if ! NPM_CANARY_COMMIT=$(git -C history.git rev-parse --verify --quiet "${NPM_CANARY##*[.-]}^{commit}"); then - echo "::error::Could not match the canary on npm, $NPM_CANARY, to a single commit in this repository." + echo "::error::Could not match the canary on npm, $NPM_CANARY, to a single commit in this repository. $HOW_TO_FIX" exit 1 fi # `npm publish` always moves a dist-tag, so a canary that is not newer must not publish at all. @@ -204,7 +205,7 @@ jobs: echo "::warning::Not publishing $VERSION, because the canary on npm, $NPM_CANARY, is from a later commit on main." echo "skip=true" >> "$GITHUB_OUTPUT" elif ! git -C history.git merge-base --is-ancestor "$NPM_CANARY_COMMIT" "$GITHUB_SHA"; then - echo "::error::Not publishing $VERSION, because its commit and the commit of the canary on npm, $NPM_CANARY, are not on the same line of history. One of them is not on main." + echo "::error::Not publishing $VERSION, because its commit and the commit of the canary on npm, $NPM_CANARY, are not on the same line of history. One of them is not on main. $HOW_TO_FIX" exit 1 fi fi