Skip to content

chore(ci): publish only when there is a release - #249

Merged
allanbowe merged 1 commit into
mainfrom
fix/publish-guard
Oct 2, 2026
Merged

allanbowe merged 1 commit into
mainfrom
fix/publish-guard

Conversation

@sasjs-dev

@sasjs-dev sasjs-dev Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Publishes only when semantic-release actually cut a release, the same guard as sasjs/adapter#902.

The failure this fixes. The publish step ran on if: success() regardless of whether semantic-release released anything:

       - name: Publish to npm with trusted publisher
+        id: publish
         if: success()
-        run: npm publish --access public
+        run: |
+          local_version=$(node -p "require('./package.json').version")
+          published_version=$(npm view @sasjs/lint version 2>/dev/null || echo "")
+
+          if [ "$local_version" = "$published_version" ]; then
+            echo "released=false" >> "$GITHUB_OUTPUT"
+            echo "Nothing to publish - $local_version is already on npm"
+            exit 0
+          fi
+
+          npm publish --access public
+          echo "released=true" >> "$GITHUB_OUTPUT"

The angular convention does not treat chore: or docs: as releasable, so a merge with no releasable commit left package.json at the version already on npm, and npm publish was rejected with "You cannot publish over the previously published versions". That is the red run earlier today on a docs(lint): merge. It is a no-op now instead.

Verified both branches, with a stubbed npm on PATH so the publish path runs without publishing anything:

local == published    exit=0  "Nothing to publish - 4.1.1 is already on npm"
                     GITHUB_OUTPUT: released=false
                     npm publish called: no

local != published   exit=0  GITHUB_OUTPUT: released=true
                     npm publish called: publish --access public

The workflow still parses (prettier exit 0).

Typed chore(ci) with [skip release] deliberately: this is CI-only, no consumer sees a difference, and a patch release would be version churn.

semantic-release only releases when it finds a releasable commit, and the angular
convention this repo uses does not treat `chore:` or `docs:` as one. The publish
step ran on `if: success()` regardless, so a merge with no releasable commit
published the version already in package.json, which npm rejects with "You cannot
publish over the previously published versions". That is the failure in the
earlier run on a `docs(lint):` merge.

The step now compares the local version against the registry and exits early when
they match, so a no-release merge is a clean no-op instead of a red pipeline.
`released` is exported on GITHUB_OUTPUT for any step that wants to act only on a
real release.

Same guard as sasjs/adapter#902.

Verified both branches with a stubbed npm on PATH, so the publish path is
exercised without publishing anything: with the versions equal the step prints
"Nothing to publish - 4.1.1 is already on npm", sets released=false and never
calls npm publish; with a new version it calls `npm publish --access public` and
sets released=true. The workflow still parses.

Typed `chore(ci)` with `[skip release]` because this is CI-only and no consumer
sees a difference - a patch release here would be version churn.
@allanbowe
allanbowe merged commit 864a83e into main Oct 2, 2026
2 checks passed
@allanbowe
allanbowe deleted the fix/publish-guard branch October 2, 2026 14:41
@sasjs-gha

sasjs-gha Bot commented Oct 4, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 4.1.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@sasjs-gha sasjs-gha Bot added the released label Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant