diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index fe576843f..234b07885 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,43 +1,79 @@ name: Node CI on: + pull_request: push: - branches: - - master + branches: [master, 'codex/**'] + workflow_dispatch: + workflow_call: + inputs: + pages-artifact: + description: Prepare the checked docs directory for the separate Pages workflow + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: checks-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} jobs: - build: - + checks: + name: Checks and build runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [20.x] - + timeout-minutes: 30 + env: + CI: true + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - node-version: ${{ matrix.node-version }} - - name: install, lint, build, and test + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: .node-version + package-manager-cache: false + - name: Install the pinned package manager + run: npm install --global --force yarn@1.22.22 + - name: Validate workflow syntax run: | - yarn - yarn test:playback - yarn lint - yarn build:all - yarn test:dash-control - env: - CI: true - - - name: Deploy to GitHub Pages - if: success() + curl --fail --silent --show-error --location https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz --output "$RUNNER_TEMP/actionlint.tar.gz" + printf '%s %s\n' '8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8' "$RUNNER_TEMP/actionlint.tar.gz" | sha256sum --check + tar -xzf "$RUNNER_TEMP/actionlint.tar.gz" -C "$RUNNER_TEMP" actionlint + "$RUNNER_TEMP/actionlint" -color + - name: Frozen install run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add -f docs - git commit -m 'Deploy to GitHub Pages' - git subtree split --prefix docs master -b gh-pages-temp - git push origin gh-pages-temp:refs/heads/gh-pages --force - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + mkdir -p refactor/.cache/ci + yarn install --frozen-lockfile --non-interactive 2>&1 | tee refactor/.cache/ci/install.log + - name: Read-only checks and tests + run: yarn ci:check 2>&1 | tee refactor/.cache/ci/check.log + - name: Build libraries and documentation + run: yarn ci:build 2>&1 | tee refactor/.cache/ci/build.log + - name: Record source and tool versions + if: always() + run: | + mkdir -p refactor/.cache/ci + git rev-parse HEAD > refactor/.cache/ci/source.txt + node --version > refactor/.cache/ci/node.txt + yarn --version > refactor/.cache/ci/yarn.txt + sha256sum yarn.lock > refactor/.cache/ci/lock.sha256 + git diff --stat > refactor/.cache/ci/generated-diff.txt + - name: Upload available logs + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: checks-${{ github.run_id }}-${{ github.run_attempt }} + path: refactor/.cache/ci/ + include-hidden-files: true + retention-days: 14 + if-no-files-found: warn + - name: Prepare Pages artifact + if: inputs.pages-artifact && github.ref == 'refs/heads/master' + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: docs + include-hidden-files: true diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 000000000..ff2f1e2e1 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,34 @@ +name: Deploy Pages + +on: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + validate: + if: github.ref == 'refs/heads/master' && vars.PAGES_DEPLOY_ENABLED == 'true' + uses: ./.github/workflows/nodejs.yml + with: + pages-artifact: true + + deploy: + if: github.ref == 'refs/heads/master' && vars.PAGES_DEPLOY_ENABLED == 'true' + needs: validate + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy the validated artifact + id: deployment + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/AGENTS.md b/AGENTS.md index 118101108..ab762f3ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,7 +74,7 @@ Use the repo scripts rather than ad hoc bundler commands. ### Development ```bash -npm run dev +yarn dev ``` Starts the local dev site on port `8082` and interactively selects a package to watch. The selected package is built into: @@ -84,7 +84,7 @@ Starts the local dev site on port `8082` and interactively selects a package to ### Production Build ```bash -npm run build +yarn build ``` Interactive build for one package. Outputs: @@ -95,25 +95,27 @@ Interactive build for one package. Outputs: Build all packages: ```bash -npm run build all +yarn build all ``` ### Other Project Scripts ```bash -npm run build:i18n -npm run build:ts -npm run build:docs -npm run build:llm -npm run build:test -npm run lint -npm run build:all +yarn build:i18n +yarn build:ts +yarn build:docs +yarn build:llm +yarn build:test +yarn lint +yarn build:all ``` Notes: -- `npm run lint` targets package source/types plus scripts and TypeScript demo assets. -- `npm run build:all` is expensive; use it when a change truly spans builds/docs/types/lint together. +- `yarn lint` is read-only; `yarn lint:fix` explicitly fixes the same source/type/script range. +- `yarn ci:check` runs strict toolchain/plan/lint/tests; `yarn ci:build` generates outputs and checks imports. +- See `refactor/ci-setup.md` for CI and separate Pages deployment; remote activation is tracked separately. +- `yarn build:all` is expensive; use it when a change truly spans builds/docs/types/lint together. ## Useful Local URLs @@ -251,9 +253,9 @@ If adding HLS-like quality/audio selection elsewhere: For package-specific builds, the normal flow is: -1. `npm run dev` and pick the package for fast local iteration +1. `yarn dev` and pick the package for fast local iteration 2. validate in `http://localhost:8082` -3. `npm run build` and pick the package when ready to update shippable artifacts +3. `yarn build` and pick the package when ready to update shippable artifacts ## Documentation Expectations diff --git a/package.json b/package.json index 8ae6b10ba..c15dace23 100644 --- a/package.json +++ b/package.json @@ -38,9 +38,16 @@ "test:dash-control": "node --test test/dash-control.test.js", "dev": "npx cross-env NODE_ENV=development node ./scripts/dev.js", "build": "npx cross-env NODE_ENV=production node ./scripts/build.js", - "lint": "npx eslint packages/*/{src,types,package.json} scripts/*.js test/* docs/assets/ts/* --fix", - "build:all": "npm run build all && npm run build:i18n && npm run build:ts && npm run build:docs && npm run lint", - "check:toolchain": "node scripts/check-toolchain.mjs" + "lint": "eslint \"packages/*/{src,types,package.json}\" \"scripts/*.{js,mjs}\" \"test/*\" \"docs/assets/ts/*\" --no-fix", + "build:all": "yarn ci:build && yarn lint", + "check:toolchain": "node scripts/check-toolchain.mjs", + "lint:fix": "eslint \"packages/*/{src,types,package.json}\" \"scripts/*.{js,mjs}\" \"test/*\" \"docs/assets/ts/*\" --fix", + "check:plan": "node refactor/scripts/plan.mjs --check", + "test:node": "node --test test/playback.test.js test/dash-control.test.js test/toolchain.test.js test/build-docs.test.js", + "test:baseline": "node --test refactor/scripts/*.test.mjs", + "ci:check": "yarn check:toolchain --strict && yarn check:plan && yarn lint && yarn test:node && yarn test:baseline", + "ci:build": "yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports", + "test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js" }, "browserslist": "last 1 Chrome version", "devDependencies": { diff --git a/refactor/README.md b/refactor/README.md index 02a8721f2..800500b7d 100644 --- a/refactor/README.md +++ b/refactor/README.md @@ -28,6 +28,7 @@ | [工具链与发布](toolchain-release.md) | TypeScript、Bun、构建、版本管理和发布回退 | | [已实现的开发环境](toolchain-setup.md) | Node/Yarn 固定版本、锁文件、安装命令和实际验证范围 | | [全包大版本策略](version-policy.md) | 每包分别升级一个 major 的目标清单、兼容要求和版本落地步骤 | +| [已实现的 CI 入口](ci-setup.md) | Yarn 检查/构建、只读 lint、Pages 隔离及远端待验收状态 | | [GitHub CI/CD](github-ci-cd.md) | PR/兼容矩阵、构建报告、Pages、npm 发布及远端准入验证 | | [AI 协作流程](ai-workflow.md) | AI 接续工作、任务边界、验证、记录和交接模板 | | [架构决策](decisions.md) | 已选方向、待验证方案及被拒绝方案 | diff --git a/refactor/baselines/ci-validation.json b/refactor/baselines/ci-validation.json new file mode 100644 index 000000000..5a34df2b2 --- /dev/null +++ b/refactor/baselines/ci-validation.json @@ -0,0 +1,61 @@ +{ + "task": "ENG-02", + "date": "2026-09-10", + "sourceCommit": "9a6cfe5c", + "node": "24.21.0", + "yarn": "1.22.22", + "platform": "win32-x64", + "inputs": { + "package.json": "3d5cb61b9a802c19d7cdbb19318ab38562846725d972dcd3cf494d2754c779a8", + "yarn.lock": "5921f159477b65161b48d4b768f462cb05a537b6a257e62732ec9dfaf4d69e7e", + ".node-version": "73fb1b615e2043a933be1c0895cde4358036acc28d785692509b822aa53c761f", + ".github/workflows/nodejs.yml": "803842ba0c5bf5bd1806604ecdf0571792a245fff09865c6dc3c94fd9fbc4a1c", + ".github/workflows/pages.yml": "e63fb5609a98ae745ff5af68ddbf3adf18a9b483fee9d9d05a957674672f3459", + "scripts/build-docs.js": "95aae678608df443ccd14579fa39f2787ac364705f5d0c360ed55e8b0c8ca6e3", + "scripts/build-ts.js": "1e1f3448de0fbd88939c3a2ecaebc8a11a48e3b1b4c23dbaeac8b58a92ab7813", + "test/build-docs.test.js": "4382e9f445118ac02984425673c84b0678f598cc03ac418ec2bf3b0b2bad23d2" + }, + "actionlint": { + "version": "1.7.12", + "windowsArchiveSha256": "6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9", + "linuxArchiveSha256": "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8", + "workflows": 2, + "passed": true, + "localShellcheck": false, + "localPyflakes": false, + "invalidKeyRejected": true + }, + "check": { + "command": "yarn ci:check", + "passed": true, + "behaviorTests": 21, + "baselineTests": 2, + "sourceFilesUnchanged": 426, + "lintFailureRejectedWithoutFixing": true + }, + "declarationGeneration": { + "command": "yarn build:ts", + "filesCompared": 21, + "typescriptPrinterOutputUnchanged": true, + "formattingFixTypes": [ + "layout" + ] + }, + "build": { + "command": "yarn ci:build", + "libraryPackages": 21, + "libraryArtifacts": 63, + "i18n": true, + "editorDeclarations": true, + "vitepress": true, + "postBuildImportSmokeFiles": 3, + "passed": true + }, + "remote": { + "actionsRuns": "unverified", + "branchProtection": "unverified", + "pagesSource": "unverified", + "pagesEnableVariable": "unverified", + "npmPublication": "not performed" + } +} diff --git a/refactor/changes/2026-09-10-ENG-02-readonly-ci.md b/refactor/changes/2026-09-10-ENG-02-readonly-ci.md new file mode 100644 index 000000000..f5988047b --- /dev/null +++ b/refactor/changes/2026-09-10-ENG-02-readonly-ci.md @@ -0,0 +1,35 @@ +# ENG-02:只读 PR 检查与独立 Pages 流程 + +日期:2026-09-10;分支 codex/compatible-modernization;起点 9a6cfe5c。 + +## 改动 + +- lint 改为只读,lint:fix 显式修复,覆盖新增 MJS 工具。新增 ci:check、ci:build、test:node、test:baseline、test:imports、check:plan;旧测试/构建入口保留。 +- Node CI 支持 PR、master/codex/** push、手动和复用调用;固定 Actions SHA、Node/Yarn、冻结安装、只读权限、并发与超时,失败也上传可获得的日志/来源摘要。 +- Pages 改为独立手动 workflow,仅 master 且 PAGES_DEPLOY_ENABLED=true 时准备和部署。复用同一次检查/构建得到的 docs artifact;仅部署作业有 Pages/OIDC 权限,移除 CI 内 Git 提交、subtree 和 force push。 +- 当前仅实现隔离框架,未切换远端 Pages source 或启用变量。合并后原 master 自动部署会停止;CI-02 完成域名/路径/恢复验证后再启用,详见 ci-setup.md。 + +## 检查中发现并修复 + +原 build-docs.js 没有把构建子进程的失败传给调用方,导致 CI 可能错误成功。新增 error/close 处理,并用真实子进程退出 23/0 验证失败和成功均正确传播。 + +声明生成器产生的 46 处格式错误以前由 lint --fix 清理。将 layout 修正纳入声明生成器,只处理它刚生成的文件,不能自动改生产源码或调整类型逻辑。21 份声明经 TypeScript printer 规范化后与格式修正前完全一致,构建后只读 lint 通过。声明拼接本身的设计问题仍由后续类型/站点任务处理。 + +旧 esm/i18n/ssr 文件只有导入和 console,不是完整行为断言;把它们放到构建后的 test:imports,避免检查旧 dist 后宣称候选已测。 + +## 验证 + +- Node 24.21.0 / Yarn 1.22.22,使用 ENG-PM-01 冻结安装的独立环境;开发依赖和 yarn.lock 未变。 +- 最终 ci:check 通过:21 项有行为断言的 Node 测试、2 项发布包/HTTP 基线测试。426 个源码/类型/测试文件检查前后哈希不变。 +- ci:build 的库包、i18n、编辑器声明、VitePress 全流程通过;21 库包 63 产物,构建后另有 3 个导入 smoke 通过。 +- 两份 workflow 经 actionlint 1.7.12 本地检查;Windows 归档 SHA-256 已核对。Windows 本轮禁用不可用的 shellcheck/pyflakes,Linux CI 命令保留其默认检查,远端尚未运行。 +- 负例:故意引入格式错误导致 ci:check 失败且文件字节未被修改;非法 workflow key 被 actionlint 拒绝。负例均在临时副本恢复,没有修改生产文件。 +- 计划/链接/依赖、源码 lint 和 Git 差异检查通过;报告见 [ci-validation.json](../baselines/ci-validation.json)。 + +没有运行新的远端 workflow、发布或推送。最低 Node/跨平台/浏览器/完整类型/候选包矩阵由后续 ENG/CI 任务实施,不能将本次绿色基础检查等同整个重构通过。 + +## 维护与回退 + +根 AGENTS、ci-setup.md、github-ci-cd.md、工具说明和任务状态同步维护。新增第三方工具只有校验归档方式使用的 actionlint 1.7.12;Actions 固定 SHA 和版本在 YAML 注释中,来源是官方发布 tag 对应提交。 + +可撤销 ENG-02 提交恢复旧脚本/workflow 配置;重新执行旧自动部署仍需要核对远端 Pages 状态。下一个任务 BASE-02 使用内置浏览器继续公共 API 快照,再推进事件/生命周期及试点所需测试基础。每项任务继续独立提交。 diff --git a/refactor/ci-setup.md b/refactor/ci-setup.md new file mode 100644 index 000000000..d137f5897 --- /dev/null +++ b/refactor/ci-setup.md @@ -0,0 +1,50 @@ +# PR 检查和 Pages 操作说明 + +## 本地命令 + +使用 toolchain-setup.md 的 Node/Yarn 环境与冻结安装。 + +| 命令 | 行为 | +| --- | --- | +| `yarn lint` | 只读 ESLint,覆盖包源码/声明、JS/MJS 工具、测试和编辑器声明 | +| `yarn lint:fix` | 显式自动修复相同范围 | +| `yarn test:node` | 显式执行 4 个有行为断言的 Node 测试文件,保留原 test:playback/test:dash-control 入口 | +| `yarn test:imports` | 3 个既有包导入 smoke,构建后执行以使用新产物;不把 console 示例视为完整行为断言 | +| `yarn test:baseline` | 固定发布包完整性及本地 HTTP 基线测试;首次可能下载已固定归档到缓存 | +| `yarn ci:check` | 严格 Node/Yarn/锁检查、计划、只读 lint、Node 和基线测试;允许写忽略缓存,不修改源码 | +| `yarn ci:build` | 21 库包、i18n、编辑器声明和文档站构建,以及构建后包导入 smoke;会生成 dist 和 docs 内容 | +| `yarn build:all` | 保留旧入口,执行 ci:build 后只读 lint | + +scripts/build-docs.js 保留原 npm run build 子命令兼容入口,现在传播失败退出码;包管理/锁维护继续使用 Yarn。MOD-02 可再统一旧内部脚本。build-ts.js 仅对刚生成的声明执行 ESLint layout 格式修正,使只读 lint 在构建后仍能通过;不是对生产源码执行自动修复。 + +## PR 和主线 + +.github/workflows/nodejs.yml 在 PR、master/codex/** push 和手动运行时触发,也供 Pages 复用。Checks and build 作业只授予 contents: read,checkout 不保留 Git 凭据,没有提交、推送、npm 发布或部署步骤。PR/推送取消过期运行,手动部署不被中途取消;初始 Ubuntu 作业限时 30 分钟。 + +Actions 固定完整 SHA,Node 来自 .node-version,Yarn 固定 1.22.22;安装使用 frozen-lockfile。actionlint 1.7.12 从官方固定归档取得,先核对提交在 workflow 中的 SHA-256,再执行。没有执行 curl 管道脚本。日志在失败时也尽量上传,包含源码 SHA、工具版本、锁摘要和生成差异概览,保留 14 天。 + +本轮不缓存 node_modules;矩阵、下载缓存、覆盖率、浏览器 trace 和候选 tarball 汇总由 CI-01 等后续任务扩展。当前测试通过不代表尚未实现的类型消费者或浏览器矩阵通过。将来的稳定 required 汇总名称由 CI-01/CI-04 核实后设置,不把本地 YAML 当作分支保护已生效。 + +## Pages 隔离与启用条件 + +.github/workflows/pages.yml 仅支持手动运行,限定 master,且仓库变量 PAGES_DEPLOY_ENABLED 必须为 true。初始启用状态未经远端核实;没有修改该变量或其他 GitHub 设置。保留此门槛用于 CI-02 完成域名、路径和远端配置验收后再启用,不能直接自动部署 PR 产物。 + +Pages 先调用同一检查/构建 workflow,成功后上传同一次运行生成的完整 docs artifact,再在 github-pages 环境的独立作业调用官方 deploy-pages。仅 deploy 作业有 pages: write / id-token: write,没有 Git contents 写权限,也没有原先的 gh-pages force push。发布排队执行,不取消正在执行的发布。 + +已静态核对当前 docs/CNAME 为 github.artplayer.org;上传包含隐藏文件以保留 .nojekyll。docs 根、document、compiled、旧 HTML/编辑器等实际线上路径和历史站点来源仍需 CI-02 逐项预检;目前未确认或修改线上 Pages source。合并该变更后旧 push-master 自动部署会停止,需先完成以下迁移步骤再启用新流程。 + +1. CI-02 记录当前 Pages source、gh-pages ref、域名/DNS、关键 URL 和可恢复的旧产物。 +2. 先验证 artifact 内容及旧路径;设置 Pages source 为 GitHub Actions,核对 github-pages 环境只允许受信任 master 部署和所需审批。 +3. 经实际部署授权后设置 PAGES_DEPLOY_ENABLED=true,在 master 手动运行 Deploy Pages;记录 CI 和部署 URL,逐项探测旧路径。 +4. 出现问题时关闭启用变量,按保存的 Pages source/gh-pages ref 恢复;不默认重新 force push 覆盖历史。恢复旧 workflow 可从 ENG-02 前 Git 历史取回,但执行远端写入仍按部署授权处理。 + +## 待核实的外部状态 + +| 项目 | 当前证据 | 负责任务 | +| --- | --- | --- | +| 新 PR/主线 workflow 实际执行、故障演练 | 仅本地静态和命令验证,无远端 run | CI-04 | +| required checks、fork 权限和环境审批 | 未读取/修改仓库设置 | CI-04 | +| Pages source、启用变量、域名和回退目标 | 只读源码 CNAME,远端未知 | CI-02 | +| npm 权限、trusted publisher、候选发布 | 未配置、未执行 | CI-03/CI-04 | + +参考:[GitHub Pages 自定义 workflow](https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages)、[setup-node](https://github.com/actions/setup-node)、[actionlint 1.7.12](https://github.com/rhysd/actionlint/releases/tag/v1.7.12)。本轮已核对选用 Actions 的实际 action.yml 和发布 tag 对应 SHA;后续升级需重新检查。 diff --git a/refactor/github-ci-cd.md b/refactor/github-ci-cd.md index eb9c1c65f..6909e0487 100644 --- a/refactor/github-ci-cd.md +++ b/refactor/github-ci-cd.md @@ -1,12 +1,12 @@ # GitHub CI/CD 优化与验收 -2026-09-10 用户明确要求在本次重构中优化和增强 GitHub CI/CD。范围包含仓库检查、测试矩阵、构建产物、文档站部署和 npm 发布准备,并持续维护脚本文档及故障处理指南。本文是待实施规范,不代表 workflows 已更新或远端运行成功。 +2026-09-10 用户明确要求在本次重构中优化和增强 GitHub CI/CD。范围包含仓库检查、测试矩阵、构建产物、文档站部署和 npm 发布准备,并持续维护脚本文档及故障处理指南。ENG-02 已实现初步检查与部署隔离,操作见 [ci-setup.md](ci-setup.md);完整矩阵及远端验收仍待后续任务。 -## 当前源码基线 +## ENG-02 前源码基线 -[nodejs.yml](../.github/workflows/nodejs.yml) 是当前唯一 workflow:仅 push master 触发,Ubuntu + Node 20.x,checkout/setup-node 使用 v2;单一 job 执行 yarn 安装、两组 Node 测试、lint 和 build:all,再提交 docs、subtree split 并 force push gh-pages。 +[nodejs.yml](../.github/workflows/nodejs.yml) 曾是唯一 workflow:仅 push master 触发,Ubuntu + Node 20.x,checkout/setup-node 使用 v2;单一 job 执行 yarn 安装、两组 Node 测试、lint 和 build:all,再提交 docs、subtree split 并 force push gh-pages。 -[package.json](../package.json) 中 lint 使用 --fix,build:all 又包含 lint;当前 workflow 没有 PR 触发、真实浏览器矩阵、类型消费者/tarball 检查、显式权限、并发取消、超时或报告上传。上述是文件检查结果,尚未核对 GitHub 的实际历史运行、Pages 配置、分支保护、环境规则或 npm 账号配置。旧流程的部署目标与访问路径必须先核实,不能仅改 YAML 就宣称迁移完成。 +改造前 [package.json](../package.json) 中 lint 使用 --fix,build:all 又包含 lint;当前 workflow 没有 PR 触发、真实浏览器矩阵、类型消费者/tarball 检查、显式权限、并发取消、超时或报告上传。上述是文件检查结果,尚未核对 GitHub 的实际历史运行、Pages 配置、分支保护、环境规则或 npm 账号配置。旧流程的部署目标与访问路径必须先核实,不能仅改 YAML 就宣称迁移完成。 ## 目标流水线 @@ -22,7 +22,7 @@ ENG-02 先建立只读 PR 检查和部署隔离框架,后续 CI 任务逐步 ## CI 的可靠性与运行成本 -- Node/TS/包管理器版本来自 ENG-01、BASE-08 和 Bun 试点;最低消费者兼容与构建所需运行时分别验证,不因 Actions 升级静默提高消费者要求。 +- Node/TS/包管理器版本来自 ENG-01/ENG-PM-01 和 BASE-08;用户指定 Yarn 为包管理器,Bun 仅隔离评估。最低消费者兼容与构建所需运行时分别验证,不因 Actions 升级静默提高消费者要求。 - 覆盖 Linux 与 Windows 的关键脚本;浏览器覆盖 Chromium/Firefox/WebKit 和有需要的正式 Chrome。真机、Cast 等证据仍使用环境矩阵,不将 hosted runner 通过当作真机通过。 - 安装使用选定包管理器的锁定模式;缓存按 OS、运行时、包管理器及锁文件隔离,浏览器缓存还绑定测试工具版本。禁止无关 PR 缓存/产物进入有发布权限的任务。 - 快速检查依据依赖图而非简单路径过滤;共享核心/构建/锁文件变化扩大检查,影响不确定时完整回归。文档单改可缩小范围,但 required 汇总检查必须给出真实结果,不能将失败/取消误报成功或留下永久 pending。 diff --git a/refactor/plan.md b/refactor/plan.md index 586e5ceb8..5ee8ea4d2 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 214 项,范围 22 个包及工作区/示例。 -状态:todo 197 / doing 0 / blocked 0 / done 17 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 196 / doing 0 / blocked 0 / done 18 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -75,7 +75,7 @@ | --- | --- | --- | --- | --- | --- | --- | | ENG-01 | workspace
固定 Node、包管理器与依赖 | BASE-01 | 版本 pin、唯一锁文件、安装说明 | 干净环境可复现,最低 Node 与构建依赖一致,未夹带全量升级 | M | done | | ENG-PM-01 | workspace
按用户选择切换固定 Yarn 包管理器 | ENG-01 | Yarn 固定版本、唯一 yarn.lock、工具检查与安装维护文档 | 干净冻结安装、Node 测试和全部构建通过;记录解析差异和依赖用途,独立提交 | M | done | -| ENG-02 | workspace
拆分只读检查并建立 PR CI | ENG-01, ENG-PM-01 | lint/lint:fix、PR 与主线检查、独立部署任务;遵循 github-ci-cd.md,PR/重构分支触发、最小权限及 workflow 静态检查 | 仓库内检查可执行且不改源码、不发布;required checks 的外部设置状态列入发布台账,不阻塞本地框架建设 | M | todo | +| ENG-02 | workspace
拆分只读检查并建立 PR CI | ENG-01, ENG-PM-01 | lint/lint:fix、PR 与主线检查、独立部署任务;遵循 github-ci-cd.md,PR/重构分支触发、最小权限及 workflow 静态检查 | 仓库内检查可执行且不改源码、不发布;required checks 的外部设置状态列入发布台账,不阻塞本地框架建设 | M | done | | ENG-03 | workspace
建立公共行为与单元测试入口 | ENG-02, BASE-03 | 保留现有 node:test,测试目录/夹具/统一入口 | 已有 19 项回归保留,旧版与候选可用同一夹具运行 | M | todo | | ENG-04 | workspace
建立类型测试基础 | ENG-02, BASE-05 | 根与分包 tsconfig、显式 TS 依赖、正反例测试 | 核心/试点与迁移模块严格检查,未迁移第三方/包历史问题独立台账;明确最低/当前 TS 和各环境类型 | M | todo | | ENG-05 | workspace
建立真实浏览器测试服务 | ENG-03, BASE-04, BASE-08 | Playwright projects、本地 Range/失败媒体服务;复用 docs 页面/样本的状态隔离、错误采集与候选资源映射 | Chromium/Firefox/WebKit 的基础播放 smoke 和报告可执行;以媒体状态断言判定通过,区分轻量用例与真实编辑器交互 | M | todo | @@ -420,3 +420,4 @@ - BASE-HARNESS-01: [记录](changes/2026-09-10-BASE-HARNESS-01-browser-fixture.md) - ENG-01: [记录](changes/2026-09-10-ENG-01-reproducible-toolchain.md) [记录](baselines/toolchain-validation.json) - ENG-PM-01: [记录](changes/2026-09-10-ENG-PM-01-yarn-toolchain.md) [记录](baselines/yarn-validation.json) +- ENG-02: [记录](changes/2026-09-10-ENG-02-readonly-ci.md) [记录](baselines/ci-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index b62756b89..cc69cc9b9 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,11 @@ # 进度与证据 +## 当前实施:ENG-02 只读检查与 CI 基础已完成 + +新增 PR/主线/复用检查与独立手动 Pages 流程;lint 与自动修复拆分,修复文档构建退出码和生成声明格式。23 项 Node/基线测试、3 个构建后导入 smoke、21 库包 63 产物与文档构建通过;426 个源/类型/测试文件只读检查前后不变,actionlint 静态和负例通过。详见 [ENG-02](changes/2026-09-10-ENG-02-readonly-ci.md) 与 [操作说明](ci-setup.md)。 + +当前 214 项任务,18 完成、196 待办。ENG-PM-01 已提交 9a6cfe5c;本轮独立提交 ENG-02。未推送、未启用远端 CI/Pages、未发布;CI-02/CI-04 继续负责远端验收。下一任务 BASE-02,使用内置浏览器保存并校验公共 API 基线。 + ## 当前实施:ENG-PM-01 已完成 Yarn 切换 标准工具链为 Node 24.21.0 / Yarn Classic 1.22.22,唯一维护 yarn.lock。最终干净冻结安装、20 项 Node 测试、只读工具检查、21 库包及文档站构建通过,63 个库产物与 npm 基线 SHA-256 全同。详见 [ENG-PM-01](changes/2026-09-10-ENG-PM-01-yarn-toolchain.md)。既有搜索 peer 警告交 SITE-05 验证。 diff --git a/refactor/tasks.json b/refactor/tasks.json index 50160b4ea..47b5cf50c 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -439,11 +439,14 @@ "ENG-01", "ENG-PM-01" ], - "status": "todo", + "status": "done", "risk": "M", "deliverable": "lint/lint:fix、PR 与主线检查、独立部署任务;遵循 github-ci-cd.md,PR/重构分支触发、最小权限及 workflow 静态检查", "acceptance": "仓库内检查可执行且不改源码、不发布;required checks 的外部设置状态列入发布台账,不阻塞本地框架建设", - "evidence": [] + "evidence": [ + "changes/2026-09-10-ENG-02-readonly-ci.md", + "baselines/ci-validation.json" + ] }, { "id": "ENG-03", diff --git a/refactor/toolchain-setup.md b/refactor/toolchain-setup.md index f478565a3..3b91e6fbe 100644 --- a/refactor/toolchain-setup.md +++ b/refactor/toolchain-setup.md @@ -7,7 +7,7 @@ 1. 切换到 .node-version 指定的 Node,安装 Yarn 1.22.22;例如 `npm install --global yarn@1.22.22`,然后确认 `node --version` 和 `yarn --version`。npm 仅可用于安装 Yarn 工具及消费者兼容检查,不用于维护本仓库依赖锁。 2. 干净 checkout 执行 `yarn install --frozen-lockfile --non-interactive`,禁止 CI 自动更新锁或忽略安装脚本/engines。参见 [Yarn Classic install](https://classic.yarnpkg.com/en/docs/cli/install/)。 3. 执行 `yarn check:toolchain --strict`,核对实际 Node/Yarn、19 个固定开发工具和 22 个 workspace 的声明及传递依赖锁条目。普通检查允许满足最低工具要求的 Node,同时打印标准版本。 -4. 执行 `yarn test:playback`、`yarn test:dash-control`、`yarn build all` 和 `yarn workspace artplayer-vitepress build`。只读 lint 和 PR CI 的拆分由 ENG-02 完成;旧 lint 当前仍有 --fix。 +4. 执行 `yarn test:playback`、`yarn test:dash-control`、`yarn build all` 和 `yarn workspace artplayer-vitepress build`。ENG-02 已拆分只读 lint 与 lint:fix;PR/主线入口和独立 Pages 流程见 ci-setup.md。 私有根包最低 Node 为 ^20.19.0 || >=22.12.0,与原本使用的 Vite 7 一致。本轮验证 Node 24.21.0,其他版本矩阵由 CI-01 接续,不能宣称所有最低环境已经通过。 diff --git a/scripts/build-docs.js b/scripts/build-docs.js index b18b86771..3044b59cf 100644 --- a/scripts/build-docs.js +++ b/scripts/build-docs.js @@ -3,7 +3,13 @@ import spawn from 'cross-spawn' const proc = spawn('npm', ['run', 'build'], { cwd: './packages/artplayer-vitepress/', + stdio: 'inherit', }) -proc.stdout.pipe(process.stdout) -proc.stderr.pipe(process.stderr) +proc.on('error', (error) => { + console.error(error.message) + process.exitCode = 1 +}) +proc.on('close', (code) => { + process.exitCode = code ?? 1 +}) diff --git a/scripts/build-ts.js b/scripts/build-ts.js index b34df6830..e1e231fb4 100644 --- a/scripts/build-ts.js +++ b/scripts/build-ts.js @@ -1,5 +1,6 @@ import fs from 'node:fs' import path from 'node:path' +import { ESLint } from 'eslint' import { glob } from 'glob' function ensureDirExists(filePath) { @@ -57,6 +58,13 @@ console.log(`✨ Built ${artplayerTSoutput}`); pluginFiles.sort() const allFiles = [...pluginFiles, 'artplayer.d.ts'] + const eslint = new ESLint({ fix: true, fixTypes: ['layout'] }) + const results = await eslint.lintFiles(allFiles.map(file => path.join('docs/assets/ts', file))) + await ESLint.outputFixes(results) + if (results.some(result => result.errorCount)) { + const formatter = await eslint.loadFormatter('stylish') + throw new Error(formatter.format(results)) + } const commonJsPath = path.join('docs/assets/js/common.js') const commonJsContent = fs.readFileSync(commonJsPath, 'utf-8') const newLibUris = allFiles.map(file => `'./assets/ts/${file}'`).join(',\n ') diff --git a/test/build-docs.test.js b/test/build-docs.test.js new file mode 100644 index 000000000..7cb39f599 --- /dev/null +++ b/test/build-docs.test.js @@ -0,0 +1,36 @@ +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +// eslint-disable-next-line test/no-import-node-test -- Verify the CLI failure contract using a real child process. +import { test } from 'node:test' +import { fileURLToPath } from 'node:url' + +const root = fileURLToPath(new URL('../', import.meta.url)) + +test('documentation build forwards child failure and success exit codes', () => { + const cache = path.join(root, 'refactor/.cache') + fs.mkdirSync(cache, { recursive: true }) + const fixture = fs.mkdtempSync(path.join(cache, 'docs-exit-')) + try { + const pkg = path.join(fixture, 'packages/artplayer-vitepress') + fs.mkdirSync(pkg, { recursive: true }) + for (const code of [23, 0]) { + fs.writeFileSync(path.join(pkg, 'package.json'), JSON.stringify({ + private: true, + scripts: { build: `node -e "process.exit(${code})"` }, + })) + const result = spawnSync(process.execPath, [path.join(root, 'scripts/build-docs.js')], { + cwd: fixture, + encoding: 'utf8', + }) + assert.equal(result.status, code, result.stderr) + } + } + finally { + const relative = path.relative(cache, fixture) + assert(relative.startsWith('docs-exit-') && !relative.includes(path.sep)) + fs.rmSync(fixture, { recursive: true, force: true }) + } +})