diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a2b9aef --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + name: Validate on Node ${{ matrix.node-version }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node-version: + - 18.x + - 20.x + - 22.x + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Test + run: npm test + + - name: Build + run: npm run build + + - name: Verify committed dist is current + run: git diff --exit-code -- dist + + - name: Verify package contents + run: npm pack --dry-run + + production-audit: + name: Production dependency audit + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Audit published dependency tree + run: npm audit --omit=dev --audit-level=moderate diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..976b268 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,65 @@ +name: Publish + +on: + release: + types: + - published + workflow_dispatch: + +permissions: + contents: read + id-token: write + +concurrency: + group: publish-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + name: Publish package + runs-on: ubuntu-latest + environment: npm + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22.x + registry-url: https://registry.npmjs.org + cache: npm + + - name: Upgrade npm + run: npm install -g npm@latest + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Test + run: npm test + + - name: Build + run: npm run build + + - name: Verify committed dist is current + run: git diff --exit-code -- dist + + - name: Verify package contents + run: npm pack --dry-run + + - name: Verify release tag matches package version + if: github.event_name == 'release' + run: node -e "const pkg = require('./package.json'); const expected = 'v' + pkg.version; if (process.env.GITHUB_REF_NAME !== expected) { console.error('Release tag ' + process.env.GITHUB_REF_NAME + ' does not match package version ' + expected); process.exit(1); }" + + - name: Dry-run publish + if: github.event_name == 'workflow_dispatch' + run: npm publish --access public --provenance --dry-run + + - name: Publish to npm + if: github.event_name == 'release' + run: npm publish --access public --provenance diff --git a/README.md b/README.md index 2fd6d01..e5178e8 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,34 @@ npm test npm run build ``` +## Release process + +Pull requests run typecheck, tests, build, package verification, and a production dependency audit in GitHub Actions. + +Publishing runs when a GitHub Release is published. The release tag must match the package version in `package.json` with a leading `v`. For example, `package.json` version `0.2.0` must be released with tag `v0.2.0`; otherwise the workflow fails before publishing. + +To publish a release: + +1. Bump the package version, for example `npm version 0.2.0 --no-git-tag-version`. +2. Commit `package.json` and `package-lock.json`. +3. Merge the version bump to `main`. +4. Create and publish a GitHub Release tagged `v0.2.0`. +5. The `Publish` workflow verifies the package, then runs `npm publish --provenance --access public`. + +Before the first release, configure npm trusted publishing for this package: + +1. Merge `.github/workflows/publish.yml` to `main`. +2. Open the `@patchstack/connect` package settings on npmjs.com. +3. In **Trusted publishing**, choose **GitHub Actions**. +4. Configure: + - Organization/user: `patchstack` + - Repository: `connect` + - Workflow filename: `publish.yml` + - Environment name: `npm` +5. In GitHub repository settings, create an `npm` environment. Optional but recommended: require reviewer approval for that environment. + +Do not add an npm publish token to GitHub secrets for this workflow. Trusted publishing uses GitHub OIDC short-lived credentials. After the first trusted publish succeeds, npm recommends setting package publishing access to require two-factor authentication and disallow tokens. + ## License MIT