Skip to content

Publish to Public NPM #60

Publish to Public NPM

Publish to Public NPM #60

name: Publish to Public NPM
on:
workflow_dispatch:
inputs:
version:
description: "Version to publish (leave empty to use package.json version)"
required: false
type: string
npm_tag:
description: "NPM dist-tag (auto = detect from version)"
required: false
type: choice
default: "latest"
options:
- "auto"
- "latest"
- "beta"
- "alpha"
- "next"
dry_run:
description: "Dry run (preview without publishing)"
required: false
type: boolean
default: true
skip_slack_notification:
description: "Skip Slack notification"
required: false
type: boolean
default: false
permissions:
contents: write
id-token: write
concurrency:
group: npm-publish
cancel-in-progress: false
jobs:
publish:
name: Build & Publish to Public NPM
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
with:
version: 10.6.5
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "24"
cache: "pnpm"
registry-url: "https://registry.npmjs.org"
- name: Upgrade npm to latest version
run: npm install -g npm@latest
- name: Pre-flight version and registry check
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
VERSION_INPUT: ${{ github.event.inputs.version }}
run: |
CORE_VERSION=$(jq -r '.version' packages/core/package.json)
REACT_VERSION=$(jq -r '.version' packages/react/package.json)
if [ "$CORE_VERSION" != "$REACT_VERSION" ]; then
echo "::error::Version mismatch — core@$CORE_VERSION != react@$REACT_VERSION. Both packages must have the same version."
exit 1
fi
VERSION="${VERSION_INPUT:-$CORE_VERSION}"
if [ "$DRY_RUN" != "true" ]; then
if npm view "@auth0/universal-components-core@$VERSION" version 2>/dev/null; then
echo "::error::@auth0/universal-components-core@$VERSION already exists on npm. Aborting."
exit 1
fi
if npm view "@auth0/universal-components-react@$VERSION" version 2>/dev/null; then
echo "::error::@auth0/universal-components-react@$VERSION already exists on npm. Aborting."
exit 1
fi
echo "Neither package exists at $VERSION — safe to publish."
else
echo "Dry run — skipping npm registry check."
fi
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm run build
- name: Verify version alignment
id: detect
env:
VERSION_INPUT: ${{ github.event.inputs.version }}
run: |
CORE_VERSION=$(node -p "require('./packages/core/package.json').version")
REACT_VERSION=$(node -p "require('./packages/react/package.json').version")
if [ "$CORE_VERSION" != "$REACT_VERSION" ]; then
echo "::error::Version mismatch: core=$CORE_VERSION, react=$REACT_VERSION. Both packages must have the same version."
exit 1
fi
if [ -n "$VERSION_INPUT" ]; then
VERSION="$VERSION_INPUT"
else
VERSION="$CORE_VERSION"
fi
determine_tag() {
local version=$1
if [[ "$version" == *"alpha"* ]]; then
echo "alpha"
elif [[ "$version" == *"beta"* ]]; then
echo "beta"
elif [[ "$version" == *"rc"* ]]; then
echo "next"
else
echo "latest"
fi
}
NPM_TAG=$(determine_tag "$VERSION")
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "npm_tag=${NPM_TAG}" >> $GITHUB_OUTPUT
echo "Publishing version: $VERSION (detected tag: $NPM_TAG)"
- name: Set final version and tag
id: final
env:
NPM_TAG_INPUT: ${{ github.event.inputs.npm_tag }}
VERSION: ${{ steps.detect.outputs.version }}
DETECTED_TAG: ${{ steps.detect.outputs.npm_tag }}
run: |
validate_input() {
local input="$1"
if [[ -z "$input" ]]; then
echo ""
return 0
fi
if [[ "$input" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "$input"
else
echo "Invalid input: $input" >&2
exit 1
fi
}
validate_input "$VERSION" || exit 1
validate_input "$NPM_TAG_INPUT" || exit 1
if [ "$NPM_TAG_INPUT" = "auto" ] || [ -z "$NPM_TAG_INPUT" ]; then
NPM_TAG="$DETECTED_TAG"
else
NPM_TAG="$NPM_TAG_INPUT"
fi
echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo "NPM_TAG=${NPM_TAG}" >> $GITHUB_ENV
echo "Publishing: ${VERSION} @ ${NPM_TAG}"
- name: Update Core package for public npm
working-directory: ./packages/core
run: |
node -e "
const pkg = require('./package.json');
pkg.version = process.env.VERSION;
pkg.publishConfig = {
access: 'public',
registry: 'https://registry.npmjs.org/'
};
require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
"
- name: Update React package for public npm
working-directory: ./packages/react
run: |
node -e "
const pkg = require('./package.json');
pkg.version = process.env.VERSION;
pkg.publishConfig = {
access: 'public',
registry: 'https://registry.npmjs.org/'
};
if (pkg.dependencies && pkg.dependencies['@auth0/universal-components-core']) {
pkg.dependencies['@auth0/universal-components-core'] = process.env.VERSION;
}
require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
"
- name: Publish Core package to public npm
id: publish-core
working-directory: ./packages/core
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
NODE_AUTH_TOKEN: ""
run: |
echo "Publishing @auth0/universal-components-core@$VERSION with tag: $NPM_TAG"
if [ "$DRY_RUN" = "true" ]; then
echo "DRY RUN - Skipping publish"
npm publish --dry-run --tag="$NPM_TAG"
echo "published=dry-run" >> $GITHUB_OUTPUT
else
npm publish --provenance --access public --tag="$NPM_TAG"
echo "published=true" >> $GITHUB_OUTPUT
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Publish React package to public npm
id: publish-react
working-directory: ./packages/react
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
NODE_AUTH_TOKEN: ""
run: |
echo "Publishing @auth0/universal-components-react@$VERSION with tag: $NPM_TAG"
if [ "$DRY_RUN" = "true" ]; then
echo "DRY RUN - Skipping publish"
npm publish --dry-run --tag="$NPM_TAG"
echo "published=dry-run" >> $GITHUB_OUTPUT
else
npm publish --provenance --access public --tag="$NPM_TAG"
echo "published=true" >> $GITHUB_OUTPUT
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Generate Summary
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
run: |
echo "## Public NPM Publication Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$DRY_RUN" = "true" ]; then
echo "### DRY RUN MODE" >> $GITHUB_STEP_SUMMARY
echo "No packages were published. This was a test run." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
echo "### Published Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- \`@auth0/universal-components-core@${VERSION}\`" >> $GITHUB_STEP_SUMMARY
echo " - Tag: \`${NPM_TAG}\`" >> $GITHUB_STEP_SUMMARY
echo " - Registry: https://registry.npmjs.org" >> $GITHUB_STEP_SUMMARY
echo " - Install: \`npm install @auth0/universal-components-core@${NPM_TAG}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- \`@auth0/universal-components-react@${VERSION}\`" >> $GITHUB_STEP_SUMMARY
echo " - Tag: \`${NPM_TAG}\`" >> $GITHUB_STEP_SUMMARY
echo " - Registry: https://registry.npmjs.org" >> $GITHUB_STEP_SUMMARY
echo " - Install: \`npm install @auth0/universal-components-react@${NPM_TAG}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Links" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- [View @auth0/universal-components-core on npm](https://www.npmjs.com/package/@auth0/universal-components-core)" >> $GITHUB_STEP_SUMMARY
echo "- [View @auth0/universal-components-react on npm](https://www.npmjs.com/package/@auth0/universal-components-react)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note:** Your internal JFrog registry continues to use \`@auth0-web-ui-components/*\` packages." >> $GITHUB_STEP_SUMMARY
- name: Create Git tags
if: github.event.inputs.dry_run != 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
for PKG in core react; do
TAG_NAME="${PKG}@${VERSION}"
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
echo "Tag $TAG_NAME exists locally, deleting..."
git tag -d "$TAG_NAME"
fi
if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME"; then
echo "Tag $TAG_NAME exists remotely, deleting..."
git push origin ":refs/tags/$TAG_NAME" || true
fi
git tag -a "$TAG_NAME" -m "Release @auth0/universal-components-${PKG}@${VERSION}"
git push origin "$TAG_NAME"
echo "Created tag: $TAG_NAME"
done
- name: Create GitHub Release for Core
if: github.event.inputs.dry_run != 'true'
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2
with:
tag_name: core@${{ env.VERSION }}
name: "@auth0/universal-components-core@${{ env.VERSION }}"
body: |
Published to NPM
**Package**: `@auth0/universal-components-core`
**Version**: `${{ env.VERSION }}`
**NPM Tag**: `${{ env.NPM_TAG }}`
```bash
npm install @auth0/universal-components-core@${{ env.VERSION }}
```
[View on NPM](https://www.npmjs.com/package/@auth0/universal-components-core/v/${{ env.VERSION }})
prerelease: ${{ env.NPM_TAG != 'latest' }}
draft: false
- name: Create GitHub Release for React
if: github.event.inputs.dry_run != 'true'
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2
with:
tag_name: react@${{ env.VERSION }}
name: "@auth0/universal-components-react@${{ env.VERSION }}"
body: |
Published to NPM
**Package**: `@auth0/universal-components-react`
**Version**: `${{ env.VERSION }}`
**NPM Tag**: `${{ env.NPM_TAG }}`
**Requires**: `@auth0/universal-components-core@${{ env.VERSION }}`
```bash
npm install @auth0/universal-components-react@${{ env.VERSION }}
```
[View on NPM](https://www.npmjs.com/package/@auth0/universal-components-react/v/${{ env.VERSION }})
prerelease: ${{ env.NPM_TAG != 'latest' }}
draft: false
- name: Restore package.json files
if: always()
run: |
git checkout packages/core/package.json packages/react/package.json || true
- name: Send Slack notification
if: |
always() &&
!cancelled() &&
github.event.inputs.dry_run != 'true' &&
github.event.inputs.skip_slack_notification != 'true'
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
SLACK_WORKFLOW_WEBHOOK: ${{ secrets.SLACK_WORKFLOW_WEBHOOK }}
run: |
echo "Preparing Slack notification..."
CORE_PUBLISHED="${{ steps.publish-core.outputs.published }}"
REACT_PUBLISHED="${{ steps.publish-react.outputs.published }}"
if [ "$CORE_PUBLISHED" = "true" ] && [ "$REACT_PUBLISHED" = "true" ]; then
STATUS="SUCCESS: Packages published to public NPM"
MESSAGE="Published: @auth0/universal-components-core@${VERSION}, @auth0/universal-components-react@${VERSION}"
elif [ "$CORE_PUBLISHED" = "dry-run" ] || [ "$REACT_PUBLISHED" = "dry-run" ]; then
STATUS="INFO: Dry run completed"
MESSAGE="Dry run: @auth0/universal-components-core@${VERSION}, @auth0/universal-components-react@${VERSION}"
else
STATUS="FAILED: Package publishing failed"
MESSAGE="One or more packages failed to publish. Check workflow logs."
fi
echo "=== WORKFLOW SUMMARY ==="
echo "Status: $STATUS"
echo "Message: $MESSAGE"
echo ""
if [ -n "$SLACK_WORKFLOW_WEBHOOK" ]; then
echo "Sending Slack notification..."
HTTP_CODE=$(curl -X POST "$SLACK_WORKFLOW_WEBHOOK" \
-H "Content-Type: application/json" \
-w "%{http_code}" \
-o /tmp/slack_response.txt \
-d "{
\"status\": \"$STATUS\",
\"message\": \"$MESSAGE\",
\"package_details\": \"@auth0/universal-components-core@${VERSION}, @auth0/universal-components-react@${VERSION}\",
\"core_version\": \"$VERSION\",
\"react_version\": \"$VERSION\",
\"registry\": \"https://registry.npmjs.org\",
\"trigger\": \"${{ github.event_name }}\",
\"actor\": \"${{ github.actor }}\",
\"action_url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"
}")
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
echo "Slack notification sent successfully (HTTP $HTTP_CODE)"
else
echo "WARNING: Slack notification failed (HTTP $HTTP_CODE)"
cat /tmp/slack_response.txt
fi
else
echo "Slack webhook not configured, skipping notification"
fi
- name: Handle workflow failure
if: failure()
run: |
echo "## Workflow Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The workflow encountered an error. Please check the logs above for details." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Common Issues" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Authentication**: Verify NPM_TOKEN secret is configured correctly" >> $GITHUB_STEP_SUMMARY
echo "- **Version conflict**: Package version may already exist on npm" >> $GITHUB_STEP_SUMMARY
echo "- **Version mismatch**: Core and React package.json versions must be identical" >> $GITHUB_STEP_SUMMARY
echo "- **Build failure**: Check if packages build successfully" >> $GITHUB_STEP_SUMMARY
echo "- **Network issues**: NPM registry may be unreachable" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "1. Review the error logs in this workflow run" >> $GITHUB_STEP_SUMMARY
echo "2. Fix the issue and retry the workflow" >> $GITHUB_STEP_SUMMARY
echo "3. If needed, manually publish using: \`pnpm publish\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY