1 Commits
Author SHA1 Message Date
Nicholas Tindleandunknown 28d65d4a21 ci(platform): keep the gitbook branch a one-way copy of master's docs (#15165)
### Why / What / How

**Why:** GitBook publishes agpt.co/docs from the `gitbook` branch. That
branch has been kept in step with `dev` by hand-made sync PRs (#14019,
#14171, #14554), and it drifts between them: the live site is currently
three weeks behind. The branch is also writable by people and by GitBook
itself, so changes could be made there that never reach `dev`. The
branch exists so that GitBook never writes to a deployable branch; that
stays.

**What:** `gitbook` becomes a one-way, docs-only copy of `master`'s
`docs/`, written only by automation. The published docs then match what
is deployed. If anything else writes to the branch, the sync reports it
in an issue and puts `master`'s docs back.

**How:** A workflow runs on every push to `master`, daily, and on manual
dispatch. It runs a small stdlib-only script as the `autogpt-batch-bot`
App. The script reads `master`'s `docs/` tree, and if `gitbook` does not
already hold exactly that, writes one commit on top of the branch head
whose tree is only `docs/`. It uses the Git Data API, never
force-pushes, and leaves author and committer unset so GitHub signs the
commit as the App. A repository ruleset (applied by hand afterwards,
recorded here as JSON) restricts updates of `gitbook` to that App.
`GITHUB_TOKEN` cannot be given a ruleset bypass, which is why the App is
used.

### Changes 🏗️

- **`.github/workflows/docs-gitbook-sync.yml`** (new): the workflow. It
only runs in this repository and only from `master`'s copy of itself.
- **`.github/workflows/scripts/gitbook_sync.py`** (new): the sync.
- A commit it wrote carries a `Synced-From: <master sha>` trailer. The
head counts as its own only if it is docs-only and its `docs/` equals
that commit's `docs/`.
- When the head is not its own, it lists the first-parent commits since
its last snapshot and who moved the branch (repository activity log),
reports them in an issue **before** moving the branch, writes a fresh
snapshot on top, and exits non-zero. The foreign commits stay in the
branch history.
- A branch that was rewritten or recreated since a previous sync is
reported the same way. The very first sync, when the App has never
written the branch, is not.
- If it cannot update the branch (or `master` has no `docs/`, or the
branch is missing), it reports that in the same issue and exits
non-zero.
- A report already on the open issue is not posted again, so a problem
that persists does not add a comment per run. Only an issue the App
itself opened is reused.
- Text taken from commits is quoted and stripped of control characters
before it reaches the issue or the job log.
- **`.github/workflows/scripts/test_gitbook_sync.py`** (new): 33 tests
over an in-memory fake of the endpoints used. They run in the existing
PR status checker job.
- **`.github/rulesets/gitbook-sync-only.json`,
`gitbook-no-force-push-or-delete.json`** (new): the two rulesets, as the
request bodies for `POST /repos/{owner}/{repo}/rulesets`. Nothing
applies them automatically.
- **`docs/content/contribute/index.md`, `docs/AGENTS.md`**: docs changes
go to `dev`; never commit to `gitbook` or edit pages in GitBook.

**Configuration:**

- Uses the existing `BATCH_BOT_APP_ID` variable and
`BATCH_BOT_PRIVATE_KEY` secret. The token is requested with `contents:
write` and `issues: write` only.
- Optional new repository variable `GITBOOK_SYNC_NOTIFY`: who to mention
on a report. The default mentions `@Significant-Gravitas/maintainers`
and `@ntindle`; the App cannot see organization teams today, so the team
mention may not notify.

**What happens when this reaches `master`:**

- The release push itself triggers the first run. It writes one commit
on `gitbook` that removes everything except `docs/` (about 6,000 files)
and brings `docs/` to `master`'s. GitBook then imports the Platform and
Integrations spaces; Home is unchanged.
- Nothing on `gitbook` is lost: its tree equals `dev` as of 2026-09-14,
which `master` contains. Anything merged into `gitbook` between now and
that release would be dropped without a report, because the first sync
is silent by design.

**Manual steps after the first successful run (admin):**

1. Apply the two rulesets: `gh api -X POST
repos/Significant-Gravitas/AutoGPT/rulesets --input
.github/rulesets/gitbook-sync-only.json`, then the same with
`gitbook-no-force-push-or-delete.json`.
2. Move #14048 to `dev` and close #15157; PRs into `gitbook` cannot be
merged once the rulesets are on.
3. In GitBook: stop change requests being merged in the three synced
spaces, and remove Git Sync from the two unused spaces that still point
at this branch.

**What this does not do, or has not been proven:**

- GitHub cannot stop a change request being merged inside GitBook. Such
a change goes live on the site, fails to push, and is overwritten by the
next copy.
- Three steps are only exercised by the first real run: the App (which
has no `workflows` permission) writing a commit that removes workflow
files, the App's ruleset bypass applying to an API ref update, and
GitBook importing an update made through the API. A rejected write is
reported and fails the run; the fallback is for an admin to push that
first commit.
- The App's key is a repository-level secret, so anyone who can push a
workflow to any branch could mint its token. The rulesets keep out
GitBook, pull-request merges and ordinary pushes; they are not a barrier
against people with write access.
- A hand-made commit that copies the snapshot format exactly is treated
as the sync's own.

### Agents and large language models used

Claude Code (model: unknown)

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] `python -m unittest discover -s .github/workflows/scripts -p
"test_*.py"` passes (54 tests, 33 new), prints no workflow commands and
writes nothing to the step summary
- [x] Dry run against this repository with a read-only token
(`DRY_RUN=1`): reads `master` and `gitbook`, detects a first sync, and
plans one commit on top of `gitbook@fb7c677bd3`
- [x] The single-entry tree built from `master`'s `docs/` is identical
to `master`'s `docs/` (checked locally with `git mktree` and `git diff`)
- [x] Both ruleset files match the shape of the live `batch-rollup
bot-only` ruleset
- [ ] First real run on `master` (only possible after release; see
above)

<details>
  <summary>Example test plan</summary>
  
  - [ ] Create from scratch and execute an agent with at least 3 blocks
- [ ] Import an agent from file upload, and confirm it executes
correctly
  - [ ] Upload agent to marketplace
- [ ] Import an agent from marketplace and confirm it executes correctly
  - [ ] Edit an agent from monitor, and confirm it executes correctly
</details>

#### For configuration changes:

- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)

<details>
  <summary>Examples of configuration changes</summary>

  - Changing ports
  - Adding new services that need to communicate with each other
  - Secrets or environment variable changes
  - New or infrastructure changes such as databases
</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> The first run on `master` will rewrite `gitbook` to a docs-only tree
and changes how published docs are updated; misconfiguration or rulesets
not applied could block sync or leave stale docs until fixed.
> 
> **Overview**
> Adds **automated one-way sync** from `master`'s `docs/` to the
`gitbook` branch (what GitBook publishes), replacing manual sync PRs and
direct edits on `gitbook`.
> 
> A new **`docs-gitbook-sync`** workflow runs on pushes to `master`,
daily, and on dispatch. It mints a **batch-bot GitHub App** token and
runs `gitbook_sync.py`, which fast-forwards `gitbook` with a
**docs-only** commit tagged `Synced-From: <master sha>`. If the branch
head was changed by anything else, the script **opens or updates an
issue** (with deduped reports), restores `master`'s docs on top, and
**fails the job** so the drift is visible. Two **ruleset JSON files**
document branch rules: no delete/force-push, and updates allowed only
for the sync App (applied manually by admins).
> 
> Contributor docs in **`docs/AGENTS.md`** and
**`docs/content/contribute/index.md`** now say to PR **`dev`**, not
`gitbook`, and warn that GitBook edits are overwritten.
**`test_gitbook_sync.py`** adds broad unit tests for sync, reporting,
and API retry behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
aeb0d72c4e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: unknown (Claude Code) <[email protected]>
2026-10-03 15:42:26 +00:00