diff --git a/.changeset/strict-cli-tests.md b/.changeset/strict-cli-tests.md new file mode 100644 index 0000000..02a205b --- /dev/null +++ b/.changeset/strict-cli-tests.md @@ -0,0 +1,5 @@ +--- +"@bridgent/cli": patch +--- + +Require the CLI test command to execute at least one test file. diff --git a/.github/RELEASE_TEMPLATE.md b/.github/RELEASE_TEMPLATE.md index fed7ed0..62e90d3 100644 --- a/.github/RELEASE_TEMPLATE.md +++ b/.github/RELEASE_TEMPLATE.md @@ -11,7 +11,9 @@ ## Changelog - + {{changelog}} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 455a38b..c056182 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,9 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: ci: name: Node ${{ matrix.node }} on ${{ matrix.os }} @@ -25,52 +28,40 @@ jobs: NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + + - name: Setup pnpm + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 + with: + cache: true - name: Setup Node.js ${{ matrix.node }} - uses: actions/setup-node@v6 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: ${{ matrix.node }} - - name: Activate pnpm via Corepack - run: | - npm install -g corepack@latest - corepack enable - corepack prepare --activate - pnpm --version - - - name: Cache pnpm store - uses: actions/cache@v5 - with: - path: ~/.local/share/pnpm/store - key: pnpm-store-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store-${{ runner.os }}-${{ matrix.node }}- + - name: Report toolchain + run: pnpm --version - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Audit production dependencies + run: pnpm audit:prod + - name: Cache turbo - uses: actions/cache@v5 + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5 with: path: .turbo key: turbo-${{ runner.os }}-${{ matrix.node }}-${{ github.sha }} restore-keys: | turbo-${{ runner.os }}-${{ matrix.node }}- - - name: Lint - run: pnpm turbo run lint - - - name: Typecheck - run: pnpm turbo run typecheck - - # Build before Test so the @bridgent/host-test cli-smoke case can find - # the freshly built `bridgent` binary at packages/cli/dist/cli.mjs. - - name: Build - run: pnpm turbo run build - - - name: Test - run: pnpm turbo run test + # Turbo's ^build edges build the CLI before host-test runs. + - name: Build, test, typecheck, and lint + run: pnpm check # PR-only: every PR that touches a publishable package must ship a changeset. # Skipped on the auto-generated "chore(release): version packages" PR (it @@ -86,21 +77,19 @@ jobs: NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 0 + persist-credentials: false + + - name: Setup pnpm + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: '24' - - name: Activate pnpm via Corepack - run: | - npm install -g corepack@latest - corepack enable - corepack prepare --activate - - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 819379b..0f77fea 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -39,26 +39,19 @@ jobs: DOCS_BASE: /Bridgent/ steps: - - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: - node-version: '24' + persist-credentials: false - - name: Activate pnpm via Corepack - run: | - npm install -g corepack@latest - corepack enable - corepack prepare --activate + - name: Setup pnpm + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 + with: + cache: true - - name: Cache pnpm store - uses: actions/cache@v5 + - name: Setup Node.js + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: - path: ~/.local/share/pnpm/store - key: pnpm-store-${{ runner.os }}-docs-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store-${{ runner.os }}-docs- + node-version: '24' - name: Install dependencies run: pnpm install --frozen-lockfile @@ -67,10 +60,10 @@ jobs: run: pnpm --filter @bridgent/docs build - name: Configure Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: path: apps/docs/.vitepress/dist @@ -85,4 +78,4 @@ jobs: steps: - name: Deploy id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 30de87d..5cfaf2d 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -26,9 +26,10 @@ jobs: name: Create GitHub Release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 0 + persist-credentials: false - name: Parse tag id: parse diff --git a/docs/release-checklist.md b/docs/release-checklist.md index da4e0b8..33ee361 100644 --- a/docs/release-checklist.md +++ b/docs/release-checklist.md @@ -11,7 +11,9 @@ authenticator app is just simpler and more reliable. ## Pre-flight - [ ] `main` is green (CI all checks pass) -- [ ] `pnpm turbo run lint typecheck test build` passes locally on Node 24 +- [ ] `pnpm check` passes locally on Node 24 (the same build, test, typecheck, + and lint gate used by CI) +- [ ] `pnpm audit:prod` reports no high-severity production dependency advisories - [ ] `pnpm --filter @bridgent/host-test test` passes (cross-host protocol harness) - [ ] Before running `pnpm changeset version`, `pnpm changeset status` shows the changeset(s) you expect. After versioning, this command is expected @@ -35,15 +37,10 @@ will prompt for an OTP because the npm account has 2FA scope set to ## Release flow -For the current v0.3 metadata + inspect hints line, publish the six packages -that changed: - -- `@bridgent/core@0.3.0` -- `@bridgent/cli@0.3.0` -- `@bridgent/source-openapi@0.3.0` -- `@bridgent/source-prisma@0.3.0` -- `@bridgent/source-drizzle@0.3.0` -- `@bridgent/source-trpc@0.3.1` +Bridgent uses independent package versions (`fixed` and `linked` are empty in +`.changeset/config.json`), so version skew between packages is expected. Treat +`pnpm changeset status` as the source of truth for the packages and versions in +each release; do not reuse a package list from an earlier release. ```bash # 1) If there are pending changesets, bump versions + regenerate CHANGELOGs. @@ -55,9 +52,10 @@ git add . git commit -m "chore(release): version packages" git push -# 3) Build everything fresh — `changeset publish` does NOT build for you -pnpm install -pnpm turbo run build +# 3) Reinstall from the committed lockfile and run the same full gate as CI. +# `changeset publish` does not build or test for you. +pnpm install --frozen-lockfile +pnpm check # 4) Publish unpublished package versions — will prompt for OTP per package pnpm changeset publish @@ -68,32 +66,35 @@ pnpm changeset publish git push --follow-tags ``` -Expected during step 4 for the current release: +Expected during step 4 (the exact package names and versions come from +`pnpm changeset status`): ``` -🦋 info Publishing "@bridgent/core" at "0.3.0" -🦋 info Publishing "@bridgent/cli" at "0.3.0" -🦋 info Publishing "@bridgent/source-openapi" at "0.3.0" -🦋 info Publishing "@bridgent/source-prisma" at "0.3.0" -🦋 info Publishing "@bridgent/source-drizzle" at "0.3.0" -🦋 info Publishing "@bridgent/source-trpc" at "0.3.1" +🦋 info Publishing "@bridgent/" at "" Enter OTP: ______ ``` +## GitHub Actions configuration + +The current manual npm publishing flow requires no repository secret to be +configured. `GITHUB_TOKEN` is supplied automatically by GitHub and is used only +by `github-release.yml` after a maintainer pushes package tags. `DOCS_BASE` and +`NPM_CONFIG_REGISTRY` are non-secret workflow configuration values. + +If CI publishing is reintroduced, configure exactly one of these authentication +paths manually under repository Settings → Secrets and variables → Actions: + +- OIDC trusted publishing: no npm token; grant the release job + `id-token: write` and configure each package's trusted publisher on npm. +- Token publishing: add `NPM_TOKEN` as a repository or environment secret. + +Never commit either credentials or one-time passwords. + ## Post-release -- [ ] Verify packages on npm: - `npm view @bridgent/core version --registry=https://registry.npmjs.org/` → `0.3.0` -- [ ] Verify packages on npm: - `npm view @bridgent/cli version --registry=https://registry.npmjs.org/` → `0.3.0` -- [ ] Verify packages on npm: - `npm view @bridgent/source-openapi version --registry=https://registry.npmjs.org/` → `0.3.0` -- [ ] Verify packages on npm: - `npm view @bridgent/source-prisma version --registry=https://registry.npmjs.org/` → `0.3.0` -- [ ] Verify packages on npm: - `npm view @bridgent/source-drizzle version --registry=https://registry.npmjs.org/` → `0.3.0` -- [ ] Verify packages on npm: - `npm view @bridgent/source-trpc version --registry=https://registry.npmjs.org/` → `0.3.1` +- [ ] For every package reported by `pnpm changeset status`, verify the published + version with + `npm view @bridgent/ version --registry=https://registry.npmjs.org/` - [ ] Smoke test the installed CLI, metadata probe, and changed source adapters from a clean temp project - [ ] Confirm auto-created one Release per package tag (the `github-release.yml` workflow handles it). Tweak titles or add highlights at the top of any Release if the auto-extracted CHANGELOG section needs polish. - [ ] Re-record demo GIF if the headline UX changed (`docs/recording.md`) diff --git a/package.json b/package.json index db2921f..c246f03 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,13 @@ "test": "turbo run test", "lint": "turbo run lint", "typecheck": "turbo run typecheck", + "check": "turbo run build test typecheck lint", + "audit:prod": "pnpm audit --prod --audit-level high", "docs:dev": "pnpm --filter @bridgent/docs dev", "docs:build": "pnpm --filter @bridgent/docs build", "changeset": "changeset", "version-packages": "changeset version", - "release": "pnpm turbo run build && changeset publish", + "release": "pnpm check && changeset publish", "clean": "rm -rf packages/*/dist packages/*/.turbo apps/*/dist apps/*/.turbo .turbo node_modules/.cache" }, "devDependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 198cb25..92b143e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,7 +38,7 @@ "scripts": { "build": "tsdown", "dev": "tsdown --watch", - "test": "vitest run --passWithNoTests", + "test": "vitest run", "typecheck": "tsc --noEmit", "lint": "eslint ." }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 250458e..72a12fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1034,6 +1034,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1959,6 +1965,10 @@ packages: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -2539,8 +2549,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.5: + resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -2739,8 +2749,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ip-address@10.2.0: - resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + ip-address@10.5.0: + resolution: {integrity: sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -3050,6 +3060,10 @@ packages: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minisearch@7.2.0: resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} @@ -3422,6 +3436,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -4503,6 +4522,11 @@ snapshots: eslint: 10.4.1(jiti@2.7.0) ignore: 7.0.5 + '@eslint-community/eslint-utils@4.10.1(eslint@10.4.1(jiti@2.7.0))': + dependencies: + eslint: 10.4.1(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(jiti@2.7.0))': dependencies: eslint: 10.4.1(jiti@2.7.0) @@ -5036,8 +5060,8 @@ snapshots: '@typescript-eslint/project-service@8.60.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) - '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: @@ -5061,7 +5085,7 @@ snapshots: eslint: 10.4.1(jiti@2.7.0) json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - semver: 7.8.1 + semver: 7.8.5 typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5107,8 +5131,8 @@ snapshots: '@typescript-eslint/types': 8.60.0 '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.1 + minimatch: 10.2.6 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -5132,7 +5156,7 @@ snapshots: '@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.4.1(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.60.0 '@typescript-eslint/types': 8.60.0 '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) @@ -5352,7 +5376,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.5 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -5429,6 +5453,10 @@ snapshots: dependencies: balanced-match: 4.0.4 + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -6007,7 +6035,7 @@ snapshots: express-rate-limit@8.5.2(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.2.0 + ip-address: 10.5.0 express@5.2.1: dependencies: @@ -6070,7 +6098,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.2: {} + fast-uri@3.1.5: {} fast-wrap-ansi@0.2.2: dependencies: @@ -6277,7 +6305,7 @@ snapshots: inherits@2.0.4: {} - ip-address@10.2.0: {} + ip-address@10.5.0: {} ipaddr.js@1.9.1: {} @@ -6761,6 +6789,10 @@ snapshots: dependencies: brace-expansion: 5.0.6 + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + minisearch@7.2.0: {} mitt@3.0.1: {} @@ -7122,6 +7154,8 @@ snapshots: semver@7.8.1: {} + semver@7.8.5: {} + send@1.2.1: dependencies: debug: 4.4.3 diff --git a/turbo.json b/turbo.json index c1e0e4f..fe9992f 100644 --- a/turbo.json +++ b/turbo.json @@ -5,10 +5,7 @@ "build": { "dependsOn": ["^build"], "inputs": [ - "src/**", - "package.json", - "tsdown.config.ts", - "tsconfig.json", + "$TURBO_DEFAULT$", "$TURBO_ROOT$/tsconfig.base.json" ], "outputs": ["dist/**", ".vitepress/dist/**", ".vitepress/cache/**"] @@ -21,27 +18,22 @@ "test": { "dependsOn": ["^build"], "inputs": [ - "src/**", - "test/**", - "tests/**", - "vitest.config.ts", - "package.json" + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/tsconfig.base.json" ], - "outputs": ["coverage/**"] + "outputs": [] }, "typecheck": { "dependsOn": ["^build"], "inputs": [ - "src/**", - "tsconfig.json", + "$TURBO_DEFAULT$", "$TURBO_ROOT$/tsconfig.base.json" ], "outputs": [] }, "lint": { "inputs": [ - "src/**", - "**/*.{ts,tsx,js,mjs,vue,md}", + "$TURBO_DEFAULT$", "$TURBO_ROOT$/eslint.config.ts" ], "outputs": []