diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6c9d7be5..131f4a87 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,13 +1,17 @@ # ───────────────────────────────────────────────────────────── -# VextJS 文档站 — GitHub Pages 自动部署 +# VextJS 文档站 — 保留源码仓库 Pages,并新增组织根站点 # # 触发条件: # - main 的 push-triggered CI 成功后,部署该次 CI 的精确 SHA -# - 手动触发(workflow_dispatch) +# - 在 main 手动触发(workflow_dispatch) # # 流水线结构: # 1. build — 在 website/ 目录安装依赖并执行 rspress build -# 2. deploy — 部署到当前仓库 GitHub Pages: /vextjs/ +# 2. deploy-pages — 保留源码仓库的 /vextjs/ Pages 发布 +# 3. deploy-organization — 新增独立组织根站点 https://vextjs.github.io/ +# +# 首次配置:目标仓库初始化 main,Pages 选择 main / (root), +# 添加可写 Deploy Key,并在源码仓库保存 VEXT_DOCS_DEPLOY_KEY。 # # @see website/package.json §scripts # @see https://rspress.dev/guide/start/deploy @@ -23,16 +27,12 @@ on: - main workflow_dispatch: -# 同一分支同时只允许一个部署运行,新运行会取消旧运行 -concurrency: - group: pages-${{ github.event.workflow_run.head_branch || github.ref_name || github.run_id }} - cancel-in-progress: true - -# GitHub Pages 部署所需权限 +# 源码与产物读取使用 GITHUB_TOKEN;目标仓库写入使用专用 SSH Deploy Key。 permissions: contents: read - pages: write - id-token: write + +env: + DEPLOY_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} # ── Jobs ───────────────────────────────────────────────────── @@ -41,28 +41,43 @@ jobs: # 1. 构建文档站 # ──────────────────────────────────────────────────────────── build: - name: Build Docs + name: Build Docs (${{ matrix.site }}) runs-on: ubuntu-latest if: >- ${{ - github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') || ( github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && - github.event.workflow_run.head_branch == 'main' + github.event.workflow_run.head_branch == 'main' && + github.event.workflow_run.head_repository.full_name == github.repository ) }} + permissions: + contents: read + pages: write + strategy: + fail-fast: false + matrix: + include: + - site: project + base: /vextjs/ + url: https://devcodex-labs.github.io/vextjs + - site: organization + base: / + url: https://vextjs.github.io env: - DEPLOY_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} + VEXT_DOCS_BASE: ${{ matrix.base }} + VEXT_DOCS_SITE_URL: ${{ matrix.url }} steps: - name: Checkout uses: actions/checkout@v7 with: fetch-depth: 0 # 获取完整提交历史,rspress lastUpdated 功能需要 ref: ${{ env.DEPLOY_SHA }} + persist-credentials: false - name: Verify deployment commit - if: github.event_name == 'workflow_run' shell: bash run: | ACTUAL_SHA=$(git rev-parse HEAD) @@ -71,14 +86,7 @@ jobs: echo "Checked out SHA: $ACTUAL_SHA" exit 1 fi - echo "Deploying successful CI SHA: $ACTUAL_SHA" - - - name: Resolve deployment target - id: target - shell: bash - run: | - echo "docs_base=/vextjs/" >> "$GITHUB_OUTPUT" - echo "site_url=https://devcodex-labs.github.io/vextjs" >> "$GITHUB_OUTPUT" + echo "Building deployment SHA: $ACTUAL_SHA" - name: Setup Node.js uses: actions/setup-node@v7 @@ -87,7 +95,8 @@ jobs: cache: "npm" cache-dependency-path: website/package-lock.json - - name: Setup GitHub Pages + - name: Setup source repository Pages + if: matrix.site == 'project' uses: actions/configure-pages@v6 - name: Install dependencies @@ -106,47 +115,190 @@ jobs: run: npm run build:publish -- --rollout=final env: NODE_ENV: production - VEXT_DOCS_BASE: ${{ steps.target.outputs.docs_base }} - VEXT_DOCS_SITE_URL: ${{ steps.target.outputs.site_url }} - - name: Rewrite robots sitemap URL + - name: Prepare Pages files shell: bash run: | cat > website/dist/robots.txt <> "$GITHUB_OUTPUT" + else + echo "Skipping superseded build: $DEPLOY_SHA (main is $CURRENT_SHA)" + echo "publish=false" >> "$GITHUB_OUTPUT" + fi + - name: Check organization deployment setup + id: organization + if: steps.current.outputs.publish == 'true' + env: + DOCS_DEPLOY_KEY: ${{ secrets.VEXT_DOCS_DEPLOY_KEY }} + shell: bash + run: | + if [[ -n "$DOCS_DEPLOY_KEY" ]]; then + echo "ready=true" >> "$GITHUB_OUTPUT" + else + echo "ready=false" >> "$GITHUB_OUTPUT" + echo "::notice::Skipping organization Pages publication until VEXT_DOCS_DEPLOY_KEY is configured." + fi + + # 原源码仓库 Pages 保留官方 Actions 发布流程,与组织站点并行。 + deploy-pages: + name: Publish source repository Pages + runs-on: ubuntu-latest + needs: prepare-deployment + if: needs.prepare-deployment.outputs.current == 'true' + permissions: + contents: read + pages: write + id-token: write + concurrency: + group: vextjs-docs-project + cancel-in-progress: false environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages + - name: Check current main commit + id: current + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + CURRENT_SHA=$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/main" --jq '.object.sha') + if [[ "$CURRENT_SHA" == "$DEPLOY_SHA" ]]; then + echo "publish=true" >> "$GITHUB_OUTPUT" + else + echo "Skipping superseded build: $DEPLOY_SHA (main is $CURRENT_SHA)" + echo "publish=false" >> "$GITHUB_OUTPUT" + fi + + - name: Deploy source repository Pages + if: steps.current.outputs.publish == 'true' id: deployment uses: actions/deploy-pages@v5 - - name: Print deployment URL + # ──────────────────────────────────────────────────────────── + # 新增组织根站点;其密钥或推送失败不阻止原站点的独立发布任务。 + # ──────────────────────────────────────────────────────────── + deploy-organization: + name: Publish to vextjs.github.io + runs-on: ubuntu-latest + needs: prepare-deployment + if: >- + needs.prepare-deployment.outputs.current == 'true' && + needs.prepare-deployment.outputs.organization_ready == 'true' + + # 只串行化已通过构建且仍对应 main 的发布,不中断正在进行的 Git 推送。 + concurrency: + group: vextjs-docs-organization + cancel-in-progress: false + + environment: + name: organization-docs + url: https://vextjs.github.io/ + + env: + DOCS_PUBLISH_REPOSITORY: vextjs/vextjs.github.io + VEXT_DOCS_SITE_URL: https://vextjs.github.io + + steps: + - name: Check current main commit + id: current + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + CURRENT_SHA=$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/main" --jq '.object.sha') + if [[ "$CURRENT_SHA" != "$DEPLOY_SHA" ]]; then + echo "Skipping superseded build: $DEPLOY_SHA (main is $CURRENT_SHA)" + echo "publish=false" >> "$GITHUB_OUTPUT" + else + echo "publish=true" >> "$GITHUB_OUTPUT" + fi + + - name: Verify deployment key + if: steps.current.outputs.publish == 'true' + env: + DOCS_DEPLOY_KEY: ${{ secrets.VEXT_DOCS_DEPLOY_KEY }} + shell: bash + run: | + if [[ -z "$DOCS_DEPLOY_KEY" ]]; then + echo "::error::Set VEXT_DOCS_DEPLOY_KEY to the writable Deploy Key for ${DOCS_PUBLISH_REPOSITORY}." + exit 1 + fi + + - name: Checkout Pages repository + if: steps.current.outputs.publish == 'true' + uses: actions/checkout@v7 + with: + repository: ${{ env.DOCS_PUBLISH_REPOSITORY }} + ref: main + path: site + ssh-key: ${{ secrets.VEXT_DOCS_DEPLOY_KEY }} + + - name: Download verified docs + if: steps.current.outputs.publish == 'true' + uses: actions/download-artifact@v8 + with: + name: docs-dist-organization + path: docs-dist + + - name: Publish verified docs + if: steps.current.outputs.publish == 'true' + shell: bash run: | - echo "✅ 文档站已部署至: ${{ steps.deployment.outputs.page_url }}" + test -f docs-dist/index.html + test -f docs-dist/zh/index.html + test -f docs-dist/docs-manifest.json + test -f docs-dist/sitemap.xml + git -C site rm -r --ignore-unmatch -- . + cp -a docs-dist/. site/ + touch site/.nojekyll + cd site + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add --all + if git diff --cached --quiet; then + echo "Documentation is already current." + else + git commit -m "docs: publish ${DEPLOY_SHA}" + git push origin HEAD:main + fi + echo "Published to ${DOCS_PUBLISH_REPOSITORY}; Pages will update ${VEXT_DOCS_SITE_URL}/" diff --git a/README.md b/README.md index 98fdfa63..a7350090 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ AI-first describes the engineering surface: conventions, scaffolding, typed cont The npm package name is `vextjs`; the CLI binary is `vext`. Requires Node.js **`^20.19.0 || >=22.12.0`**. Cold-start from the registry with **`npx vextjs …`**. After install, use project scripts or local `npx vext`. -**Docs:** https://devcodex-labs.github.io/vextjs/ · **Migration:** [MIGRATION.md](./MIGRATION.md) · **Changelog:** [CHANGELOG.md](./CHANGELOG.md) +**Docs:** https://devcodex-labs.github.io/vextjs/ · **Organization docs:** https://vextjs.github.io/ · **Migration:** [MIGRATION.md](./MIGRATION.md) · **Changelog:** [CHANGELOG.md](./CHANGELOG.md) --- diff --git a/scripts/validation/verify-documentation-contract.mjs b/scripts/validation/verify-documentation-contract.mjs index 6f146ac1..1ea4007c 100644 --- a/scripts/validation/verify-documentation-contract.mjs +++ b/scripts/validation/verify-documentation-contract.mjs @@ -1908,6 +1908,7 @@ function verifyReadmePublicEntryContract() { "res.render", "Apache-2.0", "https://devcodex-labs.github.io/vextjs/", + "https://vextjs.github.io/", "https://github.com/devcodex-labs/vextjs", "https://devcodex-labs.github.io/vextjs/llms.txt", "https://devcodex-labs.github.io/vextjs/capabilities.json", @@ -1932,7 +1933,6 @@ function verifyReadmePublicEntryContract() { "rateLimit.enabled: true", ]); forbidTokens("README.md", [ - "vextjs.github.io", "github.com/vextjs/vext", "License: MIT", "Node.js-%3E%3D18.0.0", @@ -2616,8 +2616,8 @@ function verifyRenderedMachineArtifacts() { locale: "en", indexName: "llms.txt", fullName: "llms-full.txt", - fullUrl: "https://devcodex-labs.github.io/vextjs/llms-full.txt", - alternateUrl: "https://devcodex-labs.github.io/vextjs/zh/llms.txt", + fullUrl: `${siteUrl}/llms-full.txt`, + alternateUrl: `${siteUrl}/zh/llms.txt`, optionalHeading: "## Other language", forbiddenOptionalHeading: "## Optional", }, @@ -2625,8 +2625,8 @@ function verifyRenderedMachineArtifacts() { locale: "zh", indexName: "zh/llms.txt", fullName: "zh/llms-full.txt", - fullUrl: "https://devcodex-labs.github.io/vextjs/zh/llms-full.txt", - alternateUrl: "https://devcodex-labs.github.io/vextjs/llms.txt", + fullUrl: `${siteUrl}/zh/llms-full.txt`, + alternateUrl: `${siteUrl}/llms.txt`, optionalHeading: "## 可选语言", forbiddenOptionalHeading: "## Optional", }, diff --git a/website/docs/en/guide/deployment.md b/website/docs/en/guide/deployment.md index 2d80876a..2df38d1f 100644 --- a/website/docs/en/guide/deployment.md +++ b/website/docs/en/guide/deployment.md @@ -887,7 +887,24 @@ Run Job schedulers and workers as processes separate from HTTP. `npx vextjs job ## Publish the documentation site -When maintaining this VextJS repository, `.github/workflows/docs.yml` publishes the documentation to [GitHub Pages](https://devcodex-labs.github.io/vextjs/) with base path `/vextjs/`. Automatic publishing follows a successful main push CI and checks out that exact SHA. This is separate from application deployment above. The DevCodex Labs organization homepage is maintained in a separate repository. +When maintaining this VextJS repository, documentation sources remain in `devcodex-labs/vextjs`. The same commit is published to two documentation sites. These are separate from application deployment above. + +| Site | URL | Base path | Publishing method | +| ------------------------------- | ---------------------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------- | +| Source repository documentation | [https://devcodex-labs.github.io/vextjs/](https://devcodex-labs.github.io/vextjs/) | `/vextjs/` | GitHub Actions Pages deployment in the source repository | +| Organization documentation | [https://vextjs.github.io/](https://vextjs.github.io/) | `/` | Push to the separate public repository `vextjs/vextjs.github.io`, then publish through that repository's Pages | + +Keep **GitHub Actions** selected in the source repository's `Settings → Pages → Source`. Complete this setup once for the additional organization site; subsequent builds publish automatically: + +1. Create the public repository `vextjs.github.io` in the `vextjs` organization and initialize its `main` branch. This repository is reserved for build output: publishing replaces the site files at its root, removes stale output, and preserves Git commit history. +2. Create a dedicated SSH Deploy Key for that repository. Add the public key in the site repository's `Settings → Deploy keys` with **Allow write access** enabled. Save the private key as `VEXT_DOCS_DEPLOY_KEY` in the source repository's `Settings → Secrets and variables → Actions`. Never put the private key in source code, documentation, or chat messages. +3. In the site repository's `Settings → Pages`, select **Deploy from a branch**, branch **main**, and folder **/(root)**. The repository name determines the organization root URL; no CNAME is needed. + +`.github/workflows/docs.yml` builds with Node.js 22 and sets `VEXT_DOCS_BASE` and `VEXT_DOCS_SITE_URL` separately for the paths and URLs in the table. Both artifacts are validated and uploaded separately to keep root-path assets distinct from `/vextjs/` assets. Automatic publishing follows a successful source-repository `main` push CI and checks out that exact SHA. + +After both builds and their complete documentation publishing checks pass, independent publishing jobs start: the source repository uses a Pages artifact and the built-in `GITHUB_TOKEN`; the organization site uses `VEXT_DOCS_DEPLOY_KEY` to push its output. Until that key is configured, the workflow skips organization publication with a notice and continues publishing the source site. After setup, a failed organization push also does not block the source repository's publishing job. Either build failing prevents both sites from publishing in that run. Before publishing, each job checks the current source `main` commit and skips builds superseded by a later commit. + +To rebuild and republish both sites, select `main` in the source repository's `Actions → Deploy Docs → Run workflow`. This manual entry also runs the complete documentation checks; other branches cannot publish to the production site through it. After the organization site's push succeeds, wait for the target repository's Pages publishing job to finish before checking the updated site. ## Next step diff --git a/website/docs/zh/guide/deployment.md b/website/docs/zh/guide/deployment.md index f1e1ee22..81aa4163 100644 --- a/website/docs/zh/guide/deployment.md +++ b/website/docs/zh/guide/deployment.md @@ -890,7 +890,24 @@ Job scheduler 和 worker 使用独立于 HTTP 的进程。用 `npx vextjs job sc ## 文档站发布 -维护 VextJS 仓库本身时,文档站由 `.github/workflows/docs.yml` 发布到 [GitHub Pages](https://devcodex-labs.github.io/vextjs/),base path 为 `/vextjs/`。自动发布跟随 main 的成功 push CI,检出对应精确 SHA;它与上文业务应用部署是两套流程。DevCodex Labs 组织主页由独立仓库维护。 +维护 VextJS 仓库本身时,文档源保存在 `devcodex-labs/vextjs`,同一提交发布到两个文档站。它们与上文业务应用部署是两套流程。 + +| 站点 | 访问地址 | base path | 发布方式 | +| -------------- | ---------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------- | +| 源码仓库文档站 | [https://devcodex-labs.github.io/vextjs/](https://devcodex-labs.github.io/vextjs/) | `/vextjs/` | 源码仓库的 GitHub Actions Pages 部署 | +| 组织文档站 | [https://vextjs.github.io/](https://vextjs.github.io/) | `/` | 推送到独立公开仓库 `vextjs/vextjs.github.io` 后,由该仓库的 Pages 发布 | + +源码仓库的 `Settings → Pages → Source` 保持 **GitHub Actions**。新增组织站需要以下首次配置,之后不需要手动复制构建结果: + +1. 在 `vextjs` 组织创建公开仓库 `vextjs.github.io`,初始化 `main` 分支。该仓库专门保存构建结果;发布时会替换根目录中的站点文件,删除旧产物并保留 Git 提交历史。 +2. 为该仓库创建专用 SSH Deploy Key。公钥放入站点仓库的 `Settings → Deploy keys`,勾选 **Allow write access**;私钥通过源码仓库的 `Settings → Secrets and variables → Actions` 保存为 `VEXT_DOCS_DEPLOY_KEY`。不要把私钥放进源码、文档或聊天消息。 +3. 在站点仓库的 `Settings → Pages` 选择 **Deploy from a branch**,分支选择 **main**、目录选择 **/(root)**。该组织根地址由仓库名称决定,无需配置 CNAME。 + +`.github/workflows/docs.yml` 使用 Node.js 22,分别设置 `VEXT_DOCS_BASE` 与 `VEXT_DOCS_SITE_URL`,按表中的路径和地址构建两个站点。两份产物分别上传和校验,避免根路径与 `/vextjs/` 的资源链接混用。自动发布跟随源码仓库 `main` 的成功 push CI,检出该次 CI 的精确 SHA。 + +两份构建及完整文档发布校验均通过后,工作流启动独立发布任务:源码仓库使用 Pages artifact 与内置 `GITHUB_TOKEN`;组织站使用 `VEXT_DOCS_DEPLOY_KEY` 推送产物。尚未配置该密钥时,工作流跳过组织站发布并记录提示,源码仓库仍照常发布。配置完成后的组织站推送失败,也不会阻止源码仓库的发布任务;任一构建失败会阻止本次两个站点的发布。发布前再次检查源码 `main` 的当前提交;已被后续提交替代的构建会跳过发布。 + +需要重新发布时,在源码仓库的 `Actions → Deploy Docs → Run workflow` 选择 `main`,同时重建和发布两个站点。手动入口同样执行完整文档校验;其他分支不能通过该入口发布到生产站点。组织站的推送任务成功后,仍需等待目标仓库的 Pages 发布任务完成才能看到更新。 ## 下一步