Skip to content

Commit d52fb27

Browse files
talagluckclaude
andauthored
Autogenerate CLI (#1205)
* cli updates - autogeneration * Fix Prettier formatting for gitbook2 CLI Wrap the hand-written CLI source to the repo's printWidth, and exclude the auto-generated generated-commands.ts from Prettier (it's regenerated on every build, so formatting it would just be undone). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Regenerate gitbook2 CLI from latest API spec (346 endpoints) Regenerate generated-commands.ts from the latest deployed OpenAPI spec (@gitbook/api 0.186.0), adding 5 new endpoints: change-request agent conversations (list/update/delete), change-request content update, and site Git Sync installations listing. Also use bracket-notation accessors for query-param options so flag names that aren't valid JS identifiers resolve correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Port integration lifecycle + openapi publish commands to gitbook2 Bring the non-autogenerated commands from the `gitbook` CLI into `gitbook2` so it's a functional superset. The integration build/publish lifecycle commands (new/dev/publish/unpublish/tail/check) are grouped under a singular `integration` command to stay distinct from the spec-generated `integrations` group (raw API ops); `openapi publish` is registered top-level. All reuse the existing shared source modules via registerCustomCommands. cli2.ts gains a keep-alive guard so `integration dev` doesn't get torn down by the post-parse process.exit(0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix empty-body response handling in generated CLI commands The generated commands hardcoded `if (response.status !== 204)` before calling response.json(), assuming 204 was the only bodyless success. Endpoints that return another no-body status (e.g. DELETE openapi spec returns 205 on deletion, 204 only when absent) hit response.json() on an empty body and failed with "Unexpected end of JSON input", exiting 1 despite the server-side success. Key off body presence instead of a specific status, which covers 204, 205, and any future bodyless success with no regression for JSON responses. Regenerated all 339 commands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix array/number body flags in generated CLI commands The command generator turned each top-level body field into a typed flag but assigned the raw commander string into the JSON body with no coercion, so --changes '[...]' shipped {"changes":"[...]"} (a string) and the API rejected it. Same for --users and any number-typed field. - Add coerceBodyFlag() to output.ts (unit-tested): JSON-parses array flags, numeric-coerces number flags, passes string/boolean through, with legible errors surfaced by the CLI's top-level handler. - Generator emits coerceBodyFlag(...) for array/number flags and renders array flags as <json> with a [JSON array] hint. - Always offer --body <json> alongside typed flags as an escape hatch (typed flags merge on top), so object-typed fields dropped by flattenObjectFlags stay reachable. - Regenerate generated-commands.ts; add coerceBodyFlag tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add hidden, deprecation-warning top-level aliases for integration verbs Make gitbook2 a backward-compatible superset of the original `gitbook` CLI: the integration lifecycle verbs (new/dev/publish/unpublish/tail/check) are now also reachable via their historical top-level spelling, in addition to the canonical `integration <verb>` group. - Factor each lifecycle command's argument/option wiring and action handler into a shared LIFECYCLE_COMMANDS table so both mounts stay in sync (no duplication). - The top-level aliases are hidden from help and print a deprecation warning to stderr (never stdout) before delegating to the same handler. - The warning derives the binary name from program.name() rather than hardcoding "gitbook2", so it follows the eventual rename to "gitbook" automatically. No namespace collisions: auth/whoami/openapi-publish already exist at identical paths and are left untouched; the six verbs don't clash with any generated group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Apply Prettier formatting to api-commands.ts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add usage attribution to the CLI User-Agent Set a distinct, convention-matching User-Agent (`GitBook-CLI/<version>`, matching GitBook-Open / GitBook-MCP-Server) and fold the invoked command path into it, so API requests can be attributed to a specific CLI command in logs / the GCP per-endpoint dashboard — no per-request plumbing. - remote.ts: UA base is now `GitBook-CLI/<version>`; getAPIClient takes an optional command path and emits `GitBook-CLI/<version> (<command>)`. auth and whoami pass their command names. - generate-commands.ts: each generated action passes its dotted command path (e.g. spaces.change-requests.content.update) to getAPIClient. - generated-commands.ts: regenerated. Verified on the wire: e.g. `GitBook-CLI/0.28.0 (spaces.change-requests.comments.list)`. * gitbook2: fix array query params, add whoami JSON, richer help, markdown --normalize Addresses the CLI-side follow-ups from the review-flow notes: - Array query params (e.g. `comments list --authors`) were serialized with String(), flattening them to a single value the API rejected with a 500. Resolve each query param's schema and route `type: array` params through a new coerceArrayQueryParam (accepts CSV or a JSON array) so the client emits repeated `key=v1&key=v2`. query is now Record<string, string | string[]>. - whoami gained --json/--yaml, so scripts can read their own user id (`whoami --json | jq -r .id`) instead of parsing the plain `ID:` line. - --help now documents enum query params (e.g. --orderBy: one of updatedAt, createdAt) and the operations of a discriminated-oneOf array flag (e.g. --changes: update_page, insert_page, delete_page), both derived from the spec. - Schema-union 422s ("expected to match one") get an actionable hint naming the accepted shape (update_page.document wants {markdown}, insert_page.into optional) via explainApiError. - New opt-in --normalize on page-document commands makes markdown round-trip safe before sending: strips a duplicated leading H1 title and collapses multi-line {% ... %} integration blocks onto one line. Detected generically from the spec (array items whose variants carry a `document`). Unit tests added for the four new output helpers; generated-commands.ts regenerated. * CLI: address review feedback on autogenerated commands - Read the OpenAPI spec from openapi.json (drop js-yaml parse) - Stop tracking generated-commands.ts; gitignore it (regenerated by prebuild) - Send a plain GitBook-CLI/<version> User-Agent (drop per-command attribution) - Call the generated typed client methods (api.<ns>.<operationId>) instead of raw api.request(); use response.data instead of JSON.parse(response.text()) - Skip SSE (text/event-stream) operations: their typed methods return an EventIterator that doesn't fit the one-response-per-command model * CLI: add browser OAuth login and make gitbook the autogenerated CLI - Add 'gitbook login'/'logout' for browser-based OAuth (DCR + PKCE + loopback redirect) with automatic token refresh. OAuth session and a personal API token can coexist: OAuth drives API commands, the token is used/required for publishing integrations. - Replace the gitbook CLI with the autogenerated (gitbook2) implementation and remove the separate gitbook2 binary. Integration lifecycle commands are now 'gitbook integration <verb>', with the historical top-level spellings kept as deprecated aliases. - Regenerate shell completions and update docs/changesets accordingly. * CLI: add prepack to guarantee codegen + build before publish Ensures 'npm publish'/'changeset publish' always regenerate the spec-derived commands and bundle dist/cli.js, even when the publish path doesn't go through 'turbo run build'. * CLI: fix formatting in output.test.ts * CI: require a @gitbook/cli changeset when @gitbook/api is bumped The gitbook CLI generates its commands from the API OpenAPI spec, so an @gitbook/api bump should also republish the CLI. Adds a 'Changeset guard' workflow that fails a PR bumping @gitbook/api without a pending @gitbook/cli changeset, and documents the one-line step in CONTRIBUTING. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 777421c commit d52fb27

21 files changed

Lines changed: 2966 additions & 199 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/cli': minor
3+
---
4+
5+
Add `gitbook login` / `gitbook logout` to authenticate through the browser using OAuth, with automatic token refresh. A browser session and a personal API token can be configured at the same time: the OAuth session is used for API commands, while a personal token (via `gitbook auth --token`) is still used — and required — for publishing integrations.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/cli': minor
3+
---
4+
5+
The `gitbook` CLI is now the spec-generated CLI: every GitBook API operation is available as a command group (e.g. `gitbook organizations list`), alongside shell completion (`gitbook completion`). The integration build/publish lifecycle now lives under `gitbook integration <verb>` (`new`, `dev`, `publish`, `unpublish`, `tail`, `check`); the historical top-level spellings (`gitbook publish`, …) keep working as deprecated aliases that print a warning. The separate `gitbook2` binary has been removed — it is now just `gitbook`.

.github/CONTRIBUTING.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,25 @@ You're also welcome to contribute updates or improvements to the packages living
4343
`@gitbook/api`: https://gitbook.com/docs/developers/gitbook-api/overview
4444
`@gitbook/runtime`: https://gitbook.com/docs/developers/integrations/runtime
4545

46+
#### Releasing packages
47+
48+
Package releases are driven by [changesets](https://github.com/changesets/changesets). Add one with `bun run changeset` (or write a `.changeset/*.md` file by hand) describing the change and the version bump for each affected package. Merges to `main` are then versioned and published automatically by the release workflow.
49+
50+
##### Bumping `@gitbook/api` also bumps `@gitbook/cli`
51+
52+
The `gitbook` CLI generates its command tree from the GitBook OpenAPI spec at build time. When you bump `@gitbook/api`, add a `@gitbook/cli` changeset in the same PR so the CLI is regenerated and republished with the new spec. A single changeset file can bump both:
53+
54+
```markdown
55+
---
56+
'@gitbook/api': minor
57+
'@gitbook/cli': patch
58+
---
59+
60+
Bump API to 0.x.0 and regenerate CLI commands
61+
```
62+
63+
You don't need to run codegen or commit anything generated — the CLI's commands are regenerated from the live spec during the release build. This is the only extra step. A CI check (`Changeset guard`) fails any PR that bumps `@gitbook/api` without a pending `@gitbook/cli` changeset.
64+
4665
## Contributing
4766

4867
### Tooling
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Changeset guard
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
cli-republish-on-api-bump:
8+
runs-on: ubuntu-latest
9+
name: CLI changeset on API bump
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@v4
13+
with:
14+
# Full history so we can diff against the PR base.
15+
fetch-depth: 0
16+
- name: Require a @gitbook/cli changeset when @gitbook/api is bumped
17+
env:
18+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
19+
run: |
20+
set -euo pipefail
21+
22+
api_re="^['\"]?@gitbook/api['\"]?[[:space:]]*:"
23+
cli_re="^['\"]?@gitbook/cli['\"]?[[:space:]]*:"
24+
25+
# Changeset files introduced by THIS PR (added or modified), excluding the README.
26+
pr_changesets=$(git diff --name-only --diff-filter=AM "$BASE_SHA...HEAD" -- '.changeset/*.md' \
27+
| grep -vi '/README\.md$' || true)
28+
29+
if [ -z "$pr_changesets" ]; then
30+
echo "No changeset changes in this PR — nothing to check."
31+
exit 0
32+
fi
33+
34+
# Does this PR introduce a @gitbook/api bump?
35+
api_bumped=false
36+
while IFS= read -r f; do
37+
[ -n "$f" ] && [ -f "$f" ] || continue
38+
if grep -Eq "$api_re" "$f"; then api_bumped=true; fi
39+
done <<< "$pr_changesets"
40+
41+
if [ "$api_bumped" = false ]; then
42+
echo "This PR does not bump @gitbook/api — nothing to enforce."
43+
exit 0
44+
fi
45+
46+
# Is there ANY pending @gitbook/cli changeset (from this PR or already on the branch)?
47+
# The release batches all pending changesets, so a @gitbook/cli bump anywhere counts.
48+
cli_bumped=false
49+
for f in .changeset/*.md; do
50+
[ -f "$f" ] || continue
51+
case "$f" in */README.md) continue ;; esac
52+
if grep -Eq "$cli_re" "$f"; then cli_bumped=true; fi
53+
done
54+
55+
if [ "$cli_bumped" = false ]; then
56+
echo "::error::This PR bumps @gitbook/api but no pending changeset bumps @gitbook/cli."
57+
echo "The gitbook CLI generates its commands from the API's OpenAPI spec, so an"
58+
echo "@gitbook/api bump should normally also republish the CLI. Add"
59+
echo "\"'@gitbook/cli': patch\" to your changeset (or add a second .changeset/*.md)"
60+
echo "so the CLI is regenerated and released with the new spec."
61+
exit 1
62+
fi
63+
64+
echo "Changeset guard passed (a @gitbook/cli changeset is present)."

.prettierignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ docs
2525
*.html
2626

2727
## Project specific ignores
28-
integrations/runkit/src/webframe.ts
28+
integrations/runkit/src/webframe.ts
29+
30+
# Auto-generated by scripts/generate-commands.ts (regenerated on every build).
31+
packages/cli/src/generated-commands.ts

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/ci.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
node-version: 18.x
2121
- run: npm ci
2222
- run: npm install -g @gitbook/cli
23-
- run: gitbook publish .
23+
- run: gitbook integration publish .
2424
env:
2525
GITBOOK_TOKEN: ${{ secrets.GITBOOK_TOKEN }}
2626
```

docs/cli/quickstart.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,24 @@ npm install @gitbook/cli -g
1010

1111
### Step 2: Authenticate with your account
1212

13-
Create an API token in your GitBook.com at [app.gitbook.com/account/developer](https://app.gitbook.com/account/developer).
13+
Sign in through your browser:
1414

15-
Then run the following command to authenticate locally:
15+
```
16+
gitbook login
17+
```
18+
19+
Or, to publish integrations, authenticate with a personal API token instead. Create one at [app.gitbook.com/account/developer](https://app.gitbook.com/account/developer), then run:
1620

1721
```
18-
gitbook auth
22+
gitbook auth --token <token>
1923
```
2024

25+
> Publishing integrations requires a personal API token; the browser (OAuth) session cannot publish.
26+
2127
### Step 3: Bootstrap your app
2228

2329
Bootstrap your first app by running:
2430

2531
```
26-
gitbook new
32+
gitbook integration new
2733
```

docs/cli/reference.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,49 @@
11
# Reference
22

3-
References for the command line utility `gitbook`. The CLI can be used to create, test and publish integrations for the GitBook Integrations platform.
3+
References for the command line utility `gitbook`. The CLI can be used to create, test and publish integrations for the GitBook Integrations platform, and to call the GitBook API directly.
44

55
Install the CLI using `npm install @gitbook/cli -g`.
66

7+
Most of the command tree is generated from the GitBook API: each API operation is exposed as a command group, e.g. `gitbook organizations list` or `gitbook spaces get <space>`. Run `gitbook --help` to explore them, and `gitbook completion --install` to set up shell completion. The commands below are the authentication and integration-lifecycle commands.
8+
9+
## `gitbook login`
10+
11+
Authenticate the CLI through your browser using OAuth. Running the command opens GitBook in your default browser, prompts you to authorize the CLI, and stores the resulting token locally. Access tokens are refreshed automatically as needed.
12+
13+
> Publishing integrations (`gitbook integration publish` / `unpublish`) is not available with a browser session and still requires a personal API token — use `gitbook auth` for those.
14+
15+
## `gitbook logout`
16+
17+
Remove the authentication stored for the current environment.
18+
719
## `gitbook auth`
820

921
Authenticate the CLI with a GitBook Developer API token. You can generate a personal developer token in your [GitBook Developer settings](https://app.gitbook.com/account/developer).
1022

1123
The token can also be provided using the command line argument `--token=<token>`; if none is provider, it'll be prompted.
1224

13-
## `gitbook new <dir>`
25+
## `gitbook integration new <dir>`
1426

1527
Create and initialize a new integration locally. The program will prompt for information about the integration.
1628

17-
## `gitbook dev <spaceId>`
29+
## `gitbook integration dev <spaceId>`
1830

1931
Create a live connection with a space within your GitBook organization. Updates made locally while the connection is running will automatically be received in the chosen space.&#x20;
2032

2133
See the [development section](../getting-started/development.md) to learn more.
2234

23-
## `gitbook publish`
35+
## `gitbook integration publish`
2436

2537
Publish the integration defined in the `gitbook-manifest.yaml` file. See the [GitBook Manifest reference](broken-reference) documentation to learn more.
2638

2739
See the [publishing section](../getting-started/publishing.md) to learn more about publishing your integration to GitBook.
2840

29-
## `gitbook unpublish <integration-name>`
41+
## `gitbook integration unpublish <integration-name>`
3042

3143
Unpublish your integration from the GitBook integration platform. Pass the name of the integration as an argument to the command.
3244

45+
> The integration lifecycle commands (`new`, `dev`, `publish`, `unpublish`, `tail`, `check`) are also accepted at the top level (`gitbook publish`, …) as deprecated aliases that print a warning. Prefer the `gitbook integration <verb>` form.
46+
3347
## `gitbook whoami`
3448

3549
Print information about the currently authenticated user.

packages/cli/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
dist/
2+
3+
# Auto-generated from packages/api/spec/openapi.json by scripts/generate-commands.ts
4+
# (regenerated by the `prebuild` step); not hand-edited, so not tracked.
5+
src/generated-commands.ts

0 commit comments

Comments
 (0)