diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8445896..f08ff14 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,11 +3,19 @@ name: Release on: push: branches: [main] + pull_request: + types: [opened, synchronize, reopened, closed] + branches: [main] workflow_dispatch: +concurrency: + group: publish-${{ github.event.pull_request.number || 'release' }} + cancel-in-progress: true + jobs: release: name: Release + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: contents: write @@ -49,3 +57,108 @@ jobs: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} NPM_CONFIG_PROVENANCE: true run: pnpm exec semantic-release + + publish-preview: + name: Publish Preview + if: >- + github.event_name == 'pull_request' && + github.event.action != 'closed' && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10.17.0 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + cache: "pnpm" + registry-url: "https://registry.npmjs.org" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Set preview version + id: version + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + SHORT_SHA=$(git rev-parse --short HEAD) + PREVIEW_VERSION="0.0.0-pr.${PR_NUMBER}.${SHORT_SHA}" + npm version "$PREVIEW_VERSION" --no-git-tag-version + echo "version=$PREVIEW_VERSION" >> "$GITHUB_OUTPUT" + echo "tag=pr-${PR_NUMBER}" >> "$GITHUB_OUTPUT" + + - name: Publish preview package + run: npm publish --tag ${{ steps.version.outputs.tag }} --access public --provenance + + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + const version = '${{ steps.version.outputs.version }}'; + const tag = '${{ steps.version.outputs.tag }}'; + const marker = ''; + const body = [ + marker, + '## Preview Package Published', + '', + '```', + `pnpm add effect-jmap@${version}`, + '# or', + `pnpm add effect-jmap@${tag}`, + '```', + '', + `_Published from commit ${context.sha.substring(0, 7)}_`, + ].join('\n'); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + }); + const existing = comments.find(c => c.body.includes(marker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body, + }); + } + + cleanup-preview: + name: Cleanup Preview + if: >- + github.event_name == 'pull_request' && + github.event.action == 'closed' && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: "https://registry.npmjs.org" + + - name: Remove dist-tag + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + TAG="pr-${{ github.event.pull_request.number }}" + npm dist-tag rm effect-jmap "$TAG" 2>/dev/null || true diff --git a/AGENTS.md b/AGENTS.md index 9264b94..0bd4774 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,13 @@ A TypeScript library implementing RFC 8621 JMAP for Mail using Effect-TS. - **Sentence-case** subjects: `fix: Correct the return type` (not `fix: correct...`) - No "Generated with" banners or co-author attributions +## Publishing + +- Both release and preview publishing live in `publish.yml` to share a single npm OIDC trusted publisher +- Release (push to main): uses `semantic-release` with OIDC token exchange +- Preview (pull requests): publishes `0.0.0-pr..` versions via `npm publish --provenance` with OIDC +- Do not create separate publishing workflow files — npm only allows one trusted publisher per package + ## Implementing JMAP Methods See [docs/implementing-spec-features.md](docs/implementing-spec-features.md) for the workflow.