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))