From fe8fa51b5076d982f61bf5f395c2df7ffe5ee9c1 Mon Sep 17 00:00:00 2001 From: Chin Rock Date: Tue, 27 Oct 2026 16:54:17 +0800 Subject: [PATCH] fix(docs): publish directly from LangBot before archiving docs repository Co-authored-by: multica-agent --- .github/workflows/sync-blog-articles.yml | 4 +- .github/workflows/sync-github-wiki.yml | 2 +- .github/workflows/sync-landing-submodule.yml | 14 +-- AGENTS.md | 2 +- ARCHITECTURE.md | 2 +- README.md | 2 +- README_CN.md | 2 +- README_ES.md | 2 +- README_FR.md | 2 +- README_JP.md | 2 +- README_KO.md | 2 +- README_RU.md | 2 +- README_TW.md | 2 +- README_VI.md | 2 +- context7.json | 7 ++ dev-docs/DOCS_MIGRATION.md | 125 ++++++++++--------- docs/README.md | 2 +- docs/README_EN.md | 2 +- docs/context7.json | 4 - docs/node_modules | 1 + docs/scripts/build-github-wiki.py | 6 +- docs/scripts/sync-landing-submodule.py | 22 +++- docs/tests/test_build_github_wiki.py | 2 +- docs/tests/test_sync_landing_submodule.py | 24 +++- 24 files changed, 133 insertions(+), 104 deletions(-) create mode 100644 context7.json delete mode 100644 docs/context7.json create mode 120000 docs/node_modules diff --git a/.github/workflows/sync-blog-articles.yml b/.github/workflows/sync-blog-articles.yml index 03a9d3ca1..133687309 100644 --- a/.github/workflows/sync-blog-articles.yml +++ b/.github/workflows/sync-blog-articles.yml @@ -14,7 +14,7 @@ concurrency: jobs: sync: - if: github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_MIRROR_ENABLED == 'true' + if: github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_PUBLISH_ENABLED == 'true' runs-on: ubuntu-latest timeout-minutes: 20 defaults: @@ -26,7 +26,7 @@ jobs: with: ref: master fetch-depth: 0 - # A PAT push triggers the mirror and Wiki workflows; GITHUB_TOKEN does not. + # A PAT push triggers the landing and Wiki workflows; GITHUB_TOKEN does not. token: ${{ secrets.LANGBOT_WIKI_PUSH_TOKEN || secrets.RELEASE_UPLOAD_GITHUB_TOKEN }} - uses: actions/setup-node@v7 with: diff --git a/.github/workflows/sync-github-wiki.yml b/.github/workflows/sync-github-wiki.yml index eed97553f..26502f1ab 100644 --- a/.github/workflows/sync-github-wiki.yml +++ b/.github/workflows/sync-github-wiki.yml @@ -57,7 +57,7 @@ jobs: sync: name: Publish to LangBot Wiki - if: github.event_name != 'pull_request' && github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_MIRROR_ENABLED == 'true' + if: github.event_name != 'pull_request' && github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_PUBLISH_ENABLED == 'true' needs: build runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.github/workflows/sync-landing-submodule.yml b/.github/workflows/sync-landing-submodule.yml index b6d99468a..09ced90d9 100644 --- a/.github/workflows/sync-landing-submodule.yml +++ b/.github/workflows/sync-landing-submodule.yml @@ -1,4 +1,4 @@ -name: Sync docs mirror and landing submodule +name: Sync landing docs from LangBot on: push: @@ -15,7 +15,7 @@ concurrency: jobs: sync: - if: github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_MIRROR_ENABLED == 'true' + if: github.repository == 'langbot-app/LangBot' && github.ref == 'refs/heads/master' && vars.DOCS_PUBLISH_ENABLED == 'true' runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -27,16 +27,6 @@ jobs: - name: Test synchronization contracts working-directory: docs run: python3 -m unittest discover -s tests -p test_sync_landing_submodule.py -v - - name: Publish docs subtree (fast-forward only) - env: - GH_TOKEN: ${{ secrets.LANGBOT_WIKI_PUSH_TOKEN || secrets.RELEASE_UPLOAD_GITHUB_TOKEN }} - run: | - gh auth setup-git - git remote add docs-mirror https://github.com/langbot-app/langbot-docs.git - git fetch docs-mirror main - docs_sha=$(git subtree split --prefix=docs) - git merge-base --is-ancestor docs-mirror/main "$docs_sha" - git push docs-mirror "$docs_sha:refs/heads/main" - name: Update landing submodule and production fork working-directory: docs env: diff --git a/AGENTS.md b/AGENTS.md index 3abf8259d..daa1c8fdd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ Run tests in Linux to match CI; on Windows, use Linux containers through `wslc` ## Where to Look -- Public documentation source: `docs/`; published at https://langbot.app/docs/. `langbot-app/langbot-docs` is a generated subtree mirror; edit here. +- Public documentation source: `docs/`; published at https://langbot.app/docs/. `langbot-app/langbot-docs` is a legacy archive; edit here. - Architecture map: `ARCHITECTURE.md`. - Dev environment guide: https://langbot.app/docs/zh/develop/dev-config. - Plugin runtime / CLI / SDK debugging: https://langbot.app/docs/zh/develop/plugin-runtime. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 350968ea8..39a59cc1f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -348,4 +348,4 @@ When one of these changes, update the others if the behavior or contract changed ## Documentation repositories -The public documentation site lives in `docs/` and is built with Node.js 24 and Fumapress. Internal design and review notes live in `dev-docs/`. The `langbot-app/langbot-docs` repository is a fast-forward subtree mirror for the landing site submodule and Context7. See `dev-docs/DOCS_MIGRATION.md` for publication and cutover details. +The public documentation site lives in `docs/` and is built with Node.js 24 and Fumapress. Internal design and review notes live in `dev-docs/`. The landing site keeps its `docs` submodule path but tracks LangBot `master`, building from the nested `docs/` directory. The old `langbot-app/langbot-docs` repository can be archived; publication never writes to it. See `dev-docs/DOCS_MIGRATION.md` for publication and cutover details. diff --git a/README.md b/README.md index 199ddbb8a..bca0602f7 100644 --- a/README.md +++ b/README.md @@ -189,4 +189,4 @@ Thanks to all [contributors](https://github.com/langbot-app/LangBot/graphs/contr -[Documentation source](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[Documentation source](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_CN.md b/README_CN.md index 18a7d3426..c3849fd78 100644 --- a/README_CN.md +++ b/README_CN.md @@ -213,4 +213,4 @@ LangBot **从设计上就对 Agent 友好** —— 你的编码 Agent(Claude C If you're a code agent based on LLM, you should read [AGENTS.md](AGENTS.md) to understand the project structure and development process before you start any development. --> -[文档源码](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[文档源码](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_ES.md b/README_ES.md index f0fc684e9..a57239ac1 100644 --- a/README_ES.md +++ b/README_ES.md @@ -187,4 +187,4 @@ Gracias a todos los [colaboradores](https://github.com/langbot-app/LangBot/graph -[Código fuente de la documentación](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[Código fuente de la documentación](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_FR.md b/README_FR.md index 33c6bcc82..ce97b49c3 100644 --- a/README_FR.md +++ b/README_FR.md @@ -187,4 +187,4 @@ Merci à tous les [contributeurs](https://github.com/langbot-app/LangBot/graphs/ -[Sources de la documentation](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[Sources de la documentation](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_JP.md b/README_JP.md index a76774e67..76fb45d26 100644 --- a/README_JP.md +++ b/README_JP.md @@ -187,4 +187,4 @@ LangBot をより良くするために貢献してくださったすべての[ -[ドキュメントのソース](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[ドキュメントのソース](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_KO.md b/README_KO.md index bcc9c32f7..4ffe399a6 100644 --- a/README_KO.md +++ b/README_KO.md @@ -187,4 +187,4 @@ LangBot을 더 나은 프로젝트로 만들어 주신 모든 [기여자](https: -[문서 소스](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[문서 소스](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_RU.md b/README_RU.md index a8fd8e65c..9765c41cc 100644 --- a/README_RU.md +++ b/README_RU.md @@ -187,4 +187,4 @@ LangBot **дружелюбен к агентам по своей архитек -[Исходники документации](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[Исходники документации](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_TW.md b/README_TW.md index 0b9fc1ae6..a71aaa431 100644 --- a/README_TW.md +++ b/README_TW.md @@ -205,4 +205,4 @@ LangBot **從設計上就對 Agent 友善** —— 你的編碼 Agent(Claude C -[文件原始碼](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[文件原始碼](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/README_VI.md b/README_VI.md index 60f5354cc..5b6c9f54d 100644 --- a/README_VI.md +++ b/README_VI.md @@ -187,4 +187,4 @@ Cảm ơn tất cả [người đóng góp](https://github.com/langbot-app/LangB -[Mã nguồn tài liệu](https://github.com/langbot-app/LangBot/tree/main/docs) · [langbot-docs mirror](https://github.com/langbot-app/langbot-docs) +[Mã nguồn tài liệu](https://github.com/langbot-app/LangBot/tree/master/docs) · [langbot-docs archive](https://github.com/langbot-app/langbot-docs) diff --git a/context7.json b/context7.json new file mode 100644 index 000000000..ec3ab1dbc --- /dev/null +++ b/context7.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://context7.com/schema/context7.json", + "projectTitle": "LangBot", + "description": "Open-source platform for building AI agents and instant-messaging bots.", + "branch": "master", + "folders": ["docs/en"] +} diff --git a/dev-docs/DOCS_MIGRATION.md b/dev-docs/DOCS_MIGRATION.md index 7f69df28f..7de9a43b7 100644 --- a/dev-docs/DOCS_MIGRATION.md +++ b/dev-docs/DOCS_MIGRATION.md @@ -1,69 +1,72 @@ -# Documentation subtree and publication +# Documentation publication after the repository migration -Public docs live in `LangBot/docs/`. The former internal `docs/` directory is -`dev-docs/` (27 top-level entries, 87 tracked files at migration). Do not merge -this migration using GitHub squash or rebase: use a merge commit to retain the -subtree parent and original signed commits. Imported history is reachable through -the subtree merge's second parent. Use `git log -- ` -(with the original path relative to the docs repository) to see original authors. -A path-limited `git log docs/` or `--follow` does not reliably traverse the -unrelated-history import. The imported commit is -`51dddcb3f891d60bc38645e88b2dbd515344da81` (888 commits, including 452 signed -commit objects, all retained without rewriting). +Public docs live in `LangBot/docs/`; internal notes live in `dev-docs/`. The +original 27 top-level internal entries (87 files) were moved without loss. +The unsquashed subtree import retains all 888 commits, including 452 signed +objects, from `51dddcb3f891d60bc38645e88b2dbd515344da81`. Use +`git log 51dddcb3f891d60bc38645e88b2dbd515344da81 -- ` to inspect +the original authors. The import is already merged; do not rewrite that history. -## Local checks +## Publication source -With Node.js 24, run `npm ci`, `npm run typecheck`, `npm test`, and `npm run build` -inside `docs/`. Output is `docs/dist/`. The site remains mounted at `/docs/` with -canonical URL `https://langbot.app/docs`. +`langbot-app/langbot-docs` is a historical archive, not a writable mirror. +No publication step splits or pushes to that repository. The landing repository +keeps its submodule **path** `docs`, but its **URL** is +`https://github.com/langbot-app/LangBot.git`, tracking `master`. Consequently, +the landing build runs the documentation package inside `docs/docs/` and copies +`docs/docs/dist/public` to `public/docs`. The public URL stays +`https://langbot.app/docs/`; Vite's base path and canonical site URL are unchanged. -## Cutover after the PR is reviewed and merged +The main-repository synchronization job advances only the landing gitlink to +LangBot `master` and then synchronizes `RockChinQ/LangBot-LandingPage-Vue`. +It checks `.gitmodules` on the exact landing revision it updates, refuses old +repository configurations, and rejects rewinds/divergence. It never force-pushes. -1. Pause the legacy `sync-blog-articles.yml` workflow in `langbot-app/langbot-docs` - and let any running writer finish. Freeze direct commits to that repository. - Pause its legacy Wiki and landing sync workflows too, avoiding duplicate - publishers while switching ownership. Do not push disabling commits directly - to the mirror. -2. Fetch mirror `main` and confirm it is an ancestor of the new subtree split. - If it advanced since import, integrate it with an **unsquashed** - `git subtree pull --prefix=docs main` through another reviewed PR - before cutover; reapply the inert mirror workflow files if necessary. Never - force-push or rewrite the mirror history. -3. The publication jobs read `LANGBOT_WIKI_PUSH_TOKEN` and fall back to the - existing `RELEASE_UPLOAD_GITHUB_TOKEN` when that secret is absent. The fallback - was verified on a scratch branch: it authenticates as the repository owner - with `repo` scope and push+admin on `LangBot`, `langbot-docs`, - `langbot-landing-page` and the production fork, and it can clone and push the - Wiki. It does not carry the `workflow` scope, so it cannot push changes to - `.github/workflows/**` in the mirror; the split only rewrites `docs/**` - content, so this is fine unless a future change edits the inert mirror stubs. - Adding a dedicated `LANGBOT_WIKI_PUSH_TOKEN` with the same access keeps the - blast radius of the fallback credential out of the docs pipeline. Do not - weaken branch protection to accommodate this migration. -4. Set LangBot repository variable `DOCS_MIRROR_ENABLED=true`. Dispatch - `sync-landing-submodule.yml` on master. It fetches full master history, runs - `git subtree split --prefix=docs`, verifies the current mirror head is an - ancestor, pushes without force, then advances only landing's `docs` gitlink. - The first split replaces legacy mirror workflows with inert manual stubs. -5. Dispatch `sync-github-wiki.yml` and `sync-blog-articles.yml` in LangBot. - The blog job now commits only docs article paths to LangBot master using the - dedicated token, so its push triggers the mirror and Wiki workflows. -6. Confirm the `docs` gitlink in `langbot-app/langbot-landing-page`, then confirm - production fork `RockChinQ/LangBot-LandingPage-Vue` matches upstream. This is - the actual fork in the existing sync script (not the similarly named fork - mentioned in the original migration proposal). Observe Cloudflare Pages - deployment and check `https://langbot.app/docs/`, localized pages, assets, - sitemap and canonical URLs. Verify Wiki pages after publication. +## Cutover order -The four root workflows own publication and validation. Nested workflow stubs -are intentionally inert when split into the mirror. No direct writes should be -made to the mirror after cutover; all edits belong in LangBot `docs/`. +1. Merge the paired landing PR: change `.gitmodules`, the pinned `docs` gitlink, + and `scripts/build-embedded-docs.mjs` together. Verify its full `pnpm build`. +2. Merge the main-repository publication changes. Keep publishers disabled until + both repositories are ready; the new flag is `DOCS_PUBLISH_ENABLED`, not + `DOCS_MIRROR_ENABLED`. The old flag does not enable these jobs. +3. Stop the old repository's Blog, Wiki and landing workflows and let any active + writers finish. The owner can archive `langbot-app/langbot-docs` once the + new publication chain is verified. Never write disabling commits to it as + part of the new publication chain. +4. Verify `LANGBOT_WIKI_PUSH_TOKEN`, or the existing `RELEASE_UPLOAD_GITHUB_TOKEN` + fallback, has access to LangBot, landing, production fork and Wiki. No token + value belongs in logs. Publication only updates gitlinks/content, never + workflow files in another repository, and no docs-mirror permission is needed. +5. Set `DOCS_PUBLISH_ENABLED=true` in LangBot. Dispatch + `sync-landing-submodule.yml` on **master**. It also brings the production fork + up to date; confirm the fork matches landing main and observe Cloudflare's + resulting deployment before accepting the cutover. +6. Dispatch `sync-github-wiki.yml` and `sync-blog-articles.yml` on **master**. + The Blog job commits only `docs/` article paths to LangBot using a token whose + pushes trigger subsequent landing/Wiki workflows. Wiki images and attribution + now point to LangBot `master/docs`, so new assets do not depend on the archive. +7. Check `/docs/`, the en/zh/ja guide pages, assets, sitemap, Markdown exports, + Wiki content and Blog idempotency. A successful Wiki build is not proof of + publication: inspect the separate publish job. -`docs/context7.json` continues to identify `langbot-app/langbot-docs` because that -repository remains available with the same tree layout and original history. -Verify Context7 ingestion after the first mirror update; no library re-registration -is needed merely to relocate the authoring source. +## Context7 -Disable `DOCS_MIRROR_ENABLED` to pause publishers if cutover fails. A failed -mirror ancestry check must be investigated and reconciled, never bypassed by a -force push. Do not restore direct mirror writers while LangBot is authoritative. +The root `context7.json` follows the published Context7 schema and selects +`master:docs/en`. Context7's current parser indexes English documentation. +The old nested ownership configuration belonged to the legacy repository and +has been removed; it does not transfer ownership of a Context7 library. +The owner must add/refresh `https://github.com/langbot-app/LangBot` in Context7 +and verify a result from `docs/en` after merge. The existing +`/langbot-app/langbot-docs` index is only a historical snapshot after archival. +Do not report an index refresh as complete solely because this config exists. + +## Local validation and recovery + +Use Node.js 24. In `docs/`, run `npm ci`, `npm run typecheck`, `npm test`, and +`npm run build`. In landing, initialize the submodule and run `pnpm install +--frozen-lockfile`, `pnpm test`, and `pnpm build`. + +Disable `DOCS_PUBLISH_ENABLED` to pause publication. If landing still points at +the legacy repository or the production fork has diverged, reconcile through +reviewed changes; do not bypass the synchronization checks. Keep the last +working deployment available until the replacement passes verification. diff --git a/docs/README.md b/docs/README.md index 377a8bbda..54d2e26d4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # [LangBot](https://github.com/langbot-app/LangBot) 官方文档 -> 文档源码位于 [LangBot/docs](https://github.com/langbot-app/LangBot/tree/main/docs),[langbot-docs](https://github.com/langbot-app/langbot-docs) 仅作为发布 mirror。 +> 文档源码位于 [LangBot/docs](https://github.com/langbot-app/LangBot/tree/master/docs),[langbot-docs](https://github.com/langbot-app/langbot-docs) 将作为历史归档保留。 > [LangBot 代码仓库](https://github.com/langbot-app/LangBot) > 这是 LangBot 4.0 的文档,3.0 文档请见 [langbot-docs 的 v3 分支](https://github.com/langbot-app/langbot-docs/tree/v3) diff --git a/docs/README_EN.md b/docs/README_EN.md index da4dd37d4..92f9588f4 100644 --- a/docs/README_EN.md +++ b/docs/README_EN.md @@ -1,4 +1,4 @@ -> Edit documentation in [LangBot/docs](https://github.com/langbot-app/LangBot/tree/main/docs). The [langbot-docs repository](https://github.com/langbot-app/langbot-docs) is a generated publication mirror. Run the commands below from `LangBot/docs/`. +> Edit documentation in [LangBot/docs](https://github.com/langbot-app/LangBot/tree/master/docs). The [langbot-docs repository](https://github.com/langbot-app/langbot-docs) is retained as a historical archive. Run the commands below from `LangBot/docs/`. # [LangBot](https://github.com/langbot-app/LangBot) Official Documentation diff --git a/docs/context7.json b/docs/context7.json deleted file mode 100644 index 2f7dafcd4..000000000 --- a/docs/context7.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "url": "https://context7.com/langbot-app/langbot-docs", - "public_key": "pk_lYnjPxdPQabvC87DxU34F" -} diff --git a/docs/node_modules b/docs/node_modules new file mode 120000 index 000000000..c07fdde59 --- /dev/null +++ b/docs/node_modules @@ -0,0 +1 @@ +../../LangBot/docs/node_modules \ No newline at end of file diff --git a/docs/scripts/build-github-wiki.py b/docs/scripts/build-github-wiki.py index 9304b92bf..2d35cc11b 100755 --- a/docs/scripts/build-github-wiki.py +++ b/docs/scripts/build-github-wiki.py @@ -73,7 +73,7 @@ def rewrite_url(source: PurePosixPath, target: str, *, image: bool = False) -> s return target if image: path = parsed.path.lstrip("/") if parsed.path.startswith("/") else (source.parent / parsed.path).as_posix() - return urlunsplit(("https", "raw.githubusercontent.com", f"/langbot-app/langbot-docs/main/{path}", parsed.query, parsed.fragment)) + return urlunsplit(("https", "raw.githubusercontent.com", f"/langbot-app/LangBot/master/docs/{path}", parsed.query, parsed.fragment)) return resolve_doc_target(source, target) or target @@ -235,7 +235,7 @@ def build(root: Path, output: Path) -> int: home = [ "# LangBot Documentation", "", - "This GitHub Wiki is automatically synchronized from [langbot-app/langbot-docs](https://github.com/langbot-app/langbot-docs).", + "This GitHub Wiki is automatically synchronized from [LangBot/docs](https://github.com/langbot-app/LangBot/tree/master/docs).", "", ] for language, label in LANGUAGES.items(): @@ -246,7 +246,7 @@ def build(root: Path, output: Path) -> int: (output / "Home.md").write_text("\n".join(home).rstrip() + "\n", encoding="utf-8") (output / "_Sidebar.md").write_text(build_sidebar(root, titles), encoding="utf-8") (output / "_Footer.md").write_text( - "Automatically synchronized from [langbot-app/langbot-docs](https://github.com/langbot-app/langbot-docs).\n", + "Automatically synchronized from [LangBot/docs](https://github.com/langbot-app/LangBot/tree/master/docs).\n", encoding="utf-8", ) return len(documents) diff --git a/docs/scripts/sync-landing-submodule.py b/docs/scripts/sync-landing-submodule.py index 93b4fddc6..7feb83034 100644 --- a/docs/scripts/sync-landing-submodule.py +++ b/docs/scripts/sync-landing-submodule.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 """Advance only the landing docs gitlink, then sync its production fork.""" import argparse +import base64 +import configparser import json import os import urllib.error import urllib.request -SOURCE = "langbot-app/langbot-docs" +SOURCE = "langbot-app/LangBot" TARGET = "langbot-app/langbot-landing-page" DEPLOY = "RockChinQ/LangBot-LandingPage-Vue" @@ -25,14 +27,24 @@ def api(method, path, data=None): return json.load(response) -def head(call, repo): - return call("GET", repo + "/git/ref/heads/main")["object"]["sha"] +def head(call, repo, branch="main"): + return call("GET", repo + "/git/ref/heads/" + branch)["object"]["sha"] def sync(call=api, dry_run=False): for attempt in range(5): - source = head(call, SOURCE) # Coalesce queued events onto current main. + source = head(call, SOURCE, "master") parent = head(call, TARGET) + # Check the same target revision used to build the candidate tree. Never + # put a LangBot SHA into a gitlink still configured for the old repository. + modules = call("GET", TARGET + "/contents/.gitmodules?ref=" + parent) + config = configparser.ConfigParser(interpolation=None) + config.read_string(base64.b64decode(modules["content"]).decode("utf-8")) + section = 'submodule "docs"' + if (config.get(section, "path", fallback=None) != "docs" + or config.get(section, "url", fallback=None) != "https://github.com/langbot-app/LangBot.git" + or config.get(section, "branch", fallback=None) != "master"): + raise RuntimeError("Landing docs must point to LangBot/master before synchronization") commit = call("GET", TARGET + "/git/commits/" + parent) tree = call("GET", TARGET + "/git/trees/" + commit["tree"]["sha"]) entry = next(item for item in tree["tree"] if item["path"] == "docs") @@ -52,7 +64,7 @@ def sync(call=api, dry_run=False): "tree": [{"path": "docs", "mode": "160000", "type": "commit", "sha": source}], }) candidate = call("POST", TARGET + "/git/commits", { - "message": "docs: sync langbot-docs@" + source[:12], + "message": "docs: sync LangBot@" + source[:12], "tree": updated["sha"], "parents": [parent], "author": {"name": "github-actions[bot]", "email": "41898282+github-actions[bot]@users.noreply.github.com"}, }) diff --git a/docs/tests/test_build_github_wiki.py b/docs/tests/test_build_github_wiki.py index e8dd85593..130db8cca 100644 --- a/docs/tests/test_build_github_wiki.py +++ b/docs/tests/test_build_github_wiki.py @@ -36,7 +36,7 @@ description: "ignored" self.assertIn("> **提示**", output) self.assertIn("[模型文档](zh-usage-models-readme)", output) self.assertIn( - "https://raw.githubusercontent.com/langbot-app/langbot-docs/main/images/ui.png", + "https://raw.githubusercontent.com/langbot-app/LangBot/master/docs/images/ui.png", output, ) diff --git a/docs/tests/test_sync_landing_submodule.py b/docs/tests/test_sync_landing_submodule.py index afcf72f34..0dd4ea16d 100644 --- a/docs/tests/test_sync_landing_submodule.py +++ b/docs/tests/test_sync_landing_submodule.py @@ -1,4 +1,5 @@ """Offline API-contract tests; the workflow run verifies real GitHub writes.""" +import base64 import importlib.util from pathlib import Path import unittest @@ -10,8 +11,9 @@ spec.loader.exec_module(sync) class FakeAPI: - def __init__(self, already=False, conflict=False, status="ahead"): + def __init__(self, already=False, conflict=False, status="ahead", migrated=True): self.calls = [] + self.migrated = migrated self.pin = "new-docs" if already else "old-docs" self.landing = "landing-parent" self.deploy = "old-deploy" @@ -20,7 +22,12 @@ class FakeAPI: def __call__(self, method, path, data=None): self.calls.append((method, path, data)) - if path.endswith("/git/ref/heads/main"): + if "/contents/.gitmodules?ref=" in path: + repo = "LangBot" if self.migrated else "langbot-docs" + branch = "master" if self.migrated else "main" + content = f'[submodule "docs"]\npath = docs\nurl = https://github.com/langbot-app/{repo}.git\nbranch = {branch}\n' + return {"content": base64.b64encode(content.encode()).decode()} + if "/git/ref/heads/" in path: sha = "new-docs" if path.startswith(sync.SOURCE + "/") else self.deploy if path.startswith(sync.DEPLOY + "/") else self.landing return {"object": {"sha": sha}} if method == "GET" and "/git/commits/" in path: @@ -83,3 +90,16 @@ class SyncTests(unittest.TestCase): with self.assertRaises(RuntimeError): sync.sync(api) self.assertTrue(all(m == "GET" for m, p, d in api.calls)) + + def test_reads_master_and_does_not_contact_archived_repository(self): + api = FakeAPI() + sync.sync(api) + self.assertIn(("GET", "langbot-app/LangBot/git/ref/heads/master", None), api.calls) + self.assertFalse(any("langbot-app/langbot-docs/" in p for _, p, _ in api.calls)) + + def test_old_submodule_configuration_fails_before_any_write(self): + for already in [False, True]: + api = FakeAPI(already=already, migrated=False) + with self.assertRaisesRegex(RuntimeError, "must point to LangBot/master"): + sync.sync(api) + self.assertTrue(all(m == "GET" for m, _, _ in api.calls))