diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml deleted file mode 100644 index 7bcc723..0000000 --- a/.github/workflows/release-beta.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Release Beta - -on: - workflow_dispatch: - # SDK uses automatic triggers on develop branch, but for now - # we'll manually trigger. - # push: - # branches: [main] - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - release-beta: - runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - - steps: - - name: Check RELEASE_PAT secret exists - run: | - if [ -z "${{ secrets.RELEASE_PAT }}" ]; then - echo "Error: RELEASE_PAT secret is required but not set." - echo "This workflow requires a Personal Access Token to bypass branch protection rules." - echo "See PUBLISHING.md." - exit 1 - fi - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - # Use PAT to bypass branch protection rules when committing and pushing - token: ${{ secrets.RELEASE_PAT }} - # Branch check is necessary for manual triggers (workflow_dispatch) to ensure - # we only push version changes to main. If using automatic push triggers, - # the trigger itself would enforce the branch, but manual triggers can run - # from any branch. - - name: Check branch is main - run: | - if [ "${{ github.ref }}" != "refs/heads/main" ]; then - echo "Error: This workflow must be run on the main branch" - exit 1 - fi - - uses: actions/setup-node@v6 - with: - node-version: 20 - cache: "npm" - registry-url: "https://registry.npmjs.org" - - run: npm ci - - run: npm run check - - - name: Enter prerelease mode (if not already) - run: | - if [ ! -f .changeset/pre.json ]; then - npx changeset pre enter beta - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add .changeset/pre.json - git commit -m "chore: enter beta prerelease mode" - fi - - - name: Version packages - run: npm run version-packages - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create .npmrc - run: | - echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - echo "@hypercerts-org:registry=https://registry.npmjs.org/" >> .npmrc - - - name: Publish beta packages - # Use npm run release to match regular releases - it runs check before publishing - # to ensure validation after versioning (package.json/changelog changes) - run: npm run release - env: - NPM_CONFIG_PROVENANCE: true - - - name: Commit and push version changes - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add -A - git diff --staged --quiet || git commit -m "chore: version packages (beta)" - git push diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ce7131..35219c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,37 +11,113 @@ jobs: permissions: contents: write pull-requests: write + # Required for npm Trusted Publishers via GitHub OIDC + # See: https://docs.npmjs.com/trusted-publishers id-token: write steps: - uses: actions/checkout@v6 with: fetch-depth: 0 + + # Branch validation: Only allow develop (beta) or main (stable) + - name: Validate branch + run: | + if [ "${{ github.ref }}" == "refs/heads/develop" ]; then + echo "RELEASE_TYPE=beta" >> $GITHUB_ENV + echo "Detected beta release from develop branch" + elif [ "${{ github.ref }}" == "refs/heads/main" ]; then + echo "RELEASE_TYPE=stable" >> $GITHUB_ENV + echo "Detected stable release from main branch" + else + echo "Error: This workflow can only be run from 'develop' (beta) or 'main' (stable) branches" + exit 1 + fi + - uses: actions/setup-node@v6 with: node-version: 20 cache: "npm" + # registry-url is required for npm Trusted Publishers registry-url: "https://registry.npmjs.org" + - run: npm ci - run: npm run check - - name: Create .npmrc + # Stable release: Verify prerelease mode has been exited + # The exit intent should already be set on develop before merging to main + - name: Verify prerelease mode exit + if: env.RELEASE_TYPE == 'stable' run: | - echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - echo "@hypercerts-org:registry=https://registry.npmjs.org/" >> .npmrc + if [ -f .changeset/pre.json ]; then + # Check if prerelease mode has been exited (exit intent set) + if ! grep -q '"exit": true' .changeset/pre.json 2>/dev/null; then + echo "Error: Prerelease mode must be exited before merging to main." + echo "Run 'npm run changeset pre exit' on the develop branch and commit the change." + exit 1 + fi + echo "Prerelease mode exit intent confirmed - changeset version will handle the exit" + else + echo "No prerelease mode detected (pre.json not present)" + fi + # Beta-specific: Enter prerelease mode (if not already) + - name: Enter prerelease mode (if not already) + if: env.RELEASE_TYPE == 'beta' + run: | + if [ ! -f .changeset/pre.json ]; then + npx changeset pre enter beta + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add .changeset/pre.json + git commit -m "chore: enter beta prerelease mode" + git push + fi + + # Beta-specific: Version packages manually + - name: Version packages + if: env.RELEASE_TYPE == 'beta' + run: npm run version-packages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # No .npmrc creation needed - npm Trusted Publishers uses GitHub OIDC tokens + # automatically via the id-token: write permission and registry-url configuration + + # Stable release: Use changesets/action which handles versioning and publishing - name: Create Release Pull Request or Publish + if: env.RELEASE_TYPE == 'stable' id: changesets uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3 with: publish: npm run release version: npm run version-packages - title: "chore: release packages" - commit: "chore: release packages" + title: "chore: release package" + commit: "chore: release package" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: true + # Beta-specific: Publish beta packages directly + - name: Publish beta packages + if: env.RELEASE_TYPE == 'beta' + # Use npm run release to match regular releases - it runs check before publishing + # to ensure validation after versioning (package.json/changelog changes) + run: npm run release + env: + NPM_CONFIG_PROVENANCE: true + + # Beta-specific: Commit and push version changes + - name: Commit and push version changes + if: env.RELEASE_TYPE == 'beta' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add -A + git diff --staged --quiet || git commit -m "chore: version packages (beta)" + git push + + # Stable release: Log published packages - name: Log published packages - if: steps.changesets.outputs.published == 'true' + if: env.RELEASE_TYPE == 'stable' && steps.changesets.outputs.published == 'true' run: echo "Published - ${{ steps.changesets.outputs.publishedPackages }}" diff --git a/PUBLISHING.md b/PUBLISHING.md index b3174a6..3e9d1a9 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -1,33 +1,61 @@ # Publishing Guide for Maintainers This document describes how to publish the `@hypercerts-org/lexicon` -package to npm using GitHub Actions workflows with Changesets. All -publishing workflows are manually triggered to give you full control -over when releases are made. +package to npm using GitHub Actions workflows with Changesets. The +workflow uses separate branches for beta and stable releases, with all +releases manually triggered to give you full control over when releases +are made. + +## Branch Strategy + +- **`develop` branch**: Beta/prerelease versions +- **`main` branch**: Stable releases + +**Suggested Flow:** `feature` → `develop` (beta) → `main` (stable) + +**Note:** Direct development on `main` is also supported. The `develop` → `main` flow is suggested when you want to publish beta versions for testing before stable releases. If you work directly on `main`, you can skip the beta testing phase and go straight to stable releases. + +### Release Flow + +```text +┌──────────────────────────────────────────────────────────────────┐ +│ │ +│ feature/* ──────────────────┐ │ +│ │ │ │ +│ │ npm run changeset │ PR │ +│ │ (describe changes) │ │ +│ ▼ ▼ │ +│ ┌─────────┐ merge ┌──────────┐ │ +│ │ commit │ ────────► │ develop │ ──► Manual publish @beta │ +│ │ + .md │ └────┬─────┘ (0.9.0-beta.1) │ +│ └─────────┘ │ │ +│ │ PR (after: npm changeset pre exit) │ +│ ▼ │ +│ ┌────────┐ │ +│ │ main │ ──► Creates "Release PR" │ +│ └────┬───┘ │ +│ │ │ +│ │ merge Release PR │ +│ ▼ │ +│ ┌──────────────┐ │ +│ │ npm publish │ ──► @latest (0.9.0) │ +│ └──────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` ## Prerequisites Before publishing, ensure you have: -1. **GitHub Secrets configured:** - - `RELEASE_PAT`: GitHub Personal Access Token with permissions to bypass - branch protection rules. Required for the beta release workflow that - commits and pushes version changes to protected branches. - - **Recommended:** Fine-grained Personal Access Token - - Create at: - - Repository access: Select this repository - - Repository permissions: `Contents` (read and write), `Metadata` - (read-only) - - **Alternative:** Classic Personal Access Token (if fine-grained - doesn't work with your branch protection settings) - - Create at: - - Required scopes: `repo` (full control of private repositories) - - This is needed because `GITHUB_TOKEN` cannot bypass branch protection - - **Note:** Ensure branch protection rules allow the token to bypass - protection (uncheck "Do not allow bypassing the above settings" or - add the token as an allowed actor) - - `NPM_TOKEN`: npm access token with publish permissions for - `@hypercerts-org` scope +1. **npm Trusted Publisher configured:** + - The workflow uses npm Trusted Publishers via GitHub OIDC for secure, + token-less publishing + - Configure on npmjs.com: Package settings → Publishing access → Add a + GitHub Actions publisher + - Workflow name: `Release` + - See: + - No `NPM_TOKEN` secret is required ## Adding Changesets @@ -49,27 +77,45 @@ This will: collisions when multiple contributors create changesets simultaneously. The filename doesn't affect version ordering—Changesets uses git history to determine the order of changes. You can rename these files if desired, -but it's not necessary and not recommended in collaborative environments. +but it's not necessary. ## Publishing a Stable Release +**If merging from `develop` → `main`:** Before merging, you must exit prerelease mode on the `develop` branch: + +```bash +# On develop branch +npm run changeset pre exit +git add .changeset/pre.json +git commit -m "chore: exit prerelease mode" +git push +``` + +This sets the exit intent in `pre.json`, which is required before merging to `main`. The PR check will verify this. + +**If working directly on `main`:** You can skip this step since `pre.json` won't exist. + To publish a stable release to npm: -1. **Navigate to GitHub Actions:** - - Go to the repository on GitHub - - Click on the "Actions" tab +1. **If merging from `develop` → `main`:** + - Run `npm run changeset pre exit` on `develop` branch + - Commit and push the change + - Merge `develop` → `main` (the PR check will verify exit intent) -2. **Select the workflow:** - - Choose "Release" from the workflow list + **If working directly on `main`:** + - Skip this step and proceed to step 2 -3. **Run the workflow:** +2. **Run the workflow:** + - Navigate to: - Click "Run workflow" - - Select the branch (typically `main`) + - Select the branch: **`main`** (the workflow automatically detects this is a stable release) - Click "Run workflow" to start -4. **What happens:** +3. **What happens:** - The workflow validates the code and regenerates TypeScript types + - If `pre.json` exists (from `develop`), verifies that prerelease mode has been exited - If there are pending changesets, it creates a "Release Pull Request" + - The Release PR will remove prerelease tags (if any) and exit prerelease mode - Merge the Release PR to publish to npm with the `latest` tag - If no changesets exist, nothing is published @@ -77,38 +123,37 @@ To publish a stable release to npm: To publish a beta/prerelease version: -1. **Navigate to GitHub Actions:** - - Go to the "Actions" tab - -2. **Select the workflow:** - - Choose "Release Beta" - -3. **Run the workflow:** +1. **Run the workflow:** + - Navigate to: - Click "Run workflow" - - Select the branch (must be `main`) + - Select the branch: **`develop`** (the workflow automatically detects this is a beta release) - Click "Run workflow" -4. **What happens:** - - The workflow enters beta prerelease mode (if not already) +2. **What happens:** + - The workflow validates the code and regenerates TypeScript types + - Enters beta prerelease mode (if not already) and commits `pre.json` to `develop` - Versions packages based on pending changesets - Publishes to npm with the `beta` tag - Version format: `0.9.0-beta.1`, `0.9.0-beta.2`, etc. - - Commits and pushes version changes back to the repository + - Commits and pushes version changes back to the `develop` branch -**Note:** This workflow requires the `RELEASE_PAT` secret to bypass -branch protection rules when pushing version changes. +**Note:** Beta releases run on the `develop` branch, so no `RELEASE_PAT` is needed (unlike protected `main` branch). ## Validating Releases (PRs) -When you open a pull request, the "PR Check" workflow automatically +When you open a pull request to `main`, the "PR Check" workflow automatically runs to: - Check if package changes (lexicons, types, package.json) have corresponding changesets - Warn if changesets are missing +- **Reject the PR if `pre.json` exists without exit intent** - This only applies + when merging from `develop` to `main`. It ensures prerelease mode has been + properly exited before merging. Direct work on `main` doesn't have `pre.json`, + so this check is skipped. -This helps ensure all user-facing changes are properly documented -before merging. +This helps ensure all user-facing changes are properly documented and that +prerelease mode is correctly managed when using the `develop` → `main` flow. ## Version Management