From 6c0e10df0da593371c0f4e50ae27627762e5d04f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 20 Oct 2025 20:56:05 -0400 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A4=96=20Fix=20npm=20publish=20workfl?= =?UTF-8?q?ow=20to=20skip=20existing=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow was failing because it tried to publish the same version (0.3.0) on every push to main. npm doesn't allow republishing the same version, which caused 404 errors with confusing messages. Changes: - Add version check before publishing - Skip publish if version already exists on npm - Add helpful message explaining how to publish new versions - Add NODE_AUTH_TOKEN env var for npm publish This allows the workflow to run successfully on main without failing when the version hasn't been bumped. --- .github/workflows/publish-npm.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 7c2fab795..ff3ba032b 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -35,8 +35,25 @@ jobs: - name: Build application run: make build + - name: Check if version already published + id: check-version + run: | + PACKAGE_VERSION=$(node -p "require('./package.json').version") + PACKAGE_NAME=$(node -p "require('./package.json').name") + + echo "Checking if ${PACKAGE_NAME}@${PACKAGE_VERSION} exists on npm..." + + if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version &>/dev/null; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Version ${PACKAGE_VERSION} already exists on npm" + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Version ${PACKAGE_VERSION} is new and can be published" + fi + - name: Determine NPM tag id: npm-tag + if: steps.check-version.outputs.exists == 'false' run: | if [[ $GITHUB_REF == refs/tags/* ]]; then echo "tag=latest" >> $GITHUB_OUTPUT @@ -47,4 +64,13 @@ jobs: fi - name: Publish to NPM + if: steps.check-version.outputs.exists == 'false' run: npm publish --tag ${{ steps.npm-tag.outputs.tag }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Skip publishing (version exists) + if: steps.check-version.outputs.exists == 'true' + run: | + echo "⏭️ Skipping publish - version already exists on npm" + echo "To publish a new version, update the version in package.json" From 32845b6cf76e388d4605d4350bf0f95446a2f2df Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 20 Oct 2025 22:12:56 -0400 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A4=96=20Use=20git=20describe=20to=20?= =?UTF-8?q?generate=20unique=20pre-release=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of checking if a version exists, generate unique versions for each commit using git describe. This allows every push to main to be publishable to npm. Version format: - Tags: Use package.json version as-is (e.g., 0.3.0) -> 'latest' tag - Main: Generate pre-release (e.g., 0.3.0-next.14.g6c0e10d) -> 'next' tag The pre-release format includes: - Base version from package.json - 'next' identifier for pre-releases - Number of commits since last tag - Short git commit hash This ensures every commit gets a unique publishable version. --- .github/workflows/publish-npm.yml | 67 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index ff3ba032b..b918784f7 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -29,48 +29,45 @@ jobs: with: registry-url: 'https://registry.npmjs.org' - - name: Generate version file - run: ./scripts/generate-version.sh - - - name: Build application - run: make build - - - name: Check if version already published - id: check-version + - name: Generate unique version from git + id: version run: | - PACKAGE_VERSION=$(node -p "require('./package.json').version") - PACKAGE_NAME=$(node -p "require('./package.json').name") + # Get base version from package.json + BASE_VERSION=$(node -p "require('./package.json').version") - echo "Checking if ${PACKAGE_NAME}@${PACKAGE_VERSION} exists on npm..." + # Generate git describe version + GIT_DESCRIBE=$(git describe --tags --always --dirty 2>/dev/null || echo "unknown") - if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version &>/dev/null; then - echo "exists=true" >> $GITHUB_OUTPUT - echo "Version ${PACKAGE_VERSION} already exists on npm" - else - echo "exists=false" >> $GITHUB_OUTPUT - echo "Version ${PACKAGE_VERSION} is new and can be published" - fi - - - name: Determine NPM tag - id: npm-tag - if: steps.check-version.outputs.exists == 'false' - run: | if [[ $GITHUB_REF == refs/tags/* ]]; then - echo "tag=latest" >> $GITHUB_OUTPUT - echo "Publishing as 'latest' tag (stable release)" + # For tags, use the base version as-is (stable release) + NPM_VERSION="${BASE_VERSION}" + NPM_TAG="latest" + echo "Publishing stable release: ${NPM_VERSION}" else - echo "tag=next" >> $GITHUB_OUTPUT - echo "Publishing as 'next' tag (pre-release from main)" + # For main branch, create a pre-release version using git describe + # Format: 0.3.0-next.5.g1a2b3c4 (base-next.commits.hash) + GIT_COMMIT=$(git rev-parse --short HEAD) + COMMITS_SINCE_TAG=$(git rev-list --count HEAD ^$(git describe --tags --abbrev=0 2>/dev/null || echo HEAD) 2>/dev/null || echo "0") + NPM_VERSION="${BASE_VERSION}-next.${COMMITS_SINCE_TAG}.g${GIT_COMMIT}" + NPM_TAG="next" + echo "Publishing pre-release: ${NPM_VERSION}" fi + + echo "version=${NPM_VERSION}" >> $GITHUB_OUTPUT + echo "tag=${NPM_TAG}" >> $GITHUB_OUTPUT + + # Update package.json with the new version + node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json')); pkg.version = '${NPM_VERSION}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');" + + echo "Updated package.json to version ${NPM_VERSION}" + + - name: Generate version file + run: ./scripts/generate-version.sh + + - name: Build application + run: make build - name: Publish to NPM - if: steps.check-version.outputs.exists == 'false' - run: npm publish --tag ${{ steps.npm-tag.outputs.tag }} + run: npm publish --tag ${{ steps.version.outputs.tag }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Skip publishing (version exists) - if: steps.check-version.outputs.exists == 'true' - run: | - echo "⏭️ Skipping publish - version already exists on npm" - echo "To publish a new version, update the version in package.json" From 27ffe2e03582bb2edb8e85f2d674ce6a1e6df9f1 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 20 Oct 2025 22:15:47 -0400 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A4=96=20Handle=20version=20promotion?= =?UTF-8?q?=20for=20tagged=20releases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a tagged release is pushed but the version already exists on npm (e.g., from a pre-release or previous publish), update the dist-tag instead of failing. Behavior: - New version -> Publish normally - Existing version + tag push -> Update dist-tag to 'latest' - Existing version + main push -> Skip (already published) This allows promoting a version to 'latest' even if the tarball was already uploaded under 'next'. --- .github/workflows/publish-npm.yml | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index b918784f7..0c8206161 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -67,7 +67,41 @@ jobs: - name: Build application run: make build + - name: Check if version exists + id: check-exists + run: | + PACKAGE_NAME=$(node -p "require('./package.json').name") + VERSION="${{ steps.version.outputs.version }}" + + if npm view "${PACKAGE_NAME}@${VERSION}" version &>/dev/null; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Version ${VERSION} already exists on npm" + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Version ${VERSION} does not exist, will publish" + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish to NPM + if: steps.check-exists.outputs.exists == 'false' run: npm publish --tag ${{ steps.version.outputs.tag }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Update dist-tag (version already exists) + if: steps.check-exists.outputs.exists == 'true' && github.ref_type == 'tag' + run: | + PACKAGE_NAME=$(node -p "require('./package.json').name") + VERSION="${{ steps.version.outputs.version }}" + TAG="${{ steps.version.outputs.tag }}" + + echo "Version ${VERSION} already published, updating dist-tag to ${TAG}" + npm dist-tag add "${PACKAGE_NAME}@${VERSION}" "${TAG}" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Skip (pre-release already exists) + if: steps.check-exists.outputs.exists == 'true' && github.ref_type != 'tag' + run: | + echo "⏭️ Pre-release version already exists, skipping"