Skip to content

Publish to NPM

Publish to NPM #149

Workflow file for this run

name: Publish to NPM
# This workflow publishes packages to npm. We use a single workflow file because
# npm Trusted Publishing currently supports only a single workflow file.
#
# Stable release flow (manual):
# 1. Locally: `bun run release <package>` — bumps version, writes CHANGELOG, commits, tags.
# 2. `git push --follow-tags origin main`
# 3. GitHub → Actions → "Publish to NPM" → Run workflow → publish_type: release, package: <package>
# The workflow reads the version from the package's package.json, builds, and publishes.
# A pre-release version (containing a hyphen, e.g. 1.1.0-rc.0) is published to the `next`
# dist-tag; a stable version is published to `latest`.
#
# Nightly pre-release flow (automatic):
# Runs at 03:00 UTC. If there were commits in the last 25h, every package is bumped to a
# timestamped pre-release version and published to the `dev` dist-tag.
on:
schedule:
- cron: '0 3 * * *' # Nightly at 03:00 UTC
workflow_dispatch:
inputs:
publish_type:
description: 'Type of publish'
required: true
type: choice
options:
- release
- nightly
default: release
package:
description: 'Package to publish (release requires a single package; nightly accepts "all")'
required: false
type: choice
options:
- all
- braintree-plugin
- elasticsearch-plugin
- mollie-plugin
- punchout-gateway-plugin
- sentry-plugin
- stellate-plugin
- stripe-plugin
- pub-sub-plugin
default: all
dry-run:
description: 'Dry run (no actual publish)'
required: false
type: boolean
default: false
jobs:
setup:
runs-on: ubuntu-latest
outputs:
publish_type: ${{ steps.determine.outputs.publish_type }}
steps:
- name: Determine publish type
id: determine
run: |
if [ "${{ github.event_name }}" == "schedule" ]; then
echo "publish_type=nightly" >> $GITHUB_OUTPUT
else
echo "publish_type=${{ inputs.publish_type }}" >> $GITHUB_OUTPUT
fi
- name: Validate release inputs
if: github.event_name == 'workflow_dispatch' && inputs.publish_type == 'release'
run: |
if [ -z "${{ inputs.package }}" ] || [ "${{ inputs.package }}" == "all" ]; then
echo "::error::A stable release requires a single package — 'all' is not allowed. Select one package."
exit 1
fi
publish:
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# For nightly builds: check if there were commits in the last 25 hours
# (25h instead of 24h to account for cron schedule jitter)
- name: Check for new commits
id: commit_check
if: needs.setup.outputs.publish_type == 'nightly'
run: |
COMMITS=$(git rev-list --count --since="25 hours" HEAD)
echo "Commits found in last 25h: $COMMITS"
if [ "$COMMITS" -eq 0 ]; then
echo "should_publish=false" >> $GITHUB_OUTPUT
else
echo "should_publish=true" >> $GITHUB_OUTPUT
fi
- name: Skip publish (no new commits)
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'false'
run: echo "No new commits in last 25 hours – skipping nightly publish."
# Gate the remaining steps: run for a release, or for a nightly that has new commits.
- name: Should run
id: should_run
run: |
if [ "${{ needs.setup.outputs.publish_type }}" == "release" ] || \
[ "${{ steps.commit_check.outputs.should_publish }}" == "true" ]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "run=false" >> $GITHUB_OUTPUT
fi
- name: Setup Node.js
if: steps.should_run.outputs.run == 'true'
uses: actions/setup-node@v4
with:
node-version: '24.x'
registry-url: 'https://registry.npmjs.org'
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.10
- name: Update npm
if: steps.should_run.outputs.run == 'true'
run: npm install -g npm@11
- name: Install dependencies
if: steps.should_run.outputs.run == 'true'
run: bun install --frozen-lockfile
- name: Build
if: steps.should_run.outputs.run == 'true'
run: bun run build
# ── Stable release ───────────────────────────────────────────────────────────
# The version is read from package.json (already bumped & tagged by `bun run release`
# and pushed to main). Nothing is committed back; npm provenance attests the published
# version against the pushed release commit.
- name: Resolve release version
id: release
if: needs.setup.outputs.publish_type == 'release'
run: |
PKG="${{ inputs.package }}"
VERSION=$(node -p "require('./packages/$PKG/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [[ "$VERSION" =~ - ]]; then
echo "dist_tag=next" >> $GITHUB_OUTPUT
echo "Publishing @vendure-community/$PKG@$VERSION (pre-release → next)"
else
echo "dist_tag=latest" >> $GITHUB_OUTPUT
echo "Publishing @vendure-community/$PKG@$VERSION (stable → latest)"
fi
- name: Publish to NPM (release)
if: needs.setup.outputs.publish_type == 'release'
run: |
cd packages/${{ inputs.package }}
ARGS="--access public --provenance --tag ${{ steps.release.outputs.dist_tag }}"
if [ "${{ inputs.dry-run }}" == "true" ]; then
ARGS="$ARGS --dry-run"
fi
npm publish $ARGS
# ── Nightly pre-release ──────────────────────────────────────────────────────
- name: Get current date
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'true'
id: date
run: echo "date=$(date +'%Y%m%d%H%M')" >> $GITHUB_OUTPUT
- name: Configure Git
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'true'
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Bump pre-release versions
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'true'
run: |
if [ "${{ inputs.package }}" != "" ] && [ "${{ inputs.package }}" != "all" ]; then
SCOPE="--scope @vendure-community/${{ inputs.package }}"
else
SCOPE="--no-private"
fi
npx lerna exec $SCOPE -- \
'NEW=$(node -p "const v=require(\"./package.json\").version.replace(/-.*/,\"\").split(\".\");v[2]++;v.join(\".\")") && \
npm version "${NEW}-dev.${{ steps.date.outputs.date }}" --no-git-tag-version'
# The version bump is committed (but never pushed) so that npm provenance
# attestation links to a commit whose package.json matches the published version.
- name: Commit version changes (nightly)
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'true'
run: |
git add packages/*/package.json
git commit -m "chore: Bump version for nightly pre-release"
- name: Publish to NPM (nightly)
if: needs.setup.outputs.publish_type == 'nightly' && steps.commit_check.outputs.should_publish == 'true'
run: |
if [ "${{ inputs.package }}" != "" ] && [ "${{ inputs.package }}" != "all" ]; then
SCOPE="--scope @vendure-community/${{ inputs.package }}"
else
SCOPE="--no-private"
fi
ARGS="--access public --provenance --tag dev"
if [ "${{ inputs.dry-run }}" = "true" ]; then
ARGS="$ARGS --dry-run"
fi
npx lerna exec $SCOPE -- npm publish $ARGS