Publish to Public NPM #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |