diff --git a/.github/workflows/publish-on-release.yml b/.github/workflows/publish-on-release.yml deleted file mode 100644 index f31e945..0000000 --- a/.github/workflows/publish-on-release.yml +++ /dev/null @@ -1,203 +0,0 @@ -name: Publish (GitHub Release) - -on: - release: - types: [released, prereleased] - workflow_dispatch: - inputs: - version: - type: choice - description: Release Version - required: true - default: next - options: - - latest - - next - branch: - description: Release Branch (confirm release branch) - required: true - default: main - -permissions: - contents: read - id-token: write - -concurrency: - group: publish-${{ github.workflow }}-${{ github.ref_name || github.run_id }} - cancel-in-progress: false - -jobs: - publish: - runs-on: ubuntu-latest - environment: Publish - steps: - - name: Checkout source - uses: actions/checkout@v5 - with: - fetch-depth: 25 - ref: ${{ github.event.release.tag_name || github.event.inputs.branch }} - - - name: Cache tool downloads - uses: actions/cache@v4 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.28.2 - - - name: Setup Node - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: pnpm - registry-url: https://registry.npmjs.org/ - - - run: corepack enable - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Generate preview version - if: github.event_name == 'workflow_dispatch' && github.event.inputs.version == 'next' - run: | - SAFE_BRANCH=$(echo "${{ github.event.inputs.branch }}" | sed 's/[^a-zA-Z0-9-]/-/g' | tr '[:upper:]' '[:lower:]') - npx changeset version --snapshot "$SAFE_BRANCH" - - - name: Verify tag matches package version - if: github.event_name == 'release' - run: | - TAG="${{ github.event.release.tag_name }}" - if [ "${TAG#v}" != "$TAG" ]; then - echo "Invalid tag format; refusing to publish." - echo "Expected: 1.2.3 (no leading 'v')" - echo "Got: $TAG" - exit 1 - fi - - VERSION_FROM_TAG="$TAG" - PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" - - if [ "$PKG_VERSION" != "$VERSION_FROM_TAG" ]; then - echo "Version mismatch; refusing to publish." - echo "Git tag: $TAG" - echo "packages/astro/package.json version: $PKG_VERSION" - exit 1 - fi - - - name: Patch prerelease version with next tag - if: github.event_name == 'release' && github.event.action == 'prereleased' - shell: bash - run: | - PACKAGE_NAME="@module-federation/astro" - CURRENT_VERSION="$(node -p "require('./packages/astro/package.json').version")" - BASE_VERSION="${CURRENT_VERSION%%-*}" - CURRENT_NEXT="$(npm view "$PACKAGE_NAME" "dist-tags.next" --json 2>/dev/null || true)" - CURRENT_NEXT="${CURRENT_NEXT#\"}" - CURRENT_NEXT="${CURRENT_NEXT%\"}" - if [ "$CURRENT_NEXT" = "null" ]; then - CURRENT_NEXT="" - fi - - PREFIX_REGEX="^${BASE_VERSION//./\\.}-next\.([0-9]+)$" - if [[ "$CURRENT_NEXT" =~ $PREFIX_REGEX ]]; then - NEXT_NUMBER=$((BASH_REMATCH[1] + 1)) - else - NEXT_NUMBER=1 - fi - - NEXT_VERSION="${BASE_VERSION}-next.${NEXT_NUMBER}" - npm pkg set version="$NEXT_VERSION" --prefix packages/astro - echo "Set prerelease version: $NEXT_VERSION" - - - name: Build package - run: pnpm build:package - - - name: Compute npm dist-tag - id: dist - shell: bash - run: | - DIST_TAG="latest" - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - if [ "${{ github.event.inputs.version }}" = "next" ]; then - DIST_TAG="next" - fi - else - if [ "${{ github.event.action }}" = "prereleased" ]; then - DIST_TAG="next" - fi - fi - - echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT" - - - name: Resolve publish action - id: publish_action - shell: bash - run: | - PACKAGE_NAME="@module-federation/astro" - PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" - DIST_TAG="${{ steps.dist.outputs.dist_tag }}" - ACTION="publish" - REASON="new version not yet on npm" - - EXISTING_VERSION="$(npm view "$PACKAGE_NAME@$PKG_VERSION" version --json 2>/dev/null || true)" - EXISTING_VERSION="${EXISTING_VERSION#\"}" - EXISTING_VERSION="${EXISTING_VERSION%\"}" - - if [ "$EXISTING_VERSION" = "$PKG_VERSION" ]; then - CURRENT_TAG_VERSION="$(npm view "$PACKAGE_NAME" "dist-tags.$DIST_TAG" --json 2>/dev/null || true)" - CURRENT_TAG_VERSION="${CURRENT_TAG_VERSION#\"}" - CURRENT_TAG_VERSION="${CURRENT_TAG_VERSION%\"}" - if [ "$CURRENT_TAG_VERSION" = "null" ]; then - CURRENT_TAG_VERSION="" - fi - - if [ "$CURRENT_TAG_VERSION" = "$PKG_VERSION" ]; then - ACTION="skip" - REASON="version already published and dist-tag already points to it" - else - echo "Refusing to republish existing npm version:" - echo "Package: $PACKAGE_NAME" - echo "Version: $PKG_VERSION" - echo "Target dist-tag: $DIST_TAG" - echo "Current '$DIST_TAG' dist-tag points to: ${CURRENT_TAG_VERSION:-}" - echo "" - echo "Trusted Publishers do not support dist-tag promotion without publishing." - echo "Use a new semver version for this release flow." - exit 1 - fi - fi - - echo "action=$ACTION" >> "$GITHUB_OUTPUT" - echo "reason=$REASON" >> "$GITHUB_OUTPUT" - - - name: Publish to npm - if: steps.publish_action.outputs.action == 'publish' - working-directory: packages/astro - run: npm publish --provenance --tag "${{ steps.dist.outputs.dist_tag }}" --access public - - - name: Skip npm publish - if: steps.publish_action.outputs.action == 'skip' - run: | - echo "Skipping npm publish:" - echo "${{ steps.publish_action.outputs.reason }}" - - - name: Post summary - if: always() - continue-on-error: true - run: | - PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" - SOURCE_REF="${{ github.event.release.tag_name || github.event.inputs.branch }}" - echo "## npm publish" > "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Package: \`@module-federation/astro\`" >> "$GITHUB_STEP_SUMMARY" - echo "Version: \`$PKG_VERSION\`" >> "$GITHUB_STEP_SUMMARY" - echo "Dist-tag: \`${{ steps.dist.outputs.dist_tag }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "Action: \`${{ steps.publish_action.outputs.action || 'unknown' }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "Reason: \`${{ steps.publish_action.outputs.reason || 'n/a' }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "Trigger: \`${{ github.event_name }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "Source ref: \`$SOURCE_REF\`" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b0c3c4..a6fb992 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,49 +1,203 @@ name: Release on: + release: + types: [released, prereleased] workflow_dispatch: + inputs: + version: + type: choice + description: Release Version + required: true + default: next + options: + - latest + - next + branch: + description: Release Branch (confirm release branch) + required: true + default: main + +permissions: + contents: read + id-token: write + +concurrency: + group: publish-${{ github.workflow }}-${{ github.ref_name || github.run_id }} + cancel-in-progress: false jobs: - publish-latest: - name: Publish package (npm) + publish: runs-on: ubuntu-latest - permissions: - id-token: write # OIDC trusted publishing + environment: Publish steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Checkout source + uses: actions/checkout@v5 + with: + fetch-depth: 25 + ref: ${{ github.event.release.tag_name || github.event.inputs.branch }} + + - name: Cache tool downloads + uses: actions/cache@v4 with: - fetch-depth: 0 # changeset needs full git history + path: ~/.cache + key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-toolcache- - name: Setup pnpm uses: pnpm/action-setup@v4 + with: + version: 10.28.2 - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v6 with: - node-version: 20 - cache: 'pnpm' + node-version: 24 + cache: pnpm + registry-url: https://registry.npmjs.org/ - # Update npm to the latest version to enable OIDC - - name: Update npm - run: | - npm install -g npm@latest - npm --version + - run: corepack enable - name: Install dependencies - run: pnpm i + run: pnpm install --frozen-lockfile + + - name: Generate preview version + if: github.event_name == 'workflow_dispatch' && github.event.inputs.version == 'next' + run: | + SAFE_BRANCH=$(echo "${{ github.event.inputs.branch }}" | sed 's/[^a-zA-Z0-9-]/-/g' | tr '[:upper:]' '[:lower:]') + npx changeset version --snapshot "$SAFE_BRANCH" + + - name: Verify tag matches package version + if: github.event_name == 'release' + run: | + TAG="${{ github.event.release.tag_name }}" + if [ "${TAG#v}" != "$TAG" ]; then + echo "Invalid tag format; refusing to publish." + echo "Expected: 1.2.3 (no leading 'v')" + echo "Got: $TAG" + exit 1 + fi + + VERSION_FROM_TAG="$TAG" + PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" + + if [ "$PKG_VERSION" != "$VERSION_FROM_TAG" ]; then + echo "Version mismatch; refusing to publish." + echo "Git tag: $TAG" + echo "packages/astro/package.json version: $PKG_VERSION" + exit 1 + fi + + - name: Patch prerelease version with next tag + if: github.event_name == 'release' && github.event.action == 'prereleased' + shell: bash + run: | + PACKAGE_NAME="@module-federation/astro" + CURRENT_VERSION="$(node -p "require('./packages/astro/package.json').version")" + BASE_VERSION="${CURRENT_VERSION%%-*}" + CURRENT_NEXT="$(npm view "$PACKAGE_NAME" "dist-tags.next" --json 2>/dev/null || true)" + CURRENT_NEXT="${CURRENT_NEXT#\"}" + CURRENT_NEXT="${CURRENT_NEXT%\"}" + if [ "$CURRENT_NEXT" = "null" ]; then + CURRENT_NEXT="" + fi + + PREFIX_REGEX="^${BASE_VERSION//./\\.}-next\.([0-9]+)$" + if [[ "$CURRENT_NEXT" =~ $PREFIX_REGEX ]]; then + NEXT_NUMBER=$((BASH_REMATCH[1] + 1)) + else + NEXT_NUMBER=1 + fi + + NEXT_VERSION="${BASE_VERSION}-next.${NEXT_NUMBER}" + npm pkg set version="$NEXT_VERSION" --prefix packages/astro + echo "Set prerelease version: $NEXT_VERSION" - name: Build package - run: pnpm run build:package + run: pnpm build:package - - name: Resolve npm tag - id: npm-tag + - name: Compute npm dist-tag + id: dist + shell: bash run: | - VERSION=$(node -p 'require("./packages/astro/package.json").version') - TAG=$(node -p 'const version = require("./packages/astro/package.json").version; const prerelease = version.match(/-([0-9A-Za-z-]+)/); prerelease ? prerelease[1] : "latest"') - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "Publishing version $VERSION with tag $TAG" + DIST_TAG="latest" + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + if [ "${{ github.event.inputs.version }}" = "next" ]; then + DIST_TAG="next" + fi + else + if [ "${{ github.event.action }}" = "prereleased" ]; then + DIST_TAG="next" + fi + fi + + echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT" + + - name: Resolve publish action + id: publish_action + shell: bash + run: | + PACKAGE_NAME="@module-federation/astro" + PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" + DIST_TAG="${{ steps.dist.outputs.dist_tag }}" + ACTION="publish" + REASON="new version not yet on npm" + + EXISTING_VERSION="$(npm view "$PACKAGE_NAME@$PKG_VERSION" version --json 2>/dev/null || true)" + EXISTING_VERSION="${EXISTING_VERSION#\"}" + EXISTING_VERSION="${EXISTING_VERSION%\"}" + + if [ "$EXISTING_VERSION" = "$PKG_VERSION" ]; then + CURRENT_TAG_VERSION="$(npm view "$PACKAGE_NAME" "dist-tags.$DIST_TAG" --json 2>/dev/null || true)" + CURRENT_TAG_VERSION="${CURRENT_TAG_VERSION#\"}" + CURRENT_TAG_VERSION="${CURRENT_TAG_VERSION%\"}" + if [ "$CURRENT_TAG_VERSION" = "null" ]; then + CURRENT_TAG_VERSION="" + fi + + if [ "$CURRENT_TAG_VERSION" = "$PKG_VERSION" ]; then + ACTION="skip" + REASON="version already published and dist-tag already points to it" + else + echo "Refusing to republish existing npm version:" + echo "Package: $PACKAGE_NAME" + echo "Version: $PKG_VERSION" + echo "Target dist-tag: $DIST_TAG" + echo "Current '$DIST_TAG' dist-tag points to: ${CURRENT_TAG_VERSION:-}" + echo "" + echo "Trusted Publishers do not support dist-tag promotion without publishing." + echo "Use a new semver version for this release flow." + exit 1 + fi + fi + + echo "action=$ACTION" >> "$GITHUB_OUTPUT" + echo "reason=$REASON" >> "$GITHUB_OUTPUT" - name: Publish to npm - run: pnpm -r publish --tag ${{ steps.npm-tag.outputs.tag }} - \ No newline at end of file + if: steps.publish_action.outputs.action == 'publish' + working-directory: packages/astro + run: npm publish --provenance --tag "${{ steps.dist.outputs.dist_tag }}" --access public + + - name: Skip npm publish + if: steps.publish_action.outputs.action == 'skip' + run: | + echo "Skipping npm publish:" + echo "${{ steps.publish_action.outputs.reason }}" + + - name: Post summary + if: always() + continue-on-error: true + run: | + PKG_VERSION="$(node -p "require('./packages/astro/package.json').version")" + SOURCE_REF="${{ github.event.release.tag_name || github.event.inputs.branch }}" + echo "## npm publish" > "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "Package: \`@module-federation/astro\`" >> "$GITHUB_STEP_SUMMARY" + echo "Version: \`$PKG_VERSION\`" >> "$GITHUB_STEP_SUMMARY" + echo "Dist-tag: \`${{ steps.dist.outputs.dist_tag }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "Action: \`${{ steps.publish_action.outputs.action || 'unknown' }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "Reason: \`${{ steps.publish_action.outputs.reason || 'n/a' }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "Trigger: \`${{ github.event_name }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "Source ref: \`$SOURCE_REF\`" >> "$GITHUB_STEP_SUMMARY" diff --git a/README.md b/README.md index 5bbc68c..35eca9f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ pnpm --filter @module-federation/astro test - Versioning: Changesets (`pnpm changeset`) - Release PR: GitHub Actions `Release Pull Request` -- Publish: GitHub Actions `Publish (GitHub Release)` +- Publish: GitHub Actions `Release` - Release notes + operational details: `docs/RELEASING.md` ## Non-Astro SSR providers diff --git a/docs/RELEASING.md b/docs/RELEASING.md index b1b5063..070f95f 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -5,7 +5,7 @@ read_when: - Preparing a release - Wiring npm publish automation - Updating release pipeline behavior -updated_at: 2026-03-31 +updated_at: 2026-04-01 --- # Releasing @@ -23,7 +23,7 @@ This repo uses Changesets for versioning and publishes `@module-federation/astro - Stable: normal release - Pre-release: prerelease on the same base tag (example: `0.2.0`) 4. GitHub Actions publishes to npm - - Workflow: `Publish (GitHub Release)` (`.github/workflows/publish-on-release.yml`) + - Workflow: `Release` (`.github/workflows/release.yml`) - Dist-tag: - Release trigger: `latest` for stable releases, `next` for prereleases - Manual trigger (`workflow_dispatch`): `latest` or `next` @@ -35,9 +35,9 @@ This repo uses Changesets for versioning and publishes `@module-federation/astro - If the version exists but the target dist-tag points elsewhere, workflow fails. - Uses npm trusted publishing (OIDC + provenance) -## Manual Publish +## Manual Trigger -Use the `Publish (GitHub Release)` workflow with `Run workflow`: +Use the `Release` workflow with `Run workflow`: - `version=latest`: publish current `branch` head with `latest` - `version=next`: generate a snapshot version (`changeset version --snapshot`) and publish with `next` @@ -48,5 +48,5 @@ Use the `Publish (GitHub Release)` workflow with `Run workflow`: - Tags must not start with `v`. - npm trusted publisher must be configured for: - repo: `module-federation/astro` - - workflow: `.github/workflows/publish-on-release.yml` + - workflow: `.github/workflows/release.yml` - environment: `Publish`