From 5e74338b300d6fd677931dbc4aacbeb883becc3b Mon Sep 17 00:00:00 2001 From: JounQin Date: Wed, 28 Oct 2026 11:20:17 +0000 Subject: [PATCH 01/12] chore: adopt changesets and automated releases Link the three packages, start at 0.0.0 with a manual first publish, gate automatic publishing until the version moves, and use changelog-github plus changesets/action. --- .changeset/README.md | 5 + .changeset/config.json | 21 ++ .changeset/initial-baseline.md | 4 + .github/workflows/ci.yml | 26 +- .github/workflows/release.yml | 55 ++++ AGENTS.md | 4 +- DEVELOPMENT.md | 36 ++- README.md | 6 +- examples/docker-compose.snippet.yml | 2 +- package.json | 16 +- packages/cli/package.json | 7 +- packages/client/package.json | 9 +- packages/host/package.json | 9 +- packages/host/src/broker.ts | 3 +- packages/host/src/commands/compose.ts | 4 +- packages/host/src/commands/stage.ts | 2 +- scripts/publish.mjs | 14 + test/unit/broker.test.ts | 3 +- test/unit/commands.test.ts | 14 +- test/unit/gh-command.test.ts | 2 +- test/unit/http.test.ts | 2 +- yarn.lock | 431 +++++++++++++++++++++++++- 22 files changed, 625 insertions(+), 50 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .changeset/initial-baseline.md create mode 100644 .github/workflows/release.yml create mode 100644 scripts/publish.mjs diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..23db17c2 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,5 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a tool to manage versioning and changelogs. Read the full documentation at https://changesets.dev. + +We also have a quick list of common questions to get you started engaging with this project in our [FAQ](https://changesets.dev/faq). diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..d7f531e7 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://unpkg.com/@changesets/config/schema.json", + "changelog": [ + "@changesets/changelog-github", + { + "repo": "code-vaults/git-credential-broker" + } + ], + "commit": false, + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "linked": [ + [ + "gcb-cli", + "gcb-client", + "gcb-host" + ] + ], + "ignore": [] +} diff --git a/.changeset/initial-baseline.md b/.changeset/initial-baseline.md new file mode 100644 index 00000000..b5b19a21 --- /dev/null +++ b/.changeset/initial-baseline.md @@ -0,0 +1,4 @@ +--- +--- + +Initial 0.0.0 release is published manually; this empty changeset records the baseline. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98bf1821..d67d7d9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,24 @@ jobs: # typecheck + build + unit + the end-to-end push against a local git-http-backend. - run: corepack yarn check + # Every published-package change needs a changeset. The release pull request removes the + # pending changesets itself, so it is exempt. + changeset: + if: github.event_name == 'pull_request' && github.head_ref != 'changeset-release/main' + timeout-minutes: 5 + name: changeset status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + - run: corepack enable + - run: corepack yarn install --immutable + - run: corepack yarn changeset status --since=origin/main + # The third platform, and not a formality: /tmp is a symlink to /private/tmp here, chmod means less and # the shell is older, all of which the suite is supposed to be indifferent to. If it fails, that is a # finding about the tests rather than about macOS. @@ -92,7 +110,7 @@ jobs: run: | $ErrorActionPreference = 'Continue' $log = Join-Path $env:RUNNER_TEMP 'daemon.txt' - node --conditions=gcb-development packages/host/bin/gcb.js daemon --check *> $log + node --conditions=development packages/host/bin/gcb.js daemon --check *> $log $code = $LASTEXITCODE $out = Get-Content $log -Raw Write-Host "exit=$code" @@ -109,7 +127,7 @@ jobs: run: | $ErrorActionPreference = 'Continue' $log = Join-Path $env:RUNNER_TEMP 'setup.txt' - node --conditions=gcb-development packages/client/bin/gcb.js setup *> $log + node --conditions=development packages/client/bin/gcb.js setup *> $log $code = $LASTEXITCODE $out = Get-Content $log -Raw Write-Host "exit=$code" @@ -126,7 +144,7 @@ jobs: run: | $ErrorActionPreference = 'Continue' $log = Join-Path $env:RUNNER_TEMP 'compose.txt' - node --conditions=gcb-development packages/host/bin/gcb.js compose --socket-dir C:\sock *> $log + node --conditions=development packages/host/bin/gcb.js compose --socket-dir C:\sock *> $log $code = $LASTEXITCODE $out = Get-Content $log -Raw Write-Host "exit=$code" @@ -142,7 +160,7 @@ jobs: - name: help still works shell: pwsh - run: node --conditions=gcb-development packages/client/bin/gcb.js --help + run: node --conditions=development packages/client/bin/gcb.js --help wsl: timeout-minutes: 20 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..dfbdcd8a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +name: Release + +on: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + # A publish that has started has to finish: cancelling it can leave some packages on npm and the + # rest not. + cancel-in-progress: false + +permissions: + contents: write + id-token: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Changesets writes each changelog from the commits since the last release. + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + cache: yarn + registry-url: https://registry.npmjs.org + + - name: Install Dependencies + run: | + corepack enable + corepack yarn install --immutable + + - name: Build + run: corepack yarn build + + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + with: + commit-message: 'chore: release packages' + pr-title: 'chore: release packages' + version-script: corepack yarn changeset:version + publish-script: corepack yarn changeset:publish + github-token: ${{ secrets.GITHUB_TOKEN }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index cce0db6e..88f06558 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,8 +60,8 @@ corepack yarn pack:check # build, then show exactly what npm would publish The CLI runs from source too, so there is no need to build to try a command: ```sh -node --conditions=gcb-development packages/client/bin/gcb.js --help -node --conditions=gcb-development packages/host/bin/gcb.js compose --socket-dir /tmp/sock # reads --config, $GIT_BROKER_CONFIG, else ./broker.config.yml +node --conditions=development packages/client/bin/gcb.js --help +node --conditions=development packages/host/bin/gcb.js compose --socket-dir /tmp/sock # reads --config, $GIT_BROKER_CONFIG, else ./broker.config.yml ``` `yarn test` builds first because the end-to-end suite execs `packages/client/bin/gcb.js` as a **real** diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f462e260..644d8ae5 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -34,9 +34,9 @@ Node strips the types, so the package sources are directly runnable with the dev condition: ```sh -node --conditions=gcb-development packages/client/bin/gcb.js --help -node --conditions=gcb-development packages/host/bin/gcb.js compose --socket-dir /tmp/sock --config /tmp/deploy/broker.config.yml -node --conditions=gcb-development packages/host/bin/gcb.js daemon --config /tmp/deploy/broker.config.yml --check +node --conditions=development packages/client/bin/gcb.js --help +node --conditions=development packages/host/bin/gcb.js compose --socket-dir /tmp/sock --config /tmp/deploy/broker.config.yml +node --conditions=development packages/host/bin/gcb.js daemon --config /tmp/deploy/broker.config.yml --check ``` Commands find the configuration by convention — `--config`, else `$GIT_BROKER_CONFIG`, else @@ -52,7 +52,7 @@ Two consequences: - The `helper` command must resolve to an executable. `setup` records `!gcb`, so git resolves the globally installed bin through PATH; a source checkout can use the same config if it has a `gcb` shim on PATH, or spell the interpreter out explicitly with - `git config --global credential.helper '!node --conditions=gcb-development /path/packages/client/bin/gcb.js'`. + `git config --global credential.helper '!node --conditions=development /path/packages/client/bin/gcb.js'`. ## Build @@ -71,7 +71,7 @@ exec-bit detection), so the build applies it every time. As the CLI, without installing anything: ```sh -node --conditions=gcb-development packages/host/bin/gcb.js daemon --config /tmp/deploy/broker.config.yml +node --conditions=development packages/host/bin/gcb.js daemon --config /tmp/deploy/broker.config.yml ``` As the commands a user of the package gets: @@ -152,13 +152,31 @@ the checkout of that version beside them. ## Releasing -1. Bump the same `version` in all three package manifests and keep the exact `gcb-cli` dependency in - client/host in lockstep. -2. `corepack yarn check`. -3. Publish in dependency order: `npm publish packages/cli`, then `packages/client` and `packages/host`. +The three packages start at `0.0.0`; the initial publish is manual and must happen in dependency +order after a green build: + +```sh +corepack yarn check +(cd packages/cli && npm publish) +(cd packages/client && npm publish) +(cd packages/host && npm publish) +``` + +After that, published changes are versioned with Changesets: + +1. Add a changeset with `corepack yarn changeset` in the pull request that changes a published package. + CI rejects a pull request without one; the release pull request is exempt. +2. The release workflow uses `changesets/action` to open or update `chore: release packages`. Merging + that pull request runs `changeset publish`. +3. `@changesets/changelog-github` writes the changelog entries; each package's `prepublishOnly` + runs `clean-pkg-json`, so the published manifest omits the workspace-only `development` export. 4. Re-render any sidecar you generated: `compose` pins `gcb-host@`, so it takes the new version only when it is rendered again. +The release workflow needs the `NPM_TOKEN` repository secret. The `0.0.0` guard in +`scripts/publish.mjs` makes the first release manual; once the versions move, `changeset publish` +performs the release. + ## Layout ``` diff --git a/README.md b/README.md index b016b56b..b9aa5c11 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,8 @@ dependencies: ```sh git clone https://github.com/code-vaults/git-credential-broker /opt/gcb cd /opt/gcb && corepack yarn install -node --conditions=gcb-development packages/client/bin/gcb.js --help -node --conditions=gcb-development packages/host/bin/gcb.js --help +node --conditions=development packages/client/bin/gcb.js --help +node --conditions=development packages/host/bin/gcb.js --help ``` Keep the checkout outside every container mount. @@ -293,7 +293,7 @@ path, because a relative one is resolved against the current directory, which is checkout: ```sh -node --conditions=gcb-development /srv/git-cred-broker/packages/client/bin/gcb.js logs \ +node --conditions=development /srv/git-cred-broker/packages/client/bin/gcb.js logs \ --socket /srv/git-cred-broker/broker.sock \ --host github.com --repo owner/repo --job 1234567890 ``` diff --git a/examples/docker-compose.snippet.yml b/examples/docker-compose.snippet.yml index 3e4f74db..8bc1f471 100644 --- a/examples/docker-compose.snippet.yml +++ b/examples/docker-compose.snippet.yml @@ -91,7 +91,7 @@ services: - /srv/git-broker-socket:/run/git-broker ``` -Pin a version (`npx --yes --package 'gcb-host@0.1.0'`) if you would rather not +Pin a version (`npx --yes --package 'gcb-host@0.0.0'`) if you would rather not resolve `latest` on every start; `compose` pins the version it was generated from. ## 2. DSH container: reach the socket and configure git diff --git a/package.json b/package.json index cad319ef..c5e738d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gcb-workspace", - "version": "0.1.0", + "version": "0.0.0", "private": true, "description": "Workspace for gcb-cli, gcb-client and gcb-host.", "license": "MIT", @@ -24,15 +24,21 @@ "scripts": { "build": "node scripts/clean-build.mjs && tsc -p packages/cli/tsconfig.build.json && tsc -p packages/client/tsconfig.build.json && tsc -p packages/host/tsconfig.build.json && node scripts/postbuild.mjs", "typecheck": "tsc -p tsconfig.json", - "test": "yarn build && node --conditions=gcb-development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts test/e2e/*.test.ts", - "test:unit": "node --conditions=gcb-development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts", - "test:e2e": "yarn build && node --conditions=gcb-development --disable-warning=ExperimentalWarning --test test/e2e/*.test.ts", + "test": "yarn build && node --conditions=development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts test/e2e/*.test.ts", + "test:unit": "node --conditions=development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts", + "test:e2e": "yarn build && node --conditions=development --disable-warning=ExperimentalWarning --test test/e2e/*.test.ts", "check": "yarn typecheck && yarn test", "pack:check": "yarn build && node scripts/pack-check.mjs", - "prepublishOnly": "yarn check" + "prepublishOnly": "yarn check", + "changeset": "changeset", + "changeset:version": "changeset version && yarn install --mode=update-lockfile", + "changeset:publish": "node scripts/publish.mjs" }, "devDependencies": { + "@changesets/changelog-github": "^1.0.1", + "@changesets/cli": "^3.0.3", "@types/node": "^24.19.0", + "clean-pkg-json": "^2.0.0", "typescript": "^7.0.2", "yaml": "^2.9.1" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 8829c89e..fd09ef05 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "gcb-cli", - "version": "0.1.0", + "version": "0.0.0", "description": "Shared command runtime and protocol for gcb-client and gcb-host.", "license": "MIT", "author": "JounQin ", @@ -18,7 +18,7 @@ ], "exports": { ".": { - "gcb-development": "./src/index.ts", + "development": "./src/index.ts", "types": "./lib/index.d.ts", "default": "./lib/index.js" } @@ -26,5 +26,8 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "prepublishOnly": "clean-pkg-json" } } diff --git a/packages/client/package.json b/packages/client/package.json index ad593a86..64e75e32 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "gcb-client", - "version": "0.1.0", + "version": "0.0.0", "description": "Container-side gcb client: credential helper, setup, and socket-backed commands.", "license": "MIT", "author": "JounQin ", @@ -22,16 +22,19 @@ ], "exports": { ".": { - "gcb-development": "./src/index.ts", + "development": "./src/index.ts", "types": "./lib/index.d.ts", "default": "./lib/index.js" } }, "dependencies": { - "gcb-cli": "0.1.0" + "gcb-cli": "0.0.0" }, "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "prepublishOnly": "clean-pkg-json" } } diff --git a/packages/host/package.json b/packages/host/package.json index 070fc905..1f0844bb 100644 --- a/packages/host/package.json +++ b/packages/host/package.json @@ -1,6 +1,6 @@ { "name": "gcb-host", - "version": "0.1.0", + "version": "0.0.0", "description": "Host-side gcb broker, configuration, providers and administration commands.", "license": "MIT", "author": "JounQin ", @@ -22,13 +22,13 @@ ], "exports": { ".": { - "gcb-development": "./src/index.ts", + "development": "./src/index.ts", "types": "./lib/index.d.ts", "default": "./lib/index.js" } }, "dependencies": { - "gcb-cli": "0.1.0", + "gcb-cli": "0.0.0", "node-fetch-native": "^1.6.7", "x-fetch": "^0.3.0", "yaml": "^2.9.1" @@ -36,5 +36,8 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "prepublishOnly": "clean-pkg-json" } } diff --git a/packages/host/src/broker.ts b/packages/host/src/broker.ts index c4e614ae..c8297285 100644 --- a/packages/host/src/broker.ts +++ b/packages/host/src/broker.ts @@ -21,6 +21,7 @@ import fs from 'node:fs'; import { parseProxy, withProxy, type ProxyChoice } from './http.ts'; import { buildGhArgv, + PACKAGE_VERSION, getGhAction, MAX_BODY_CHARS, MAX_MESSAGE_BYTES, @@ -41,7 +42,7 @@ import { hostConfig, normalizeRepoPath, repoAllowed } from './policy.ts'; import type { ActionValues, AuditSink, BrokerConfig, Provider, WireResponse } from 'gcb-cli'; /** Reported by the `ping` operation so the container side can prove what it reached. */ -export const BROKER_VERSION = '0.1.0'; +export const BROKER_VERSION = PACKAGE_VERSION; /** How long one socket connection may stay idle before it is dropped. */ // How long a connection may stay silent before it is dropped. It has to outlast the longest wait a client diff --git a/packages/host/src/commands/compose.ts b/packages/host/src/commands/compose.ts index 1a66f396..416770ff 100644 --- a/packages/host/src/commands/compose.ts +++ b/packages/host/src/commands/compose.ts @@ -29,7 +29,7 @@ export interface ComposeInput { readonly user: string; /** Base image. */ readonly image: string; - /** npm spec to run, e.g. `gcb@0.1.0`. */ + /** npm spec to run, e.g. `gcb@0.0.0`. */ readonly packageSpec: string; /** Compose service and container name. */ readonly name: string; @@ -96,7 +96,7 @@ export function renderCompose(input: ComposeInput): string { : '\n # No user.refresh yet: run `gcb authorize` and render this again. A mount' + '\n # for a path that does not exist would become a directory, and the broker would read that.'; const start = fromCode - ? `exec node --conditions=gcb-development ${CODE_DIR}/packages/host/bin/gcb.js daemon --config ${SIDECAR.configPath}` + ? `exec node --conditions=development ${CODE_DIR}/packages/host/bin/gcb.js daemon --config ${SIDECAR.configPath}` : `exec npx --yes --package '${input.packageSpec}' gcb daemon --config ${SIDECAR.configPath}`; const command = ` - sh - -c diff --git a/packages/host/src/commands/stage.ts b/packages/host/src/commands/stage.ts index b3c3cbc9..bfd28ef2 100644 --- a/packages/host/src/commands/stage.ts +++ b/packages/host/src/commands/stage.ts @@ -178,7 +178,7 @@ agent can write to is a repository whose commits the agent can rewrite. Then run it from there — Node strips types, so there is nothing to build: corepack yarn install # in ; stage itself does not run commit code - node --conditions=gcb-development /packages/host/bin/gcb.js daemon --config /broker.config.yml + node --conditions=development /packages/host/bin/gcb.js daemon --config /broker.config.yml `; /** diff --git a/scripts/publish.mjs b/scripts/publish.mjs new file mode 100644 index 00000000..dd58f30b --- /dev/null +++ b/scripts/publish.mjs @@ -0,0 +1,14 @@ +/** Publish through Changesets, except before the manual 0.0.0 release. */ +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; + +const root = path.resolve(import.meta.dirname, '..'); +const versions = ['cli', 'client', 'host'].map((name) => + JSON.parse(readFileSync(path.join(root, 'packages', name, 'package.json'), 'utf8')).version, +); +if (versions.every((version) => version === '0.0.0')) { + console.log('The initial 0.0.0 release is manual; skipping changeset publish.'); + process.exit(0); +} +execFileSync('changeset', ['publish'], { cwd: root, stdio: 'inherit' }); diff --git a/test/unit/broker.test.ts b/test/unit/broker.test.ts index 90f0c192..105edda6 100644 --- a/test/unit/broker.test.ts +++ b/test/unit/broker.test.ts @@ -13,6 +13,7 @@ import { after, describe, it } from 'node:test'; import { redact } from '../../packages/host/src/audit.ts'; import { tokenFingerprint } from '../../packages/cli/src/fingerprint.ts'; +import { PACKAGE_VERSION } from '../../packages/cli/src/version.ts'; import { CODES, createRequestHandler, prepareSocketPath } from '../../packages/host/src/broker.ts'; import { MAX_BODY_CHARS } from '../../packages/cli/src/host-request.ts'; import { createStaticProvider } from '../../packages/host/src/providers/static.ts'; @@ -107,7 +108,7 @@ describe('broker request handling', () => { const { handle } = makeHarness(); const response = await handle({ op: 'ping' }); assert.equal(response.ok, true); - assert.equal(response.version, '0.1.0'); + assert.equal(response.version, PACKAGE_VERSION); assert.deepEqual(response.hosts, [HOST]); }); diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index a46103d5..22e89675 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -932,7 +932,7 @@ describe('compose', () => { socketDir: '/srv/shared/git-broker', user: '1026:100', image: 'node:24-slim', - packageSpec: 'gcb-host@0.1.0', + packageSpec: 'gcb-host@0.0.0', name: 'git-cred-broker', }); @@ -1022,7 +1022,7 @@ describe('compose', () => { socketDir: '/srv/shared/git-broker', user: '1026:100', image: 'node:24-slim', - packageSpec: 'gcb-host@0.1.0', + packageSpec: 'gcb-host@0.0.0', name: 'git-cred-broker', }); assert.match(custom, /\/srv\/custom\/broker\.yml:\/etc\/git-cred-broker\/broker\.config\.yml:ro/); @@ -1036,7 +1036,7 @@ describe('compose', () => { }); it('runs the daemon from the published package, not from a copied checkout', () => { - assert.match(rendered, /npx --yes --package 'gcb-host@0\.1\.0' gcb daemon/); + assert.match(rendered, /npx --yes --package 'gcb-host@0\.0\.0' gcb daemon/); assert.match(rendered, new RegExp(`--config ${SIDECAR.configPath}`)); }); @@ -1046,13 +1046,13 @@ describe('compose', () => { socketDir: '/srv/shared/git-broker', user: '1026:100', image: 'node:24-slim', - packageSpec: 'gcb-host@0.1.0', + packageSpec: 'gcb-host@0.0.0', name: 'git-cred-broker', code: '/volume1/docker/git-cred-broker/app', }); assert.match(fromCode, /- \/volume1\/docker\/git-cred-broker\/app:\/opt\/gcb:ro/); - assert.match(fromCode, /exec node --conditions=gcb-development \/opt\/gcb\/packages\/host\/bin\/gcb\.js daemon/); + assert.match(fromCode, /exec node --conditions=development \/opt\/gcb\/packages\/host\/bin\/gcb\.js daemon/); assert.equal(/npx/.test(fromCode), false, 'no registry access at start'); assert.equal(/npm_config_cache/.test(fromCode), false, 'and so no cache to write'); assert.match(fromCode, /STAGED\.json/, 'it points at the record of which commit this is'); @@ -1121,7 +1121,7 @@ describe('the anchor flags, at the CLI boundary', () => { const cli = (args: readonly string[]): { status: number; stdout: string; stderr: string } => { try { - execFileSync('node', ['--conditions=gcb-development', 'packages/client/bin/gcb.js', 'pr', ...args], { + execFileSync('node', ['--conditions=development', 'packages/client/bin/gcb.js', 'pr', ...args], { cwd: path.resolve('.'), encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], @@ -1438,7 +1438,7 @@ describe('the help path', () => { it('routes gcb help pr to gcb pr --help', () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), 'gcb-help-path-')); try { - const result = execFileSync('node', ['--conditions=gcb-development', 'packages/client/bin/gcb.js', 'help', 'pr'], { + const result = execFileSync('node', ['--conditions=development', 'packages/client/bin/gcb.js', 'help', 'pr'], { cwd: path.resolve('.'), encoding: 'utf8', env: { ...process.env, HOME: home, GIT_BROKER_SOCKET: '', GIT_BROKER_SOCKET_FILE: '' }, diff --git a/test/unit/gh-command.test.ts b/test/unit/gh-command.test.ts index 7ad7b1b7..963ecc15 100644 --- a/test/unit/gh-command.test.ts +++ b/test/unit/gh-command.test.ts @@ -17,7 +17,7 @@ const CLIENT_BIN = fileURLToPath(new URL('../../packages/client/bin/gcb.js', imp function runClient(args: readonly string[], cwd?: string): Promise { return tryRunAsync( process.execPath, - ['--conditions=gcb-development', CLIENT_BIN, ...args], + ['--conditions=development', CLIENT_BIN, ...args], { cwd, env: process.env, timeoutMs: 5000 }, ); } diff --git a/test/unit/http.test.ts b/test/unit/http.test.ts index 6023681a..190f98a9 100644 --- a/test/unit/http.test.ts +++ b/test/unit/http.test.ts @@ -79,7 +79,7 @@ describe('startup proxy environment', () => { const response = await fetcherFor(null)('http://127.0.0.1:${address.port}/', { method: 'GET', headers: {} }); console.log(response.status, await response.text()); `; - const child = spawn(process.execPath, ['--conditions=gcb-development', '--input-type=module', '-e', script], { + const child = spawn(process.execPath, ['--conditions=development', '--input-type=module', '-e', script], { cwd: process.cwd(), env: { ...process.env, diff --git a/yarn.lock b/yarn.lock index 44464940..52edf9bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,265 @@ __metadata: version: 10 cacheKey: 10c0 +"@changesets/apply-release-plan@npm:^8.1.1": + version: 8.1.1 + resolution: "@changesets/apply-release-plan@npm:8.1.1" + dependencies: + "@changesets/config": "npm:^4.0.1" + "@changesets/format": "npm:^0.1.2" + "@changesets/git": "npm:^4.0.1" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + import-meta-resolve: "npm:^4.2.0" + jsonc-parser: "npm:^3.3.1" + semver: "npm:^7.8.1" + checksum: 10c0/2a1cfb9e4e3feb2c76bcec646e116ebebc81271fed21a3e77d5f170963f88347a34e5d1474f8b87258448182cfa651c047282c8b1a741613a7f7d3fefc2eb9a2 + languageName: node + linkType: hard + +"@changesets/assemble-release-plan@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/assemble-release-plan@npm:7.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/2544759583889865487aec744e948c6d84206a42aa593430436ace83003fe00d3880ef623114c396cd5fd883eccb1e03fc4a719ba5c33038a8556df8fca4b9b7 + languageName: node + linkType: hard + +"@changesets/changelog-git@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/changelog-git@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/f0f0ab31e6e6ac35ecdae46a521d4c84aab77bbb035898e364d8f9082391b2b8064ba33d9f97d4f1526195f8278f41910c72382b12fcc0092d5bb9ff3ae05310 + languageName: node + linkType: hard + +"@changesets/changelog-github@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/changelog-github@npm:1.0.1" + dependencies: + "@changesets/get-github-info": "npm:^1.0.1" + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/d1538b48d8cfa8846ff5428f7c98301342378c5d8fe0c077679dfdcf373f53e989553783159e5e594cf292b61d303560d32de4abb57b77453cc0c872acc8420b + languageName: node + linkType: hard + +"@changesets/cli@npm:^3.0.3": + version: 3.0.3 + resolution: "@changesets/cli@npm:3.0.3" + dependencies: + "@changesets/apply-release-plan": "npm:^8.1.1" + "@changesets/assemble-release-plan": "npm:^7.0.0" + "@changesets/changelog-git": "npm:^1.0.0" + "@changesets/config": "npm:^4.0.1" + "@changesets/errors": "npm:^1.0.0" + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/git": "npm:^4.0.1" + "@changesets/pre": "npm:^3.0.0" + "@changesets/read": "npm:^1.0.1" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@changesets/write": "npm:^1.0.1" + "@clack/prompts": "npm:^1.7.0" + "@manypkg/get-packages": "npm:^3.1.0" + "@pnpm/deps.graph-sequencer": "npm:^1100.0.1" + cac: "npm:^7.0.0" + import-meta-resolve: "npm:^4.2.0" + launch-editor: "npm:^2.14.1" + package-manager-detector: "npm:^1.6.0" + semver: "npm:^7.8.1" + tinyexec: "npm:^1.3.1" + bin: + changeset: bin.js + checksum: 10c0/238b98c590bfe33e794cf5ab47e0fa6eb46fc587da9498af49037877f1043b3e91dac65c990ebd1675115d82f2a3a2b3e7aa1b5d385369b0322a24e858eb1313 + languageName: node + linkType: hard + +"@changesets/config@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/config@npm:4.0.1" + dependencies: + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.7" + checksum: 10c0/959dbbb3ab9fab0427a8990cbe7d4465d9b67042c96108209c8110b7c5ccb2fc3303b75d5c024e911d3a51ca082d13d49a5091edf52014a2b8d69e89b58188ed + languageName: node + linkType: hard + +"@changesets/errors@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/errors@npm:1.0.0" + checksum: 10c0/502439fb046b9916e8a0745374c75fa80883b0bdeea92aa2e85be07b8675dcdb50df5bd108ce66eddbda1f8863be750bf817301c9d4904c2168305df870e3f9f + languageName: node + linkType: hard + +"@changesets/format@npm:^0.1.1, @changesets/format@npm:^0.1.2": + version: 0.1.3 + resolution: "@changesets/format@npm:0.1.3" + dependencies: + package-manager-detector: "npm:^1.8.0" + tinyexec: "npm:^1.3.1" + checksum: 10c0/49819e75c753a5ce1249f7b52948692b1f678e1be3721cbfb1904da9319e6eedc277273deefb783afe87a16dd53f908addc1378d331194e50c40627ce824b0c4 + languageName: node + linkType: hard + +"@changesets/get-dependents-graph@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/get-dependents-graph@npm:3.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/ab675098db0b92f61115952fb793620bb5b76e502bad5f7f63bcc85a50e5515f70e08957be87381a58141ab2078bf94dd92df86efa55e73227e7604ba6a3968b + languageName: node + linkType: hard + +"@changesets/get-github-info@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/get-github-info@npm:1.0.1" + dependencies: + dataloader: "npm:^2.2.3" + checksum: 10c0/7b45b5ebc97b297e1c373ec782c501c3b6cc911badf43e4ebaaf46ce1d8b94f62e4d758705e03d3b9df06b730ab85b9dab5ad76ff070f8ff65ef2fb21647d61b + languageName: node + linkType: hard + +"@changesets/git@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/git@npm:4.0.1" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.4" + tinyexec: "npm:^1.3.0" + checksum: 10c0/b548ecfafb238ff477a793e4ee91c1fe818274b9ea5b6d5a9d619f75a915ad27831f2accf2edce77da57f918bf1009b59cca2c5aaa7288db4b266f87872c2c23 + languageName: node + linkType: hard + +"@changesets/parse@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/parse@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + yaml: "npm:^2.9.0" + checksum: 10c0/76e93b099015420dfc08f3e3a9321ea2512659d8946d1ed86de64899b52d37faa87173cc80ead5d313ffce2f829ce380a0188c0a1cccaad48350e7c6a83720d0 + languageName: node + linkType: hard + +"@changesets/pre@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/pre@npm:3.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + checksum: 10c0/56e49668f25cabf50dcab12b6ae20fd7dbeba628390149d38a9075b05c41b954d4fbcebe7535878087c5f0b707ce96b90b727286850c71d6d321477bd1a1c18e + languageName: node + linkType: hard + +"@changesets/read@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/read@npm:1.0.1" + dependencies: + "@changesets/git": "npm:^4.0.1" + "@changesets/parse": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/125de7132b71e9c184f35ec0ad4d23b9accb0aa04d178c835ef575fbf12afbed2fd2d975e987ebb70c007c9a08dc97a267123ba515a4c2f4f070279ad8193f46 + languageName: node + linkType: hard + +"@changesets/should-skip-package@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/should-skip-package@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/9beb51e7a6a6d678fbfe60efca4646d7daf117ffb90d3dbe3e8b07806f87383cf2a49b0e15b5fe2b827b69f61c0cf7cb77c4bf27b403c2ce835c5ba7990ed0e5 + languageName: node + linkType: hard + +"@changesets/types@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/types@npm:7.0.0" + checksum: 10c0/494e09ff94968c1c81521f5ac8c6ab60a1e30d7d0cd9ea7f83c2cbac92efba38b03e008ee6fb4592d90b05d69bad864b6ae8155827d52a51c652f5c24d36ec83 + languageName: node + linkType: hard + +"@changesets/write@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/write@npm:1.0.1" + dependencies: + "@changesets/format": "npm:^0.1.1" + "@changesets/types": "npm:^7.0.0" + human-id: "npm:^4.2.0" + checksum: 10c0/c9cb074a8c524db1327e54dc1379ff4b50d3a4915409a3e5abe1edaf1ce45f6dafc6c8aced17a1f3eebfa162241b5fd8ea74fc751d2e24df56c2966e4169fcaf + languageName: node + linkType: hard + +"@clack/core@npm:1.5.1": + version: 1.5.1 + resolution: "@clack/core@npm:1.5.1" + dependencies: + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/6dca93ff5a34bfe8c150367519bc40a847e9172cb1a78b18c88da0507cb6ad899debe1ad5cfcb9796e439108f12f003cf06fd002e2293bc85ff500975e45ff13 + languageName: node + linkType: hard + +"@clack/prompts@npm:^1.7.0": + version: 1.8.1 + resolution: "@clack/prompts@npm:1.8.1" + dependencies: + "@clack/core": "npm:1.5.1" + fast-string-width: "npm:^3.0.2" + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/ed38748acde0cb1e7ebdac1242470c78bd90380cbdde1e5560e0828881e3c74e2676de666086cf8183c8d4d3f1fc6e489f15a004f0d9f8ee294a3aa744de2093 + languageName: node + linkType: hard + +"@manypkg/find-root@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/find-root@npm:3.1.0" + dependencies: + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/15b3f5c5c66881af88c1c70dcec805237a2b7e1d541ca7687aade00978680024f8e77ca2f4bac1415e00377f2e78a998d842c345c288d3e69383d50280620b1c + languageName: node + linkType: hard + +"@manypkg/get-packages@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/get-packages@npm:3.1.0" + dependencies: + "@manypkg/find-root": "npm:^3.1.0" + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/e8baabd85a13f4537ae22124d5e53f1523ba0653f33006da45fc8bd642e0134786a6aad9d0a7ed99282ae20a80687ef5700bd72980441627b5c96658503ad83d + languageName: node + linkType: hard + +"@manypkg/tools@npm:^2.1.0": + version: 2.1.2 + resolution: "@manypkg/tools@npm:2.1.2" + dependencies: + jju: "npm:^1.4.0" + tinyglobby: "npm:^0.2.13" + yaml: "npm:^2.9.0" + checksum: 10c0/05c8ece3b3b3ff170765ecf230f0a5408543f1ad43f8b5dc97651adff0ffed3d860dabdd1e2d1068aa247832d473c4de583791675b8f3d07568da4cab2a0ebc3 + languageName: node + linkType: hard + +"@pnpm/deps.graph-sequencer@npm:^1100.0.1": + version: 1100.0.1 + resolution: "@pnpm/deps.graph-sequencer@npm:1100.0.1" + checksum: 10c0/b38a152e1184055a358e7c522f068572e84759a88dfc56c98a349125b11d3cce72e452424c235af5cf18258a5034f88fe203c72ba57ffbe70b297afc50d08dea + languageName: node + linkType: hard + "@types/node@npm:^24.19.0": version: 24.19.0 resolution: "@types/node@npm:24.19.0" @@ -154,7 +413,67 @@ __metadata: languageName: node linkType: hard -"gcb-cli@npm:0.1.0, gcb-cli@workspace:packages/cli": +"cac@npm:^7.0.0": + version: 7.1.0 + resolution: "cac@npm:7.1.0" + checksum: 10c0/8bfeef75404cf7d1799abdf6e07b276f3bb646caacdef4d492c64dcff6d4277ec81254612343d041e00ede588fd57117d1a46238b6db18770461dcb2cb1f7487 + languageName: node + linkType: hard + +"clean-pkg-json@npm:^2.0.0": + version: 2.0.0 + resolution: "clean-pkg-json@npm:2.0.0" + bin: + clean-pkg-json: dist/index.mjs + checksum: 10c0/fe8828c0e788dcc20fec97f02d4f182becca30817896fe350095c01b5e22cbc5c02f912e5ce4f9ad509d11ff60e52803bbdfb153a8291df72ae1b80a0ce9aa53 + languageName: node + linkType: hard + +"dataloader@npm:^2.2.3": + version: 2.2.3 + resolution: "dataloader@npm:2.2.3" + checksum: 10c0/9b9a056fbc863ca86da87d59e053e871e263b4966aa4d55e40d61a65e96815fae5530ca220629064ca5f8e3000c0c4ec93292e170c38ff393fb34256b4d7c1aa + languageName: node + linkType: hard + +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"gcb-cli@npm:0.0.0, gcb-cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "gcb-cli@workspace:packages/cli" languageName: unknown @@ -164,7 +483,7 @@ __metadata: version: 0.0.0-use.local resolution: "gcb-client@workspace:packages/client" dependencies: - gcb-cli: "npm:0.1.0" + gcb-cli: "npm:0.0.0" bin: gcb: ./bin/gcb.js languageName: unknown @@ -174,7 +493,7 @@ __metadata: version: 0.0.0-use.local resolution: "gcb-host@workspace:packages/host" dependencies: - gcb-cli: "npm:0.1.0" + gcb-cli: "npm:0.0.0" node-fetch-native: "npm:^1.6.7" x-fetch: "npm:^0.3.0" yaml: "npm:^2.9.1" @@ -187,12 +506,55 @@ __metadata: version: 0.0.0-use.local resolution: "gcb-workspace@workspace:." dependencies: + "@changesets/changelog-github": "npm:^1.0.1" + "@changesets/cli": "npm:^3.0.3" "@types/node": "npm:^24.19.0" + clean-pkg-json: "npm:^2.0.0" typescript: "npm:^7.0.2" yaml: "npm:^2.9.1" languageName: unknown linkType: soft +"human-id@npm:^4.2.0": + version: 4.2.2 + resolution: "human-id@npm:4.2.2" + bin: + human-id: dist/cli.js + checksum: 10c0/7bf51bf2cec8c26129d3f6ea068b18eca702130641afe5c475cb92270015e403be5bdd35f53b2da38a058a3c1c2632c2d0576c026ad78dc33d30af53eb7d426d + languageName: node + linkType: hard + +"import-meta-resolve@npm:^4.2.0": + version: 4.2.0 + resolution: "import-meta-resolve@npm:4.2.0" + checksum: 10c0/3ee8aeecb61d19b49d2703987f977e9d1c7d4ba47db615a570eaa02fe414f40dfa63f7b953e842cbe8470d26df6371332bfcf21b2fd92b0112f9fea80dde2c4c + languageName: node + linkType: hard + +"jju@npm:^1.4.0": + version: 1.4.0 + resolution: "jju@npm:1.4.0" + checksum: 10c0/f3f444557e4364cfc06b1abf8331bf3778b26c0c8552ca54429bc0092652172fdea26cbffe33e1017b303d5aa506f7ede8571857400efe459cb7439180e2acad + languageName: node + linkType: hard + +"jsonc-parser@npm:^3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + +"launch-editor@npm:^2.14.1": + version: 2.14.2 + resolution: "launch-editor@npm:2.14.2" + dependencies: + picocolors: "npm:^1.1.1" + shell-quote: "npm:^1.10.0" + checksum: 10c0/cf66c75534170142269bfc1749df8a4b5178ad644d358aa08d4feda9ecb1ba7a26191a76ea2cecea95c59235c142486b928b58eaf0cd77377d21e42ed5c3e100 + languageName: node + linkType: hard + "node-fetch-native@npm:^1.6.7": version: 1.6.7 resolution: "node-fetch-native@npm:1.6.7" @@ -200,6 +562,67 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^1.6.0, package-manager-detector@npm:^1.8.0": + version: 1.9.0 + resolution: "package-manager-detector@npm:1.9.0" + checksum: 10c0/c901b08e9d284e9b517d27be1783fc279f3e814fac9de4c30441129a3a4b1475b16314ff95b98268daf49e20d5076478c50bc7b1b3ff8a591eb39f03f02dbb16 + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.4, picomatch@npm:^4.0.7": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f + languageName: node + linkType: hard + +"semver@npm:^7.8.1": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"shell-quote@npm:^1.10.0": + version: 1.12.0 + resolution: "shell-quote@npm:1.12.0" + checksum: 10c0/1605a4ed6a758bf13de7cb8197a4184d8015f7a4dbdcd6cd4a717d5b30b3ae7666300cc4ae7f3155cf80ace9739e9e82061ee5aa1a648f53f5fab31a220816d0 + languageName: node + linkType: hard + +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 + languageName: node + linkType: hard + +"tinyexec@npm:^1.3.0, tinyexec@npm:^1.3.1": + version: 1.3.1 + resolution: "tinyexec@npm:1.3.1" + checksum: 10c0/c590369cb3fa73cc18a3998caec8a00f9712d91309c881be3ac52b160fb2f80c7b1fbf3591858ab6abab47ad55385b0c86ec19982e32441a01f55298dab9ccd2 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.13": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + "typescript@npm:^7.0.2": version: 7.0.2 resolution: "typescript@npm:7.0.2" @@ -356,7 +779,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.9.1": +"yaml@npm:^2.9.0, yaml@npm:^2.9.1": version: 2.9.1 resolution: "yaml@npm:2.9.1" bin: From 69a49049f6f573b0857c2bfb683c4fe4379f29f6 Mon Sep 17 00:00:00 2001 From: JounQin Date: Wed, 28 Oct 2026 11:25:02 +0000 Subject: [PATCH 02/12] ci: publish with npm trusted publishing Use the release job's OIDC token; no NPM_TOKEN secret is read. --- .github/workflows/release.yml | 3 --- DEVELOPMENT.md | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dfbdcd8a..dafa2020 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,6 @@ jobs: with: node-version: '24' cache: yarn - registry-url: https://registry.npmjs.org - name: Install Dependencies run: | @@ -51,5 +50,3 @@ jobs: version-script: corepack yarn changeset:version publish-script: corepack yarn changeset:publish github-token: ${{ secrets.GITHUB_TOKEN }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 644d8ae5..2ca1fc07 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -173,9 +173,10 @@ After that, published changes are versioned with Changesets: 4. Re-render any sidecar you generated: `compose` pins `gcb-host@`, so it takes the new version only when it is rendered again. -The release workflow needs the `NPM_TOKEN` repository secret. The `0.0.0` guard in -`scripts/publish.mjs` makes the first release manual; once the versions move, `changeset publish` -performs the release. +The release workflow uses npm trusted publishing (OIDC): `id-token: write` is granted, no +`NPM_TOKEN` secret is read. Configure the npm package's trusted publisher for this repository first. +The `0.0.0` guard in `scripts/publish.mjs` makes the first release manual; once the versions move, +`changeset publish` performs the release. ## Layout From caeaddfb5c0100b56167c9dd0bdb5da54273ba0c Mon Sep 17 00:00:00 2001 From: JounQin Date: Wed, 28 Oct 2026 13:11:23 +0000 Subject: [PATCH 03/12] chore: address release review Drop the changeset CI gate and manual-publish helper, simplify the config, follow the standard changesets/action workflow, and publish both images on release. --- .changeset/config.json | 11 +--- .changeset/initial-baseline.md | 4 -- .github/workflows/ci.yml | 18 ------ .github/workflows/release.yml | 112 +++++++++++++++++++++++++++++---- DEVELOPMENT.md | 18 +----- package.json | 2 +- scripts/publish.mjs | 14 ----- 7 files changed, 106 insertions(+), 73 deletions(-) delete mode 100644 .changeset/initial-baseline.md delete mode 100644 scripts/publish.mjs diff --git a/.changeset/config.json b/.changeset/config.json index d7f531e7..1189c519 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,16 +6,7 @@ "repo": "code-vaults/git-credential-broker" } ], - "commit": false, "access": "public", "baseBranch": "main", - "updateInternalDependencies": "patch", - "linked": [ - [ - "gcb-cli", - "gcb-client", - "gcb-host" - ] - ], - "ignore": [] + "updateInternalDependencies": "patch" } diff --git a/.changeset/initial-baseline.md b/.changeset/initial-baseline.md deleted file mode 100644 index b5b19a21..00000000 --- a/.changeset/initial-baseline.md +++ /dev/null @@ -1,4 +0,0 @@ ---- ---- - -Initial 0.0.0 release is published manually; this empty changeset records the baseline. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d67d7d9b..37834e43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,24 +43,6 @@ jobs: # typecheck + build + unit + the end-to-end push against a local git-http-backend. - run: corepack yarn check - # Every published-package change needs a changeset. The release pull request removes the - # pending changesets itself, so it is exempt. - changeset: - if: github.event_name == 'pull_request' && github.head_ref != 'changeset-release/main' - timeout-minutes: 5 - name: changeset status - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: '24' - - run: corepack enable - - run: corepack yarn install --immutable - - run: corepack yarn changeset status --since=origin/main - # The third platform, and not a formality: /tmp is a symlink to /private/tmp here, chmod means less and # the shell is older, all of which the suite is supposed to be indifferent to. If it fails, that is a # finding about the tests rather than about macOS. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dafa2020..44476f47 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,14 +12,21 @@ concurrency: cancel-in-progress: false permissions: - contents: write - id-token: write - pull-requests: write + contents: read jobs: release: name: Release runs-on: ubuntu-latest + permissions: + # `id-token` is npm trusted publishing; `contents` and `pull-requests` are the version commit + # and the release pull request. + contents: write + id-token: write + pull-requests: write + outputs: + version: ${{ steps.version.outputs.version }} + published: ${{ steps.changesets.outputs.published }} steps: - name: Checkout Repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -30,16 +37,18 @@ jobs: - name: Setup Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: '24' + node-version: lts/* cache: yarn - name: Install Dependencies - run: | - corepack enable - corepack yarn install --immutable + run: yarn install --immutable - name: Build - run: corepack yarn build + run: yarn build + + - name: Read the package version + id: version + run: echo "version=$(jq -r .version packages/cli/package.json)" >> "$GITHUB_OUTPUT" - name: Create Release Pull Request or Publish to npm id: changesets @@ -47,6 +56,87 @@ jobs: with: commit-message: 'chore: release packages' pr-title: 'chore: release packages' - version-script: corepack yarn changeset:version - publish-script: corepack yarn changeset:publish - github-token: ${{ secrets.GITHUB_TOKEN }} + version-script: yarn changeset:version + publish-script: yarn changeset:publish + + image: + name: Publish ${{ matrix.image }} + needs: release + if: needs.release.outputs.published == 'true' + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - image: gcb-client + target: client + - image: gcb-host + target: host + steps: + - name: Checkout Repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup QEMU + uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: arm64 + + - name: Setup Buildx + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 + + - name: Log in to GHCR + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to Docker Hub + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Work out the tags + id: tags + env: + VERSION: ${{ needs.release.outputs.version }} + IMAGE: ${{ matrix.image }} + run: | + { + for registry in ghcr.io/code-vaults docker.io/code-vaults; do + echo "$registry/$IMAGE:latest" + echo "$registry/$IMAGE:sha-${GITHUB_SHA}" + echo "$registry/$IMAGE:${VERSION}" + done + } > "$RUNNER_TEMP/tags" + { + echo 'tags<> "$GITHUB_OUTPUT" + + - name: Build and push + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: . + target: ${{ matrix.target }} + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.tags.outputs.tags }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.version=${{ needs.release.outputs.version }} + org.opencontainers.image.title=${{ matrix.image }} + provenance: true + sbom: true + cache-from: type=gha,scope=${{ matrix.image }} + cache-to: type=gha,mode=max,scope=${{ matrix.image }} diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2ca1fc07..cd26a045 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -152,31 +152,19 @@ the checkout of that version beside them. ## Releasing -The three packages start at `0.0.0`; the initial publish is manual and must happen in dependency -order after a green build: - -```sh -corepack yarn check -(cd packages/cli && npm publish) -(cd packages/client && npm publish) -(cd packages/host && npm publish) -``` - -After that, published changes are versioned with Changesets: +Published changes are versioned with Changesets: 1. Add a changeset with `corepack yarn changeset` in the pull request that changes a published package. - CI rejects a pull request without one; the release pull request is exempt. 2. The release workflow uses `changesets/action` to open or update `chore: release packages`. Merging that pull request runs `changeset publish`. 3. `@changesets/changelog-github` writes the changelog entries; each package's `prepublishOnly` runs `clean-pkg-json`, so the published manifest omits the workspace-only `development` export. -4. Re-render any sidecar you generated: `compose` pins `gcb-host@`, so it takes +4. The same release builds and publishes the `gcb-client` and `gcb-host` images. +5. Re-render any sidecar you generated: `compose` pins `gcb-host@`, so it takes the new version only when it is rendered again. The release workflow uses npm trusted publishing (OIDC): `id-token: write` is granted, no `NPM_TOKEN` secret is read. Configure the npm package's trusted publisher for this repository first. -The `0.0.0` guard in `scripts/publish.mjs` makes the first release manual; once the versions move, -`changeset publish` performs the release. ## Layout diff --git a/package.json b/package.json index c5e738d3..12344a00 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "prepublishOnly": "yarn check", "changeset": "changeset", "changeset:version": "changeset version && yarn install --mode=update-lockfile", - "changeset:publish": "node scripts/publish.mjs" + "changeset:publish": "changeset publish" }, "devDependencies": { "@changesets/changelog-github": "^1.0.1", diff --git a/scripts/publish.mjs b/scripts/publish.mjs deleted file mode 100644 index dd58f30b..00000000 --- a/scripts/publish.mjs +++ /dev/null @@ -1,14 +0,0 @@ -/** Publish through Changesets, except before the manual 0.0.0 release. */ -import { execFileSync } from 'node:child_process'; -import { readFileSync } from 'node:fs'; -import path from 'node:path'; - -const root = path.resolve(import.meta.dirname, '..'); -const versions = ['cli', 'client', 'host'].map((name) => - JSON.parse(readFileSync(path.join(root, 'packages', name, 'package.json'), 'utf8')).version, -); -if (versions.every((version) => version === '0.0.0')) { - console.log('The initial 0.0.0 release is manual; skipping changeset publish.'); - process.exit(0); -} -execFileSync('changeset', ['publish'], { cwd: root, stdio: 'inherit' }); From 3fdabefb22eb1e0076a5b0faf9e0be42899e6d27 Mon Sep 17 00:00:00 2001 From: JounQin Date: Wed, 28 Oct 2026 13:51:14 +0000 Subject: [PATCH 04/12] chore: simplify release scripts and image matrix Use the standard version/release scripts and derive the image name from the target. --- .github/workflows/release.yml | 20 ++++++++------------ package.json | 5 ++--- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44476f47..4bfa2409 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,11 +56,11 @@ jobs: with: commit-message: 'chore: release packages' pr-title: 'chore: release packages' - version-script: yarn changeset:version - publish-script: yarn changeset:publish + version-script: yarn run version + publish-script: yarn release image: - name: Publish ${{ matrix.image }} + name: Publish gcb-${{ matrix.target }} needs: release if: needs.release.outputs.published == 'true' runs-on: ubuntu-latest @@ -71,11 +71,7 @@ jobs: strategy: fail-fast: false matrix: - include: - - image: gcb-client - target: client - - image: gcb-host - target: host + target: [client, host] steps: - name: Checkout Repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -108,7 +104,7 @@ jobs: id: tags env: VERSION: ${{ needs.release.outputs.version }} - IMAGE: ${{ matrix.image }} + IMAGE: gcb-${{ matrix.target }} run: | { for registry in ghcr.io/code-vaults docker.io/code-vaults; do @@ -135,8 +131,8 @@ jobs: org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.version=${{ needs.release.outputs.version }} - org.opencontainers.image.title=${{ matrix.image }} + org.opencontainers.image.title=gcb-${{ matrix.target }} provenance: true sbom: true - cache-from: type=gha,scope=${{ matrix.image }} - cache-to: type=gha,mode=max,scope=${{ matrix.image }} + cache-from: type=gha,scope=${{ matrix.target }} + cache-to: type=gha,mode=max,scope=${{ matrix.target }} diff --git a/package.json b/package.json index 12344a00..cdc47a79 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,8 @@ "check": "yarn typecheck && yarn test", "pack:check": "yarn build && node scripts/pack-check.mjs", "prepublishOnly": "yarn check", - "changeset": "changeset", - "changeset:version": "changeset version && yarn install --mode=update-lockfile", - "changeset:publish": "changeset publish" + "version": "changeset version && yarn install --mode=update-lockfile", + "release": "changeset publish" }, "devDependencies": { "@changesets/changelog-github": "^1.0.1", From 9779e715c6b54a161896544d186ed9be411fb5cc Mon Sep 17 00:00:00 2001 From: JounQin Date: Wed, 28 Oct 2026 14:06:47 +0000 Subject: [PATCH 05/12] chore: lock the three package versions together Use a fixed Changesets group so the single image tag names every package's version. --- .changeset/config.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.changeset/config.json b/.changeset/config.json index 1189c519..c7f23f6b 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -8,5 +8,12 @@ ], "access": "public", "baseBranch": "main", - "updateInternalDependencies": "patch" + "updateInternalDependencies": "patch", + "fixed": [ + [ + "gcb-cli", + "gcb-client", + "gcb-host" + ] + ] } From dccd3b10f122c226dc27aeee7b8fa026fd0a0b6a Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 08:35:55 +0000 Subject: [PATCH 06/12] refactor: rename the shared package to gcb-core The package is the shared parser, protocol and action catalog, not a CLI entry point; packages/core and gcb-core describe it. --- .../2026-10-23-three-package-workspaces.md | 24 +++++++++---------- .changeset/config.json | 2 +- .github/workflows/release.yml | 2 +- AGENTS.md | 4 ++-- DEVELOPMENT.md | 8 +++---- Dockerfile | 6 ++--- README.md | 8 +++---- ROADMAP.md | 8 +++---- package.json | 4 ++-- packages/client/bin/gcb.js | 2 +- packages/client/package.json | 2 +- packages/client/src/commands/gh.ts | 2 +- packages/client/src/commands/logs.ts | 2 +- packages/client/src/commands/pr.ts | 4 ++-- packages/client/src/commands/probe.ts | 4 ++-- packages/client/src/commands/setup.ts | 2 +- packages/client/src/helper.ts | 4 ++-- packages/client/src/index.ts | 4 ++-- packages/{cli => core}/package.json | 2 +- packages/{cli => core}/src/actions.ts | 0 packages/{cli => core}/src/errors.ts | 0 packages/{cli => core}/src/fingerprint.ts | 0 packages/{cli => core}/src/host-request.ts | 0 packages/{cli => core}/src/index.ts | 0 packages/{cli => core}/src/paths.ts | 0 packages/{cli => core}/src/platform.ts | 0 packages/{cli => core}/src/socket-protocol.ts | 0 packages/{cli => core}/src/support.ts | 0 packages/{cli => core}/src/types.ts | 0 packages/{cli => core}/src/version.ts | 0 packages/{cli => core}/tsconfig.build.json | 0 packages/{cli => core}/tsconfig.json | 0 packages/host/bin/gcb.js | 2 +- packages/host/package.json | 2 +- packages/host/src/audit.ts | 2 +- packages/host/src/broker.ts | 4 ++-- packages/host/src/commands/authorize.ts | 2 +- packages/host/src/commands/compose.ts | 4 ++-- packages/host/src/commands/diagnose.ts | 4 ++-- packages/host/src/commands/host-opener.ts | 4 ++-- packages/host/src/commands/init.ts | 4 ++-- packages/host/src/commands/stage.ts | 4 ++-- packages/host/src/config.ts | 2 +- packages/host/src/daemon.ts | 2 +- packages/host/src/gh.ts | 2 +- packages/host/src/http.ts | 2 +- packages/host/src/index.ts | 2 +- packages/host/src/policy.ts | 2 +- packages/host/src/providers/github-app.ts | 8 +++---- packages/host/src/providers/index.ts | 2 +- packages/host/src/providers/static.ts | 2 +- scripts/clean-build.mjs | 2 +- scripts/pack-check.mjs | 2 +- test/e2e/packages.test.ts | 6 ++--- test/unit/actions.test.ts | 4 ++-- test/unit/broker-lifecycle.test.ts | 4 ++-- test/unit/broker.test.ts | 10 ++++---- test/unit/commands.test.ts | 6 ++--- test/unit/gh-command.test.ts | 2 +- test/unit/github-app.test.ts | 6 ++--- test/unit/helper.test.ts | 2 +- test/unit/host-opener.test.ts | 2 +- test/unit/host-request.test.ts | 2 +- test/unit/platform.test.ts | 2 +- test/unit/policy.test.ts | 2 +- test/unit/socket-protocol.test.ts | 4 ++-- tsconfig.json | 2 +- yarn.lock | 16 ++++++------- 68 files changed, 112 insertions(+), 112 deletions(-) rename packages/{cli => core}/package.json (97%) rename packages/{cli => core}/src/actions.ts (100%) rename packages/{cli => core}/src/errors.ts (100%) rename packages/{cli => core}/src/fingerprint.ts (100%) rename packages/{cli => core}/src/host-request.ts (100%) rename packages/{cli => core}/src/index.ts (100%) rename packages/{cli => core}/src/paths.ts (100%) rename packages/{cli => core}/src/platform.ts (100%) rename packages/{cli => core}/src/socket-protocol.ts (100%) rename packages/{cli => core}/src/support.ts (100%) rename packages/{cli => core}/src/types.ts (100%) rename packages/{cli => core}/src/version.ts (100%) rename packages/{cli => core}/tsconfig.build.json (100%) rename packages/{cli => core}/tsconfig.json (100%) diff --git a/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md b/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md index ece483bf..3b020e76 100644 --- a/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md +++ b/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md @@ -1,4 +1,4 @@ -# Three packages: `gcb-cli`, `gcb-client`, `gcb-host` +# Three packages: `gcb-core`, `gcb-client`, `gcb-host` - **Date:** 2026-10-23 - **Status:** proposed; supersedes the single-package + esbuild-bundle plan @@ -10,19 +10,19 @@ Split the repository into Yarn workspaces under `packages/`: ```text packages/ - cli/ gcb-cli shared CLI runtime, argument parsing, protocol, types - client/ gcb-client container side; depends on gcb-cli - host/ gcb-host host side; depends on gcb-cli and yaml + cli/ gcb-core shared CLI runtime, argument parsing, protocol, types + client/ gcb-client container side; depends on gcb-core + host/ gcb-host host side; depends on gcb-core and yaml ``` Dependency graph: ```text -gcb-client ──> gcb-cli -gcb-host ──> gcb-cli, yaml +gcb-client ──> gcb-core +gcb-host ──> gcb-core, yaml ``` -There is no dependency from `gcb-cli` to either side, and no dependency from +There is no dependency from `gcb-core` to either side, and no dependency from `gcb-client` to `gcb-host`. The container installs only `gcb-client`; the host installs only `gcb-host`. Both top-level packages expose the same `gcb` bin, so the external command is still one name. @@ -31,7 +31,7 @@ name. - npm installs peers in ways that are easy to get wrong; a package boundary is enforced by the dependency graph instead of by documentation. -- `gcb-cli` is a base dependency, not a dispatcher that owns both backends. It must not be able to +- `gcb-core` is a base dependency, not a dispatcher that owns both backends. It must not be able to load `yaml`. - A missing `yaml` must be impossible on a host and irrelevant in a container, not a runtime surprise. @@ -39,10 +39,10 @@ name. ## Bin ownership `gcb-client` and `gcb-host` each declare `"bin": { "gcb": "./bin/gcb.js" }`. The bin is a thin -wrapper: it imports the shared runner from `gcb-cli`, passes its own command backend, and exits with +wrapper: it imports the shared runner from `gcb-core`, passes its own command backend, and exits with the result. -`gcb-cli` may carry a development bin, but it is not the published entry point. Relying on npm to +`gcb-core` may carry a development bin, but it is not the published entry point. Relying on npm to link a dependency's bin into the global prefix is not something to build a release on. Only one of the two top-level packages should be installed in a given environment. Installing both @@ -53,7 +53,7 @@ globally would race for the same `gcb` link; that is documented rather than work - `gcb-client` (implemented): `git-credential-*`, `setup`, `probe`, `logs`, `pr`. - `gcb-host` (implemented): `app`, `host`, `daemon`, `init`, `stage`, `compose`, `diagnose`, `authorize`, `host-opener`. -- `gcb-cli`: help/version, argument parsing, command lookup, error formatting, and the shared +- `gcb-core`: help/version, argument parsing, command lookup, error formatting, and the shared wire/types. `gh` and `config` are roadmap commands, not implemented yet. When `gcb gh` lands it will bypass the @@ -73,7 +73,7 @@ self-contained artifact. That is a separate decision from this package split. - Root is private; `workspaces: ["packages/*"]`. - Each package has its own `package.json`, `tsconfig.json`, `src/`, `dist/`, and `files`. -- Versions stay in lockstep; `gcb-client` and `gcb-host` depend on the exact `gcb-cli` version. +- Versions stay in lockstep; `gcb-client` and `gcb-host` depend on the exact `gcb-core` version. - CI adds a real package test: 1. `npm pack` all three; 2. install `gcb-client` into a temporary prefix, assert `gcb --help` and `gcb setup --help` diff --git a/.changeset/config.json b/.changeset/config.json index c7f23f6b..3a467af4 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -11,7 +11,7 @@ "updateInternalDependencies": "patch", "fixed": [ [ - "gcb-cli", + "gcb-core", "gcb-client", "gcb-host" ] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bfa2409..4716c052 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,7 +48,7 @@ jobs: - name: Read the package version id: version - run: echo "version=$(jq -r .version packages/cli/package.json)" >> "$GITHUB_OUTPUT" + run: echo "version=$(jq -r .version packages/core/package.json)" >> "$GITHUB_OUTPUT" - name: Create Release Pull Request or Publish to npm id: changesets diff --git a/AGENTS.md b/AGENTS.md index 88f06558..a4331463 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,7 +79,7 @@ sources must stay erasable (`erasableSyntaxOnly` is on: no enums, no parameter p `setup` cannot know the PATH of a later git process; do not pretend a setup-time check is a guarantee. If a review finding depends on an unsupported shape, answer it in the PR and leave the code alone. -- **The gh action catalog is the one source for an action.** `packages/cli/src/actions.ts` +- **The gh action catalog is the one source for an action.** `packages/core/src/actions.ts` defines its command, flags, positionals, permissions and timeout; the token mint, the creation preflight, the command table and the docs must read it rather than keep a second list. The broker rebuilds a canonical argv from validated values and never forwards the caller’s argv. Add an @@ -310,7 +310,7 @@ Full detail and the measurements behind them: | Path | What | |---|---| -| `packages/cli/src/` | shared parser, help/version, protocol, types, platform, paths and the gh action catalog | +| `packages/core/src/` | shared parser, help/version, protocol, types, platform, paths and the gh action catalog | | `packages/client/src/helper.ts` | the container-side credential helper | | `packages/client/src/commands/` | setup, probe, logs, pr, gh and remotes | | `packages/host/src/broker.ts` | unix socket server, refusal codes, audit records | diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cd26a045..15a40250 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -78,8 +78,8 @@ As the commands a user of the package gets: ```sh mkdir -p /tmp/gcb-packs -npm pack ./packages/cli ./packages/host --pack-destination /tmp/gcb-packs -npm install -g /tmp/gcb-packs/gcb-cli-*.tgz /tmp/gcb-packs/gcb-host-*.tgz +npm pack ./packages/core ./packages/host --pack-destination /tmp/gcb-packs +npm install -g /tmp/gcb-packs/gcb-core-*.tgz /tmp/gcb-packs/gcb-host-*.tgz gcb --version ``` @@ -140,7 +140,7 @@ If you add tests, three traps are already paid for in this suite: ```sh corepack yarn pack:check # shows exactly what each of the three packages would ship -npm pack ./packages/cli # the tarball itself +npm pack ./packages/core # the tarball itself npm pack ./packages/client npm pack ./packages/host ``` @@ -169,7 +169,7 @@ The release workflow uses npm trusted publishing (OIDC): `id-token: write` is gr ## Layout ``` -packages/cli/src/ parser, help/version, protocol, types, platform and paths +packages/core/src/ parser, help/version, protocol, types, platform and paths packages/client/src/ credential helper, setup, probe, logs and pr packages/host/src/ YAML config, providers, broker, daemon and host commands packages/*/bin/gcb.js the two installed bins (client and host) diff --git a/Dockerfile b/Dockerfile index 1275574e..8e851ecc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,11 +17,11 @@ COPY scripts ./scripts RUN corepack yarn install --immutable \ && corepack yarn build \ && mkdir -p /packs \ - && npm pack ./packages/cli ./packages/client ./packages/host --pack-destination /packs + && npm pack ./packages/core ./packages/client ./packages/host --pack-destination /packs FROM node:24-slim AS client COPY --from=build /packs /packs -RUN npm install -g --omit=dev /packs/gcb-cli-*.tgz /packs/gcb-client-*.tgz \ +RUN npm install -g --omit=dev /packs/gcb-core-*.tgz /packs/gcb-client-*.tgz \ && rm -rf /packs /root/.npm /tmp/* \ && find /usr/local/lib/node_modules/gcb-*/lib -type f -name '*.map' -delete \ && npm cache clean --force @@ -50,7 +50,7 @@ RUN --mount=type=cache,id=apt_cache,target=/var/cache/apt,sharing=locked \ FROM host-base AS host COPY --from=build /packs /packs -RUN npm install -g --omit=dev /packs/gcb-cli-*.tgz /packs/gcb-host-*.tgz \ +RUN npm install -g --omit=dev /packs/gcb-core-*.tgz /packs/gcb-host-*.tgz \ && rm -rf /packs /root/.npm /tmp/* \ && find /usr/local/lib/node_modules/gcb-*/lib -type f -name '*.map' -delete \ && npm cache clean --force diff --git a/README.md b/README.md index b9aa5c11..a7c82415 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Inside a checkout, `--repo` can be omitted only when `origin` names a usable Git host matches `--host`; otherwise pass `--repo`, or the matching `--host`. The exact command, flags and required values come from -[`packages/cli/src/actions.ts`](packages/cli/src/actions.ts); `gcb gh --help` renders that catalog. +[`packages/core/src/actions.ts`](packages/core/src/actions.ts); `gcb gh --help` renders that catalog. ## Set up the GitHub App first @@ -146,7 +146,7 @@ The exact command, flags and required values come from should open pull requests, **Actions: Read** for `logs`, and **Checks: Read** / **Statuses: Read** / **Issues: Read and write** for the corresponding `gcb gh` actions. Grant them in the app, not only in the request. The per-action source of truth is the catalog in - [`packages/cli/src/actions.ts`](packages/cli/src/actions.ts). + [`packages/core/src/actions.ts`](packages/core/src/actions.ts). 3. Install the app on the account that owns the repositories, granting access to those alone. 4. Download the **private key**; note the **App ID** and **Client ID**. 5. Ignore the client secret — this tool never uses it. Installation tokens are obtained by signing a @@ -578,7 +578,7 @@ this working, and the traps it already paid for, are in [AGENTS.md](AGENTS.md). ## Layout ``` -packages/cli/src/ shared parser, protocol, types, platform helpers and the gh action catalog +packages/core/src/ shared parser, protocol, types, platform helpers and the gh action catalog packages/client/src/ credential helper, setup, probe, logs, pr, gh packages/host/src/ YAML config, providers, broker, daemon and host commands packages/*/bin/ the two installed gcb bins (client and host) @@ -634,7 +634,7 @@ ARG GCB_CLIENT_IMAGE=code-vaults/gcb-client:dev FROM ${GCB_CLIENT_IMAGE} AS gcb-client FROM ${BASE} -COPY --from=gcb-client /usr/local/lib/node_modules/gcb-cli /usr/local/lib/node_modules/gcb-cli +COPY --from=gcb-client /usr/local/lib/node_modules/gcb-core /usr/local/lib/node_modules/gcb-core COPY --from=gcb-client /usr/local/lib/node_modules/gcb-client /usr/local/lib/node_modules/gcb-client RUN ln -s /usr/local/lib/node_modules/gcb-client/bin/gcb.js /usr/local/bin/gcb ``` diff --git a/ROADMAP.md b/ROADMAP.md index f691d75c..b538a3e1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -34,7 +34,7 @@ host-user delegation/live reload are not implemented. not a boundary. - **Configuration becomes human-editable** (YAML, see Phase 1), with groups and globs. `yaml` is a dependency of the host package, not a bundled artifact. -- **Three packages, one command.** `gcb-cli` is the shared runtime; `gcb-client` and `gcb-host` are the +- **Three packages, one command.** `gcb-core` is the shared runtime; `gcb-client` and `gcb-host` are the installed entry points and both expose `gcb`. The container never installs host dependencies. - **The dsh approval plugin lives in `../idsh`.** This repository only carries its roadmap item and the container-facing skill. @@ -92,9 +92,9 @@ The configuration is the source of truth and must be comfortable to edit by hand - [x] Use YAML. `yaml` is the parser: `parseDocument` preserves comments and formatting, it is pure JavaScript, and the Document API is what lets `init` update one value without rewriting the file. - [x] Split the repository into Yarn workspaces under `packages/`: - - `gcb-cli`: shared argument parsing, help, protocol and types; no `yaml`; - - `gcb-client`: container side; depends on `gcb-cli`; no host dependencies; - - `gcb-host`: host side; depends on `gcb-cli` and `yaml`; + - `gcb-core`: shared argument parsing, help, protocol and types; no `yaml`; + - `gcb-client`: container side; depends on `gcb-core`; no host dependencies; + - `gcb-host`: host side; depends on `gcb-core` and `yaml`; - both client and host expose the same `gcb` bin; only one is installed per environment. - [x] Do **not** bundle. npm installs dependencies; remove esbuild and the staged bundle build. `stage`, if kept, exports a commit and does not build a self-contained artifact. diff --git a/package.json b/package.json index cdc47a79..1b90ce04 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "gcb-workspace", "version": "0.0.0", "private": true, - "description": "Workspace for gcb-cli, gcb-client and gcb-host.", + "description": "Workspace for gcb-core, gcb-client and gcb-host.", "license": "MIT", "author": "JounQin ", "homepage": "https://github.com/code-vaults/git-credential-broker#readme", @@ -22,7 +22,7 @@ "packages/*" ], "scripts": { - "build": "node scripts/clean-build.mjs && tsc -p packages/cli/tsconfig.build.json && tsc -p packages/client/tsconfig.build.json && tsc -p packages/host/tsconfig.build.json && node scripts/postbuild.mjs", + "build": "node scripts/clean-build.mjs && tsc -p packages/core/tsconfig.build.json && tsc -p packages/client/tsconfig.build.json && tsc -p packages/host/tsconfig.build.json && node scripts/postbuild.mjs", "typecheck": "tsc -p tsconfig.json", "test": "yarn build && node --conditions=development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts test/e2e/*.test.ts", "test:unit": "node --conditions=development --disable-warning=ExperimentalWarning --test test/unit/*.test.ts", diff --git a/packages/client/bin/gcb.js b/packages/client/bin/gcb.js index 77064999..e17f8be2 100644 --- a/packages/client/bin/gcb.js +++ b/packages/client/bin/gcb.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { runCli } from 'gcb-cli'; +import { runCli } from 'gcb-core'; import { clientBackend, runHelper } from 'gcb-client'; const argv = process.argv.slice(2); diff --git a/packages/client/package.json b/packages/client/package.json index 64e75e32..3fe0a002 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -28,7 +28,7 @@ } }, "dependencies": { - "gcb-cli": "0.0.0" + "gcb-core": "0.0.0" }, "publishConfig": { "access": "public", diff --git a/packages/client/src/commands/gh.ts b/packages/client/src/commands/gh.ts index f1a39735..ec4a2b52 100644 --- a/packages/client/src/commands/gh.ts +++ b/packages/client/src/commands/gh.ts @@ -4,7 +4,7 @@ * The action catalog defines the command, flags, positionals and permissions. The client only * parses names and values; the broker rebuilds the canonical argv and runs gh with its own token. */ -import { GH_ACTIONS, getGhAction, parseArgs, say, fail, type ActionValues } from 'gcb-cli'; +import { GH_ACTIONS, getGhAction, parseArgs, say, fail, type ActionValues } from 'gcb-core'; import { requestOverSocket, resolveSocketPath } from '../helper.ts'; import { remoteRepo } from './remotes.ts'; diff --git a/packages/client/src/commands/logs.ts b/packages/client/src/commands/logs.ts index 7b4bc287..16bad269 100644 --- a/packages/client/src/commands/logs.ts +++ b/packages/client/src/commands/logs.ts @@ -8,7 +8,7 @@ * Exit codes: 0 read, 1 refused, 3 broker unreachable. */ import { requestOverSocket, resolveSocketPath } from '../helper.ts'; -import { fail, parseArgs, say } from 'gcb-cli'; +import { fail, parseArgs, say } from 'gcb-core'; /** * How long to wait for the broker. diff --git a/packages/client/src/commands/pr.ts b/packages/client/src/commands/pr.ts index ec3d1956..fb5471f4 100644 --- a/packages/client/src/commands/pr.ts +++ b/packages/client/src/commands/pr.ts @@ -21,11 +21,11 @@ import { removeResult, waitForResult, writeRequest, -} from 'gcb-cli'; +} from 'gcb-core'; import { requestOverSocket, resolveSocketPath } from '../helper.ts'; import { baseBranchFor, qualifiedBranch, remoteRepo } from './remotes.ts'; import { runGh } from './gh.ts'; -import { fail, parseArgs, say } from 'gcb-cli'; +import { fail, parseArgs, say } from 'gcb-core'; /** Every flag the flag-only lifecycle understands. */ const KNOWN_FLAGS = new Set([ diff --git a/packages/client/src/commands/probe.ts b/packages/client/src/commands/probe.ts index 8c0b286e..1ab911b9 100644 --- a/packages/client/src/commands/probe.ts +++ b/packages/client/src/commands/probe.ts @@ -5,9 +5,9 @@ * reports the decision with the credential reduced to a fingerprint, so the output is safe to * paste anywhere. Exit codes: 0 allowed, 1 denied, 3 broker unreachable. */ -import { tokenFingerprint } from 'gcb-cli'; +import { tokenFingerprint } from 'gcb-core'; import { requestOverSocket, resolveSocketPath } from '../helper.ts'; -import { fail, parseArgs, say } from 'gcb-cli'; +import { fail, parseArgs, say } from 'gcb-core'; /** Usage text for `probe`. */ export const PROBE_USAGE = `Usage: gcb probe [options] diff --git a/packages/client/src/commands/setup.ts b/packages/client/src/commands/setup.ts index 4db4f9fe..abdb35a8 100644 --- a/packages/client/src/commands/setup.ts +++ b/packages/client/src/commands/setup.ts @@ -32,7 +32,7 @@ import { warn, writeNodeCaBundle, writeSecretFile, -} from 'gcb-cli'; +} from 'gcb-core'; /** Everything `setup` needs, resolved. */ export interface SetupInput { diff --git a/packages/client/src/helper.ts b/packages/client/src/helper.ts index 77a73231..526ed989 100644 --- a/packages/client/src/helper.ts +++ b/packages/client/src/helper.ts @@ -35,8 +35,8 @@ import { homedir } from 'node:os'; import path from 'node:path'; import type { Readable, Writable } from 'node:stream'; -import { asWireResponse, encodeMessage, parseMessage } from 'gcb-cli'; -import type { WireResponse } from 'gcb-cli'; +import { asWireResponse, encodeMessage, parseMessage } from 'gcb-core'; +import type { WireResponse } from 'gcb-core'; /** Ceiling for the description git sends us. */ const MAX_INPUT_BYTES = 8 * 1024; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 39e730ca..8a388511 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -3,8 +3,8 @@ * * The host commands are not imported here; installing `gcb-client` never pulls `yaml`. */ -import { GH_ACTIONS } from 'gcb-cli'; -import type { CommandBackend } from 'gcb-cli'; +import { GH_ACTIONS } from 'gcb-core'; +import type { CommandBackend } from 'gcb-core'; export { runHelper } from './helper.ts'; diff --git a/packages/cli/package.json b/packages/core/package.json similarity index 97% rename from packages/cli/package.json rename to packages/core/package.json index fd09ef05..dcda35ce 100644 --- a/packages/cli/package.json +++ b/packages/core/package.json @@ -1,5 +1,5 @@ { - "name": "gcb-cli", + "name": "gcb-core", "version": "0.0.0", "description": "Shared command runtime and protocol for gcb-client and gcb-host.", "license": "MIT", diff --git a/packages/cli/src/actions.ts b/packages/core/src/actions.ts similarity index 100% rename from packages/cli/src/actions.ts rename to packages/core/src/actions.ts diff --git a/packages/cli/src/errors.ts b/packages/core/src/errors.ts similarity index 100% rename from packages/cli/src/errors.ts rename to packages/core/src/errors.ts diff --git a/packages/cli/src/fingerprint.ts b/packages/core/src/fingerprint.ts similarity index 100% rename from packages/cli/src/fingerprint.ts rename to packages/core/src/fingerprint.ts diff --git a/packages/cli/src/host-request.ts b/packages/core/src/host-request.ts similarity index 100% rename from packages/cli/src/host-request.ts rename to packages/core/src/host-request.ts diff --git a/packages/cli/src/index.ts b/packages/core/src/index.ts similarity index 100% rename from packages/cli/src/index.ts rename to packages/core/src/index.ts diff --git a/packages/cli/src/paths.ts b/packages/core/src/paths.ts similarity index 100% rename from packages/cli/src/paths.ts rename to packages/core/src/paths.ts diff --git a/packages/cli/src/platform.ts b/packages/core/src/platform.ts similarity index 100% rename from packages/cli/src/platform.ts rename to packages/core/src/platform.ts diff --git a/packages/cli/src/socket-protocol.ts b/packages/core/src/socket-protocol.ts similarity index 100% rename from packages/cli/src/socket-protocol.ts rename to packages/core/src/socket-protocol.ts diff --git a/packages/cli/src/support.ts b/packages/core/src/support.ts similarity index 100% rename from packages/cli/src/support.ts rename to packages/core/src/support.ts diff --git a/packages/cli/src/types.ts b/packages/core/src/types.ts similarity index 100% rename from packages/cli/src/types.ts rename to packages/core/src/types.ts diff --git a/packages/cli/src/version.ts b/packages/core/src/version.ts similarity index 100% rename from packages/cli/src/version.ts rename to packages/core/src/version.ts diff --git a/packages/cli/tsconfig.build.json b/packages/core/tsconfig.build.json similarity index 100% rename from packages/cli/tsconfig.build.json rename to packages/core/tsconfig.build.json diff --git a/packages/cli/tsconfig.json b/packages/core/tsconfig.json similarity index 100% rename from packages/cli/tsconfig.json rename to packages/core/tsconfig.json diff --git a/packages/host/bin/gcb.js b/packages/host/bin/gcb.js index 7c09900e..8d1834fe 100644 --- a/packages/host/bin/gcb.js +++ b/packages/host/bin/gcb.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { runCli } from 'gcb-cli'; +import { runCli } from 'gcb-core'; import { hostBackend } from 'gcb-host'; process.exitCode = await runCli(process.argv.slice(2), hostBackend); diff --git a/packages/host/package.json b/packages/host/package.json index 1f0844bb..9f2167af 100644 --- a/packages/host/package.json +++ b/packages/host/package.json @@ -28,7 +28,7 @@ } }, "dependencies": { - "gcb-cli": "0.0.0", + "gcb-core": "0.0.0", "node-fetch-native": "^1.6.7", "x-fetch": "^0.3.0", "yaml": "^2.9.1" diff --git a/packages/host/src/audit.ts b/packages/host/src/audit.ts index 15cb5cbb..ea235a2e 100644 --- a/packages/host/src/audit.ts +++ b/packages/host/src/audit.ts @@ -10,7 +10,7 @@ import { chmodSync, closeSync, createWriteStream, mkdirSync, openSync } from 'no import type { WriteStream } from 'node:fs'; import { dirname } from 'node:path'; -import type { AuditSink } from 'gcb-cli'; +import type { AuditSink } from 'gcb-core'; /** * Keys whose values must never reach the audit log. diff --git a/packages/host/src/broker.ts b/packages/host/src/broker.ts index c8297285..4dfe3d3a 100644 --- a/packages/host/src/broker.ts +++ b/packages/host/src/broker.ts @@ -34,12 +34,12 @@ import { parseMessage, socketPathRefusal, tokenFingerprint, -} from 'gcb-cli'; +} from 'gcb-core'; import net from 'node:net'; import path from 'node:path'; import { hostConfig, normalizeRepoPath, repoAllowed } from './policy.ts'; -import type { ActionValues, AuditSink, BrokerConfig, Provider, WireResponse } from 'gcb-cli'; +import type { ActionValues, AuditSink, BrokerConfig, Provider, WireResponse } from 'gcb-core'; /** Reported by the `ping` operation so the container side can prove what it reached. */ export const BROKER_VERSION = PACKAGE_VERSION; diff --git a/packages/host/src/commands/authorize.ts b/packages/host/src/commands/authorize.ts index b1e65ee8..9c433e22 100644 --- a/packages/host/src/commands/authorize.ts +++ b/packages/host/src/commands/authorize.ts @@ -19,7 +19,7 @@ import { dirname, join, resolve as resolvePath } from 'node:path'; import { USER_TOKEN_FILE, userTokenPath, loadConfig, resolveConfigPath } from '../config.ts'; import { collectUserToken, startDeviceFlow, type Fetcher } from '../device-flow.ts'; import { fetcherFor, parseProxy } from '../http.ts'; -import { fail, insideMountedPath, isInsideContainer, parseArgs, say, writeSecretFile } from 'gcb-cli'; +import { fail, insideMountedPath, isInsideContainer, parseArgs, say, writeSecretFile } from 'gcb-core'; /** Usage text for `authorize`. */ export const AUTHORIZE_USAGE = `Usage: gcb authorize [options] diff --git a/packages/host/src/commands/compose.ts b/packages/host/src/commands/compose.ts index 416770ff..5f4bef30 100644 --- a/packages/host/src/commands/compose.ts +++ b/packages/host/src/commands/compose.ts @@ -13,8 +13,8 @@ import fs from 'node:fs'; import path from 'node:path'; import { inferMode, loadConfig, resolveConfigPath, deploymentDir, SIDECAR } from '../config.ts'; -import { PACKAGE_VERSION } from 'gcb-cli'; -import { fail, parseArgs, say, warn } from 'gcb-cli'; +import { PACKAGE_VERSION } from 'gcb-core'; +import { fail, parseArgs, say, warn } from 'gcb-core'; import { USER_TOKEN_FILE } from '../config.ts'; /** Everything `compose` needs, resolved. */ diff --git a/packages/host/src/commands/diagnose.ts b/packages/host/src/commands/diagnose.ts index 66be0439..e5e1d53f 100644 --- a/packages/host/src/commands/diagnose.ts +++ b/packages/host/src/commands/diagnose.ts @@ -18,8 +18,8 @@ import { readFileSync } from 'node:fs'; import { assertKeysReadable, loadConfig, resolveConfigPath } from '../config.ts'; import { nextLink, signAppJwt } from '../providers/github-app.ts'; import { fetcherFor, parseProxy } from '../http.ts'; -import type { GithubAppHostConfig } from 'gcb-cli'; -import { fail, parseArgs, say, warn } from 'gcb-cli'; +import type { GithubAppHostConfig } from 'gcb-core'; +import { fail, parseArgs, say, warn } from 'gcb-core'; /** One installation, as far as this command cares. */ interface Installation { diff --git a/packages/host/src/commands/host-opener.ts b/packages/host/src/commands/host-opener.ts index d87ac6a1..d4d883ab 100644 --- a/packages/host/src/commands/host-opener.ts +++ b/packages/host/src/commands/host-opener.ts @@ -29,8 +29,8 @@ import { writeResult, type HostOpenRequest, type HostOpenResult, -} from 'gcb-cli'; -import { fail, listFlag, parseArgs, say, warn } from 'gcb-cli'; +} from 'gcb-core'; +import { fail, listFlag, parseArgs, say, warn } from 'gcb-core'; import { applyProxyChoice, parseProxy, type ProxyChoice } from '../http.ts'; const run = promisify(execFile); diff --git a/packages/host/src/commands/init.ts b/packages/host/src/commands/init.ts index 3eb9a7ba..70b81acb 100644 --- a/packages/host/src/commands/init.ts +++ b/packages/host/src/commands/init.ts @@ -28,7 +28,7 @@ import { type DeploymentMode, } from '../config.ts'; import { parseAllowEntry } from '../policy.ts'; -import type { BrokerConfig, GithubAppHostConfig } from 'gcb-cli'; +import type { BrokerConfig, GithubAppHostConfig } from 'gcb-core'; import { brokerPaths, DEPLOYMENT_MODES, @@ -37,7 +37,7 @@ import { requireFixedInSidecar, } from './deployment.ts'; import type { BrokerPaths } from './deployment.ts'; -import { fail, insideMountedPath, isInsideContainer, parseArgs, say, writeSecretFile } from 'gcb-cli'; +import { fail, insideMountedPath, isInsideContainer, parseArgs, say, writeSecretFile } from 'gcb-core'; /** The environment `init` reasons about, injectable so the guard rails can be tested. */ export interface InitEnvironment { diff --git a/packages/host/src/commands/stage.ts b/packages/host/src/commands/stage.ts index bfd28ef2..26288d4f 100644 --- a/packages/host/src/commands/stage.ts +++ b/packages/host/src/commands/stage.ts @@ -21,8 +21,8 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { canonicalPath } from 'gcb-cli'; -import { fail, insideMountedPath, parseArgs, say, warn, writeSecretFile } from 'gcb-cli'; +import { canonicalPath } from 'gcb-core'; +import { fail, insideMountedPath, parseArgs, say, warn, writeSecretFile } from 'gcb-core'; /** The record `stage` leaves behind, so the running code is identifiable afterwards. */ export const STAGED_RECORD = 'STAGED.json'; diff --git a/packages/host/src/config.ts b/packages/host/src/config.ts index 1f16db9e..eeaea59b 100644 --- a/packages/host/src/config.ts +++ b/packages/host/src/config.ts @@ -9,7 +9,7 @@ import path, { posix } from 'node:path'; import { parseDocument } from 'yaml'; import { parseAllowEntry } from './policy.ts'; -import type { BrokerConfig, GithubAppHostConfig, HostConfig, StaticHostConfig } from 'gcb-cli'; +import type { BrokerConfig, GithubAppHostConfig, HostConfig, StaticHostConfig } from 'gcb-core'; /** The file every command looks for when no configuration path is given. */ export const DEFAULT_CONFIG_FILE = 'broker.config.yml'; diff --git a/packages/host/src/daemon.ts b/packages/host/src/daemon.ts index 67a40508..f7a8f185 100644 --- a/packages/host/src/daemon.ts +++ b/packages/host/src/daemon.ts @@ -14,7 +14,7 @@ import { startBroker } from './broker.ts'; import { assertKeysReadable, assertUserTokensReadable, loadConfig, resolveConfigPath } from './config.ts'; import { createProvider } from './providers/index.ts'; import type { ProviderDeps } from './providers/index.ts'; -import type { BrokerConfig, Provider } from 'gcb-cli'; +import type { BrokerConfig, Provider } from 'gcb-core'; /** Usage text. */ const USAGE = diff --git a/packages/host/src/gh.ts b/packages/host/src/gh.ts index 9cfaff6f..c95acc49 100644 --- a/packages/host/src/gh.ts +++ b/packages/host/src/gh.ts @@ -11,7 +11,7 @@ import { tmpdir } from 'node:os'; import { StringDecoder } from 'node:string_decoder'; import path from 'node:path'; -import { buildGhArgv, getGhAction, type ActionValues } from 'gcb-cli'; +import { buildGhArgv, getGhAction, type ActionValues } from 'gcb-core'; import { applyProxyChoice, PROXY_ENV_KEYS } from './http.ts'; /** The default stdout/stderr ceiling, in bytes per stream. */ diff --git a/packages/host/src/http.ts b/packages/host/src/http.ts index 7d39355e..40268686 100644 --- a/packages/host/src/http.ts +++ b/packages/host/src/http.ts @@ -14,7 +14,7 @@ import https from 'node:https'; import { createProxy } from 'node-fetch-native/proxy'; import { fetch as directFetch } from 'node-fetch-native/node'; import { createXFetch, isXFetchError, type XFetchMiddleware } from 'x-fetch'; -import type { FetchLike } from 'gcb-cli'; +import type { FetchLike } from 'gcb-core'; /** A command's proxy choice: omitted keeps the host environment, null is direct. */ export type ProxyChoice = string | null | undefined; diff --git a/packages/host/src/index.ts b/packages/host/src/index.ts index 45744176..e8caf537 100644 --- a/packages/host/src/index.ts +++ b/packages/host/src/index.ts @@ -3,7 +3,7 @@ * * This is the only side that imports `yaml` (through config.ts) and holds the App key. */ -import type { CommandBackend } from 'gcb-cli'; +import type { CommandBackend } from 'gcb-core'; import { assertProxyEnabled } from './proxy.ts'; export * from './gh.ts'; diff --git a/packages/host/src/policy.ts b/packages/host/src/policy.ts index e6a24362..20f9a7fd 100644 --- a/packages/host/src/policy.ts +++ b/packages/host/src/policy.ts @@ -12,7 +12,7 @@ * - a wildcard is allowed only as a whole segment, never inside one, so a malformed entry * matches nothing and therefore fails closed. */ -import type { BrokerConfig, HostConfig, RepoRef } from 'gcb-cli'; +import type { BrokerConfig, HostConfig, RepoRef } from 'gcb-core'; /** One `owner` or `repo` segment: starts alphanumeric, then letters/digits/._- */ const SEGMENT_RE = /^[a-z0-9][a-z0-9._-]*$/; diff --git a/packages/host/src/providers/github-app.ts b/packages/host/src/providers/github-app.ts index 49c56861..64ee2669 100644 --- a/packages/host/src/providers/github-app.ts +++ b/packages/host/src/providers/github-app.ts @@ -20,15 +20,15 @@ import { userTokenPath } from '../config.ts'; import { currentProxyKey, proxyFetch } from '../http.ts'; import { createSign } from 'node:crypto'; -import { getGhAction, ProviderConfigError, tokenFingerprint, type ActionRunResult } from 'gcb-cli'; +import { getGhAction, ProviderConfigError, tokenFingerprint, type ActionRunResult } from 'gcb-core'; import { runGhAction } from '../gh.ts'; import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { refreshUserToken } from '../device-flow.ts'; -import type { Credential, FetchLike, FetchResponseLike, GithubAppHostConfig, Provider } from 'gcb-cli'; -import type { JobLog, JobLogRequest } from 'gcb-cli'; -import type { PullRequest, PullRequestRequest } from 'gcb-cli'; +import type { Credential, FetchLike, FetchResponseLike, GithubAppHostConfig, Provider } from 'gcb-core'; +import type { JobLog, JobLogRequest } from 'gcb-core'; +import type { PullRequest, PullRequestRequest } from 'gcb-core'; /** REST API origin. Overridable for GitHub Enterprise. */ const DEFAULT_API = 'https://api.github.com'; diff --git a/packages/host/src/providers/index.ts b/packages/host/src/providers/index.ts index f00f47ae..27b0910e 100644 --- a/packages/host/src/providers/index.ts +++ b/packages/host/src/providers/index.ts @@ -5,7 +5,7 @@ * broker, the helper, the socket protocol, the allowlist and the audit trail are all * provider-agnostic. */ -import type { FetchLike, HostConfig, Provider } from 'gcb-cli'; +import type { FetchLike, HostConfig, Provider } from 'gcb-core'; import { createGithubAppProvider } from './github-app.ts'; import { createStaticProvider } from './static.ts'; diff --git a/packages/host/src/providers/static.ts b/packages/host/src/providers/static.ts index e9075eb2..57fa18bf 100644 --- a/packages/host/src/providers/static.ts +++ b/packages/host/src/providers/static.ts @@ -12,7 +12,7 @@ */ import { readFileSync } from 'node:fs'; -import type { Credential, Provider, StaticHostConfig } from 'gcb-cli'; +import type { Credential, Provider, StaticHostConfig } from 'gcb-core'; /** Options for {@link createStaticProvider}. */ export interface StaticProviderOptions { diff --git a/scripts/clean-build.mjs b/scripts/clean-build.mjs index e582918f..715a826b 100644 --- a/scripts/clean-build.mjs +++ b/scripts/clean-build.mjs @@ -3,6 +3,6 @@ import { rmSync } from 'node:fs'; import path from 'node:path'; const root = path.resolve(import.meta.dirname, '..'); -for (const name of ['cli', 'client', 'host']) { +for (const name of ['core', 'client', 'host']) { rmSync(path.join(root, 'packages', name, 'lib'), { recursive: true, force: true }); } diff --git a/scripts/pack-check.mjs b/scripts/pack-check.mjs index c3c5c711..b3dd9e21 100644 --- a/scripts/pack-check.mjs +++ b/scripts/pack-check.mjs @@ -12,7 +12,7 @@ import path from 'node:path'; const root = path.resolve(import.meta.dirname, '..'); const scratch = mkdtempSync(path.join(os.tmpdir(), 'gcb-pack-check-')); try { - for (const name of ['cli', 'client', 'host']) { + for (const name of ['core', 'client', 'host']) { console.log(`\n== packages/${name}`); execFileSync('npm', ['pack', path.join(root, 'packages', name), '--dry-run', '--pack-destination', scratch], { cwd: root, diff --git a/test/e2e/packages.test.ts b/test/e2e/packages.test.ts index 68033244..ad1ee529 100644 --- a/test/e2e/packages.test.ts +++ b/test/e2e/packages.test.ts @@ -71,12 +71,12 @@ function bin(prefix: string): string { describe('packed npm packages', () => { before(() => { fs.mkdirSync(packs, { recursive: true }); - const cli = pack('cli'); + const core = pack('core'); const client = pack('client'); const host = pack('host'); - run('npm', ['install', '-g', '--prefix', clientPrefix, '--no-audit', '--no-fund', cli, client]); - run('npm', ['install', '-g', '--prefix', hostPrefix, '--no-audit', '--no-fund', cli, host]); + run('npm', ['install', '-g', '--prefix', clientPrefix, '--no-audit', '--no-fund', core, client]); + run('npm', ['install', '-g', '--prefix', hostPrefix, '--no-audit', '--no-fund', core, host]); }); it('installs a working client without the host parser', () => { diff --git a/test/unit/actions.test.ts b/test/unit/actions.test.ts index 305272d5..4a8ea1d1 100644 --- a/test/unit/actions.test.ts +++ b/test/unit/actions.test.ts @@ -7,8 +7,8 @@ import { GH_ACTIONS, buildGhArgv, getGhAction, -} from '../../packages/cli/src/actions.ts'; -import { MAX_BODY_CHARS, MAX_TITLE_CHARS } from '../../packages/cli/src/host-request.ts'; +} from '../../packages/core/src/actions.ts'; +import { MAX_BODY_CHARS, MAX_TITLE_CHARS } from '../../packages/core/src/host-request.ts'; describe('gh action catalog', () => { it('has the roadmap actions and no merge action', () => { diff --git a/test/unit/broker-lifecycle.test.ts b/test/unit/broker-lifecycle.test.ts index cc9d5d86..d36fec78 100644 --- a/test/unit/broker-lifecycle.test.ts +++ b/test/unit/broker-lifecycle.test.ts @@ -6,9 +6,9 @@ import path from 'node:path'; import { describe, it } from 'node:test'; import { CODES, startBroker } from '../../packages/host/src/broker.ts'; -import { MAX_MESSAGE_BYTES } from '../../packages/cli/src/socket-protocol.ts'; +import { MAX_MESSAGE_BYTES } from '../../packages/core/src/socket-protocol.ts'; import { createStaticProvider } from '../../packages/host/src/providers/static.ts'; -import type { AuditSink, BrokerConfig, Provider, StaticHostConfig, WireResponse } from '../../packages/cli/src/types.ts'; +import type { AuditSink, BrokerConfig, Provider, StaticHostConfig, WireResponse } from '../../packages/core/src/types.ts'; const HOST = 'github.com'; diff --git a/test/unit/broker.test.ts b/test/unit/broker.test.ts index 105edda6..34ed11e7 100644 --- a/test/unit/broker.test.ts +++ b/test/unit/broker.test.ts @@ -12,13 +12,13 @@ import path from 'node:path'; import { after, describe, it } from 'node:test'; import { redact } from '../../packages/host/src/audit.ts'; -import { tokenFingerprint } from '../../packages/cli/src/fingerprint.ts'; -import { PACKAGE_VERSION } from '../../packages/cli/src/version.ts'; +import { tokenFingerprint } from '../../packages/core/src/fingerprint.ts'; +import { PACKAGE_VERSION } from '../../packages/core/src/version.ts'; import { CODES, createRequestHandler, prepareSocketPath } from '../../packages/host/src/broker.ts'; -import { MAX_BODY_CHARS } from '../../packages/cli/src/host-request.ts'; +import { MAX_BODY_CHARS } from '../../packages/core/src/host-request.ts'; import { createStaticProvider } from '../../packages/host/src/providers/static.ts'; -import type { AuditSink, BrokerConfig, Provider, StaticHostConfig, WireResponse } from '../../packages/cli/src/types.ts'; -import { ProviderConfigError } from '../../packages/cli/src/errors.ts'; +import type { AuditSink, BrokerConfig, Provider, StaticHostConfig, WireResponse } from '../../packages/core/src/types.ts'; +import { ProviderConfigError } from '../../packages/core/src/errors.ts'; const HOST = 'github.com'; const SECRET = 'unit-secret-value'; diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 22e89675..7e7b70e9 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -19,11 +19,11 @@ import { renderCompose, runCompose } from '../../packages/host/src/commands/comp import { runDiagnose } from '../../packages/host/src/commands/diagnose.ts'; import { SIDECAR, loadConfig, resolveConfigPath, deploymentDir } from '../../packages/host/src/config.ts'; import { appBlock, buildBrokerConfig, mergeAllowList, parsePermissions, performInit, resolveInitSelection } from '../../packages/host/src/commands/init.ts'; -import { parseArgs, listFlag, insideMountedPath, mountedPaths } from '../../packages/cli/src/support.ts'; +import { parseArgs, listFlag, insideMountedPath, mountedPaths } from '../../packages/core/src/support.ts'; import { performSetup } from '../../packages/client/src/commands/setup.ts'; -import type { BrokerConfig, GithubAppHostConfig } from '../../packages/cli/src/types.ts'; +import type { BrokerConfig, GithubAppHostConfig } from '../../packages/core/src/types.ts'; import { runProbe } from '../../packages/client/src/commands/probe.ts'; -import { encodeMessage, parseMessage } from '../../packages/cli/src/socket-protocol.ts'; +import { encodeMessage, parseMessage } from '../../packages/core/src/socket-protocol.ts'; import { runAsync } from '../common/run.ts'; /** Scratch space for the whole file. */ diff --git a/test/unit/gh-command.test.ts b/test/unit/gh-command.test.ts index 963ecc15..9d6ad85f 100644 --- a/test/unit/gh-command.test.ts +++ b/test/unit/gh-command.test.ts @@ -8,7 +8,7 @@ import { describe, it } from 'node:test'; import { startBroker } from '../../packages/host/src/broker.ts'; import { tryRunAsync, type RunResult } from '../common/run.ts'; -import type { AuditSink, BrokerConfig, Provider, StaticHostConfig } from '../../packages/cli/src/types.ts'; +import type { AuditSink, BrokerConfig, Provider, StaticHostConfig } from '../../packages/core/src/types.ts'; /** Run the client from source, next to this file rather than from the cwd. */ const CLIENT_BIN = fileURLToPath(new URL('../../packages/client/bin/gcb.js', import.meta.url)); diff --git a/test/unit/github-app.test.ts b/test/unit/github-app.test.ts index a79357dc..841bf527 100644 --- a/test/unit/github-app.test.ts +++ b/test/unit/github-app.test.ts @@ -33,9 +33,9 @@ import { describe, it } from 'node:test'; import { createGithubAppProvider, nextLink, signAppJwt } from '../../packages/host/src/providers/github-app.ts'; import { createProvider } from '../../packages/host/src/providers/index.ts'; -import { tokenFingerprint } from '../../packages/cli/src/fingerprint.ts'; -import type { FetchLike, FetchResponseLike, GithubAppHostConfig } from '../../packages/cli/src/types.ts'; -import { ProviderConfigError } from '../../packages/cli/src/errors.ts'; +import { tokenFingerprint } from '../../packages/core/src/fingerprint.ts'; +import type { FetchLike, FetchResponseLike, GithubAppHostConfig } from '../../packages/core/src/types.ts'; +import { ProviderConfigError } from '../../packages/core/src/errors.ts'; const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); const PRIVATE_PEM = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(); diff --git a/test/unit/helper.test.ts b/test/unit/helper.test.ts index f828ac80..c317587f 100644 --- a/test/unit/helper.test.ts +++ b/test/unit/helper.test.ts @@ -15,7 +15,7 @@ import { Readable, Writable } from 'node:stream'; import { after, describe, it } from 'node:test'; import { parseArgs, parseCredentialInput, requestOverSocket, resolveSocketPath, runHelper } from '../../packages/client/src/helper.ts'; -import type { WireResponse } from '../../packages/cli/src/types.ts'; +import type { WireResponse } from '../../packages/core/src/types.ts'; const CLIENT_REQUEST = 'protocol=https\nhost=github.com\npath=acme/widget.git\n\n'; diff --git a/test/unit/host-opener.test.ts b/test/unit/host-opener.test.ts index 2dc8641f..fa196bf1 100644 --- a/test/unit/host-opener.test.ts +++ b/test/unit/host-opener.test.ts @@ -13,7 +13,7 @@ import { join } from 'node:path'; import { after, before, describe, it } from 'node:test'; import { runHostOpener } from '../../packages/host/src/commands/host-opener.ts'; -import { channelDir, listRequests, readResult, writeRequest } from '../../packages/cli/src/host-request.ts'; +import { channelDir, listRequests, readResult, writeRequest } from '../../packages/core/src/host-request.ts'; describe('the opener, once', () => { let root = ''; diff --git a/test/unit/host-request.test.ts b/test/unit/host-request.test.ts index 2d8e9e94..0f782d75 100644 --- a/test/unit/host-request.test.ts +++ b/test/unit/host-request.test.ts @@ -30,7 +30,7 @@ import { writeOpener, writeRequest, writeResult, -} from '../../packages/cli/src/host-request.ts'; +} from '../../packages/core/src/host-request.ts'; /** A good request, which each test varies one field of. */ const GOOD = { head: 'feat/thing', base: 'main', title: 'a title', body: 'a body' }; diff --git a/test/unit/platform.test.ts b/test/unit/platform.test.ts index c3d8cb7b..62350518 100644 --- a/test/unit/platform.test.ts +++ b/test/unit/platform.test.ts @@ -13,7 +13,7 @@ import path from 'node:path'; import { after, describe, it } from 'node:test'; import { prepareSocketPath } from '../../packages/host/src/broker.ts'; -import { isNativeWindows, isWindowsBackedPath, isWsl, platformRefusal, socketPathRefusal } from '../../packages/cli/src/platform.ts'; +import { isNativeWindows, isWindowsBackedPath, isWsl, platformRefusal, socketPathRefusal } from '../../packages/core/src/platform.ts'; /** Scratch space for the one test that touches the filesystem. */ const scratch = fs.mkdtempSync(path.join(os.tmpdir(), 'gcb-platform-')); diff --git a/test/unit/policy.test.ts b/test/unit/policy.test.ts index ded9b0d7..94923e42 100644 --- a/test/unit/policy.test.ts +++ b/test/unit/policy.test.ts @@ -6,7 +6,7 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { hostConfig, normalizeRepoPath, parseAllowEntry, repoAllowed } from '../../packages/host/src/policy.ts'; -import type { BrokerConfig } from '../../packages/cli/src/types.ts'; +import type { BrokerConfig } from '../../packages/core/src/types.ts'; describe('normalizeRepoPath', () => { it('accepts what git actually sends and strips the .git suffix', () => { diff --git a/test/unit/socket-protocol.test.ts b/test/unit/socket-protocol.test.ts index 4a843e58..69073a12 100644 --- a/test/unit/socket-protocol.test.ts +++ b/test/unit/socket-protocol.test.ts @@ -1,8 +1,8 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { MAX_BODY_CHARS } from '../../packages/cli/src/host-request.ts'; -import { MAX_MESSAGE_BYTES, createLineReader } from '../../packages/cli/src/socket-protocol.ts'; +import { MAX_BODY_CHARS } from '../../packages/core/src/host-request.ts'; +import { MAX_MESSAGE_BYTES, createLineReader } from '../../packages/core/src/socket-protocol.ts'; describe('createLineReader', () => { it('splits lines across chunks and ignores blank lines', () => { diff --git a/tsconfig.json b/tsconfig.json index 312c1237..2210847f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ "noEmit": true, "paths": { - "gcb-cli": ["./packages/cli/src/index.ts"], + "gcb-core": ["./packages/core/src/index.ts"], "gcb-client": ["./packages/client/src/index.ts"], "gcb-host": ["./packages/host/src/index.ts"] } diff --git a/yarn.lock b/yarn.lock index 52edf9bd..1a73d627 100644 --- a/yarn.lock +++ b/yarn.lock @@ -473,27 +473,27 @@ __metadata: languageName: node linkType: hard -"gcb-cli@npm:0.0.0, gcb-cli@workspace:packages/cli": - version: 0.0.0-use.local - resolution: "gcb-cli@workspace:packages/cli" - languageName: unknown - linkType: soft - "gcb-client@workspace:packages/client": version: 0.0.0-use.local resolution: "gcb-client@workspace:packages/client" dependencies: - gcb-cli: "npm:0.0.0" + gcb-core: "npm:0.0.0" bin: gcb: ./bin/gcb.js languageName: unknown linkType: soft +"gcb-core@npm:0.0.0, gcb-core@workspace:packages/core": + version: 0.0.0-use.local + resolution: "gcb-core@workspace:packages/core" + languageName: unknown + linkType: soft + "gcb-host@workspace:packages/host": version: 0.0.0-use.local resolution: "gcb-host@workspace:packages/host" dependencies: - gcb-cli: "npm:0.0.0" + gcb-core: "npm:0.0.0" node-fetch-native: "npm:^1.6.7" x-fetch: "npm:^0.3.0" yaml: "npm:^2.9.1" From bdaea1b20a8c4ba97b935d652c5158bbbc36d52d Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 08:47:27 +0000 Subject: [PATCH 07/12] docs: finish the core package rename in the workspace note --- .../architecture/2026-10-23-three-package-workspaces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md b/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md index 3b020e76..2528e3e1 100644 --- a/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md +++ b/.agents/notes/proposed/architecture/2026-10-23-three-package-workspaces.md @@ -10,7 +10,7 @@ Split the repository into Yarn workspaces under `packages/`: ```text packages/ - cli/ gcb-core shared CLI runtime, argument parsing, protocol, types + core/ gcb-core shared command runtime, argument parsing, protocol, types client/ gcb-client container side; depends on gcb-core host/ gcb-host host side; depends on gcb-core and yaml ``` @@ -72,7 +72,7 @@ self-contained artifact. That is a separate decision from this package split. ## Packaging and tests - Root is private; `workspaces: ["packages/*"]`. -- Each package has its own `package.json`, `tsconfig.json`, `src/`, `dist/`, and `files`. +- Each package has its own `package.json`, `tsconfig.json`, `src/`, `lib/`, and `files`. - Versions stay in lockstep; `gcb-client` and `gcb-host` depend on the exact `gcb-core` version. - CI adds a real package test: 1. `npm pack` all three; @@ -81,7 +81,7 @@ self-contained artifact. That is a separate decision from this package split. 3. install `gcb-host` into another prefix, assert `gcb app --help` and `gcb daemon --help` work and `yaml` is present. - Unit tests may continue to import `packages/*/src` through a development export condition; the - packaged entry points use `dist`. + packaged entry points use `lib`. ## Consequences From f22e6564a59cbcc3f92f200a7bc59d7485184eef Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 09:06:27 +0000 Subject: [PATCH 08/12] fix: make the release workflow actually publish Enable Corepack for yarn 4, run clean-pkg-json from prepack, restore manifests in the package e2e test, and document the remaining release prerequisites. --- .changeset/initial-release.md | 7 +++++++ .github/workflows/release.yml | 3 +++ DEVELOPMENT.md | 12 ++++++++++-- packages/client/package.json | 2 +- packages/core/package.json | 2 +- packages/host/package.json | 2 +- test/e2e/packages.test.ts | 15 +++++++++++---- test/unit/commands.test.ts | 8 ++++---- 8 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 .changeset/initial-release.md diff --git a/.changeset/initial-release.md b/.changeset/initial-release.md new file mode 100644 index 00000000..b2b1589a --- /dev/null +++ b/.changeset/initial-release.md @@ -0,0 +1,7 @@ +--- +'gcb-core': patch +'gcb-client': patch +'gcb-host': patch +--- + +Prepare the first public release of the renamed packages. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4716c052..08faf288 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,9 @@ jobs: node-version: lts/* cache: yarn + - name: Enable Corepack + run: corepack enable + - name: Install Dependencies run: yarn install --immutable diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 15a40250..01877b1c 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -155,10 +155,11 @@ the checkout of that version beside them. Published changes are versioned with Changesets: 1. Add a changeset with `corepack yarn changeset` in the pull request that changes a published package. + There is no CI gate for this; a release run with no changesets publishes nothing. 2. The release workflow uses `changesets/action` to open or update `chore: release packages`. Merging that pull request runs `changeset publish`. -3. `@changesets/changelog-github` writes the changelog entries; each package's `prepublishOnly` - runs `clean-pkg-json`, so the published manifest omits the workspace-only `development` export. +3. `@changesets/changelog-github` writes the changelog entries; each package's `prepack` runs + `clean-pkg-json`, so the published manifest omits the workspace-only `development` export. 4. The same release builds and publishes the `gcb-client` and `gcb-host` images. 5. Re-render any sidecar you generated: `compose` pins `gcb-host@`, so it takes the new version only when it is rendered again. @@ -166,6 +167,13 @@ Published changes are versioned with Changesets: The release workflow uses npm trusted publishing (OIDC): `id-token: write` is granted, no `NPM_TOKEN` secret is read. Configure the npm package's trusted publisher for this repository first. +Also configure: + +- `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` repository secrets; both registry logins happen before + the image build, so a missing pair fails the release after npm has already published +- **Settings → Actions → General → Allow GitHub Actions to create and approve pull requests** on; + `changesets/action` cannot open the version pull request without it + ## Layout ``` diff --git a/packages/client/package.json b/packages/client/package.json index 3fe0a002..063f77a5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -35,6 +35,6 @@ "registry": "https://registry.npmjs.org/" }, "scripts": { - "prepublishOnly": "clean-pkg-json" + "prepack": "clean-pkg-json" } } diff --git a/packages/core/package.json b/packages/core/package.json index dcda35ce..5dded5d9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,6 +28,6 @@ "registry": "https://registry.npmjs.org/" }, "scripts": { - "prepublishOnly": "clean-pkg-json" + "prepack": "clean-pkg-json" } } diff --git a/packages/host/package.json b/packages/host/package.json index 9f2167af..7c1a3fd5 100644 --- a/packages/host/package.json +++ b/packages/host/package.json @@ -38,6 +38,6 @@ "registry": "https://registry.npmjs.org/" }, "scripts": { - "prepublishOnly": "clean-pkg-json" + "prepack": "clean-pkg-json" } } diff --git a/test/e2e/packages.test.ts b/test/e2e/packages.test.ts index ad1ee529..0787dead 100644 --- a/test/e2e/packages.test.ts +++ b/test/e2e/packages.test.ts @@ -40,10 +40,17 @@ function runFailure(command: string, args: readonly string[]): string { function pack(name: string): string { const target = path.join(packs, name); fs.mkdirSync(target, { recursive: true }); - const output = run('npm', ['pack', path.join(ROOT, 'packages', name), '--pack-destination', target]); - const file = output.trim().split('\n').pop(); - assert.ok(file, `npm pack produced no filename for ${name}`); - return path.join(target, file); + const manifestPath = path.join(ROOT, 'packages', name, 'package.json'); + const manifest = fs.readFileSync(manifestPath, 'utf8'); + try { + const output = run('npm', ['pack', path.join(ROOT, 'packages', name), '--pack-destination', target]); + const file = output.trim().split('\n').pop(); + assert.ok(file, `npm pack produced no filename for ${name}`); + return path.join(target, file); + } finally { + // prepack runs clean-pkg-json in the workspace; restore the source manifest. + fs.writeFileSync(manifestPath, manifest); + } } /** Find a package below a prefix. */ diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 7e7b70e9..a651f301 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -1320,7 +1320,7 @@ describe('the anchor flags, at the CLI boundary', () => { await new Promise((resolve) => server.listen(socketPath, resolve)); try { const result = await runAsync(process.execPath, [ - '--conditions=gcb-development', + '--conditions=development', path.resolve('packages/client/bin/gcb.js'), 'pr', '--number', '7', '--status', '--socket', socketPath, ], { @@ -1351,7 +1351,7 @@ describe('the anchor flags, at the CLI boundary', () => { await new Promise((resolve) => server.listen(socketPath, resolve)); try { const result = await runAsync(process.execPath, [ - '--conditions=gcb-development', + '--conditions=development', 'packages/client/bin/gcb.js', 'pr', '--number', '7', '--title', 'a title', '--repo', 'acme/widget', '--socket', socketPath, ], { @@ -1385,7 +1385,7 @@ describe('the anchor flags, at the CLI boundary', () => { await new Promise((resolve) => server.listen(socketPath, resolve)); try { const result = await runAsync(process.execPath, [ - '--conditions=gcb-development', + '--conditions=development', 'packages/client/bin/gcb.js', 'pr', '--number', '7', '--body-file', bodyFile, '--repo', 'acme/widget', '--socket', socketPath, ], { @@ -1415,7 +1415,7 @@ describe('the anchor flags, at the CLI boundary', () => { await new Promise((resolve) => server.listen(socketPath, resolve)); try { const result = await runAsync(process.execPath, [ - '--conditions=gcb-development', + '--conditions=development', 'packages/client/bin/gcb.js', 'pr', '--repo', 'acme/widget', From 30a15998905cf18888903f9cbf93e881eb572a99 Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 09:25:39 +0000 Subject: [PATCH 09/12] docs: publish images to GHCR only Docker Hub has no code-vaults namespace; use ghcr.io for the release, local tags and idsh examples. --- .github/workflows/release.yml | 15 +++------------ DEVELOPMENT.md | 6 +++--- Dockerfile | 4 ++-- README.md | 21 +++++++++++---------- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08faf288..0abd39d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,13 +96,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Log in to Docker Hub - uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 - with: - registry: docker.io - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Work out the tags id: tags env: @@ -110,11 +103,9 @@ jobs: IMAGE: gcb-${{ matrix.target }} run: | { - for registry in ghcr.io/code-vaults docker.io/code-vaults; do - echo "$registry/$IMAGE:latest" - echo "$registry/$IMAGE:sha-${GITHUB_SHA}" - echo "$registry/$IMAGE:${VERSION}" - done + echo "ghcr.io/code-vaults/$IMAGE:latest" + echo "ghcr.io/code-vaults/$IMAGE:sha-${GITHUB_SHA}" + echo "ghcr.io/code-vaults/$IMAGE:${VERSION}" } > "$RUNNER_TEMP/tags" { echo 'tags< Date: Thu, 29 Oct 2026 09:55:40 +0000 Subject: [PATCH 10/12] docs: separate local image tags from published ones --- DEVELOPMENT.md | 2 +- README.md | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index a6d9d834..820d73b1 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -156,7 +156,7 @@ Published changes are versioned with Changesets: 1. Add a changeset with `corepack yarn changeset` in the pull request that changes a published package. There is no CI gate for this; with no changesets, `changeset publish` only publishes versions npm -does not have yet. + does not have yet. 2. The release workflow uses `changesets/action` to open or update `chore: release packages`. Merging that pull request runs `changeset publish`. 3. `@changesets/changelog-github` writes the changelog entries; each package's `prepack` runs diff --git a/README.md b/README.md index ec30aa3f..f4dfec71 100644 --- a/README.md +++ b/README.md @@ -605,27 +605,25 @@ The contract between the pushing container and the broker is deliberately small: ## Images -Two GHCR images, in separate repositories, mirror the two packages: +Two GHCR repositories mirror the two packages: | Image | Contents | Use | |---|---|---| | `ghcr.io/code-vaults/gcb-client:dev` | `gcb` client and Node runtime only | idsh base / `COPY --from` | | `ghcr.io/code-vaults/gcb-host:dev` | broker, `gh`, and broker dependencies | run `gcb daemon` on the host | -Published equivalents are the same names on GHCR: -`ghcr.io/code-vaults/gcb-client:dev` and `ghcr.io/code-vaults/gcb-host:dev`. -A GitHub repository can publish both packages; they share the source repository without sharing a -tag. +The release publishes both repositories with `latest`, `` and `sha-` tags. The +`:dev` tags are local build tags and are not published; do not pull them from GHCR. -For local development, build and reuse the same tags: +For local development, build and reuse the `:dev` tags: ```sh docker build --target client -t ghcr.io/code-vaults/gcb-client:dev . docker build --target host -t ghcr.io/code-vaults/gcb-host:dev . ``` -While the image exists locally, Docker uses it instead of pulling from the registry; another machine -or CI pulls the same tags. +While the image exists locally, Docker uses it instead of pulling from the registry. Another machine +or CI builds the same Dockerfile and tags, rather than pulling `:dev`. For idsh's dev image, name the client image as a stage and copy only the gcb packages into the base: @@ -641,8 +639,9 @@ RUN ln -s /usr/local/lib/node_modules/gcb-client/bin/gcb.js /usr/local/bin/gcb ``` Copy the two package directories, not all of `/usr/local/lib/node_modules`: the idsh base already has -its own `corepack` and `npm`, and a whole-directory copy would replace them. A CI build passes -`--build-arg GCB_CLIENT_IMAGE=ghcr.io/code-vaults/gcb-client:dev` instead. +its own `corepack` and `npm`, and a whole-directory copy would replace them. That default is for a +local build; CI and production pass a published version instead, e.g. +`--build-arg GCB_CLIENT_IMAGE=ghcr.io/code-vaults/gcb-client:`. ## App permissions by action From 440916bc4e4b8db461de038bdf976712927283d5 Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 10:38:57 +0000 Subject: [PATCH 11/12] test: pack package copies so prepack cannot touch the workspace Use an isolated copy in the package e2e test and stop pinning the empty 0.0.0 placeholder in the compose example. --- examples/docker-compose.snippet.yml | 5 +++-- test/e2e/packages.test.ts | 27 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/examples/docker-compose.snippet.yml b/examples/docker-compose.snippet.yml index 8bc1f471..a66ee43e 100644 --- a/examples/docker-compose.snippet.yml +++ b/examples/docker-compose.snippet.yml @@ -91,8 +91,9 @@ services: - /srv/git-broker-socket:/run/git-broker ``` -Pin a version (`npx --yes --package 'gcb-host@0.0.0'`) if you would rather not -resolve `latest` on every start; `compose` pins the version it was generated from. +Pin a released version (`npx --yes --package 'gcb-host@'`) if you would rather not +resolve `latest` on every start; `compose` pins the version it was generated from. The first +functional release is `0.0.1`; `0.0.0` is an empty placeholder. ## 2. DSH container: reach the socket and configure git diff --git a/test/e2e/packages.test.ts b/test/e2e/packages.test.ts index 0787dead..96154b8b 100644 --- a/test/e2e/packages.test.ts +++ b/test/e2e/packages.test.ts @@ -40,17 +40,22 @@ function runFailure(command: string, args: readonly string[]): string { function pack(name: string): string { const target = path.join(packs, name); fs.mkdirSync(target, { recursive: true }); - const manifestPath = path.join(ROOT, 'packages', name, 'package.json'); - const manifest = fs.readFileSync(manifestPath, 'utf8'); - try { - const output = run('npm', ['pack', path.join(ROOT, 'packages', name), '--pack-destination', target]); - const file = output.trim().split('\n').pop(); - assert.ok(file, `npm pack produced no filename for ${name}`); - return path.join(target, file); - } finally { - // prepack runs clean-pkg-json in the workspace; restore the source manifest. - fs.writeFileSync(manifestPath, manifest); - } + const source = path.join(ROOT, 'packages', name); + const copy = path.join(scratch, 'sources', name); + fs.mkdirSync(path.dirname(copy), { recursive: true }); + fs.cpSync(source, copy, { + recursive: true, + filter: (entry) => !entry.includes(`${path.sep}node_modules`) && !entry.endsWith(`${path.sep}dist`), + }); + const output = execFileSync('npm', ['pack', copy, '--pack-destination', target], { + cwd: ROOT, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + env: { ...process.env, PATH: `${path.join(ROOT, 'node_modules', '.bin')}${path.delimiter}${process.env['PATH'] ?? ''}` }, + }); + const file = output.trim().split('\n').pop(); + assert.ok(file, `npm pack produced no filename for ${name}`); + return path.join(target, file); } /** Find a package below a prefix. */ From 61ad16cdeca3a4188961d31bc87de4cdf4a79d48 Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 29 Oct 2026 11:11:04 +0000 Subject: [PATCH 12/12] test: reuse the spawn helper for isolated packing Drop the dead dist filter and pass the PATH override through run(). --- test/e2e/packages.test.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/e2e/packages.test.ts b/test/e2e/packages.test.ts index 96154b8b..e8da06e0 100644 --- a/test/e2e/packages.test.ts +++ b/test/e2e/packages.test.ts @@ -22,8 +22,13 @@ const hostPrefix = path.join(scratch, 'host'); after(() => fs.rmSync(scratch, { recursive: true, force: true })); /** Run a command and return stdout. */ -function run(command: string, args: readonly string[]): string { - return execFileSync(command, args, { cwd: ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); +function run(command: string, args: readonly string[], env?: NodeJS.ProcessEnv): string { + return execFileSync(command, args, { + cwd: ROOT, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + ...(env === undefined ? {} : { env }), + }); } /** Run a command that is expected to fail, returning its stderr. */ @@ -45,13 +50,11 @@ function pack(name: string): string { fs.mkdirSync(path.dirname(copy), { recursive: true }); fs.cpSync(source, copy, { recursive: true, - filter: (entry) => !entry.includes(`${path.sep}node_modules`) && !entry.endsWith(`${path.sep}dist`), + filter: (entry) => !entry.includes(`${path.sep}node_modules`), }); - const output = execFileSync('npm', ['pack', copy, '--pack-destination', target], { - cwd: ROOT, - encoding: 'utf8', - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...process.env, PATH: `${path.join(ROOT, 'node_modules', '.bin')}${path.delimiter}${process.env['PATH'] ?? ''}` }, + const output = run('npm', ['pack', copy, '--pack-destination', target], { + ...process.env, + PATH: `${path.join(ROOT, 'node_modules', '.bin')}${path.delimiter}${process.env['PATH'] ?? ''}`, }); const file = output.trim().split('\n').pop(); assert.ok(file, `npm pack produced no filename for ${name}`);