Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 25 additions & 4 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,41 @@ ARG NODE_VERSION
FROM node:${NODE_VERSION}-bookworm-slim

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ARG PACKAGE_MANAGER_PIN
ENV COREPACK_HOME="/corepack"
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME/bin:$PNPM_HOME:$PATH"
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& apt-get install -y --no-install-recommends \
bubblewrap \
ca-certificates \
curl \
file \
git \
iproute2 \
jq \
openssh-client \
procps \
python-is-python3 \
python3 \
ripgrep \
unzip \
&& rm -rf /var/lib/apt/lists/* \
&& git config --system init.defaultBranch main \
&& install -d -m 0755 "$COREPACK_HOME" "$PNPM_HOME" "$PNPM_HOME/bin" \
&& corepack enable --install-directory "$PNPM_HOME" \
&& corepack prepare "${PACKAGE_MANAGER_PIN}" --activate \
&& chmod -R a+rX "$COREPACK_HOME" "$PNPM_HOME"

ARG PLAYWRIGHT_CLI_PACKAGE
RUN npx --yes --package "${PLAYWRIGHT_CLI_PACKAGE}" playwright install-deps chromium
RUN npx --yes --package "${PLAYWRIGHT_CLI_PACKAGE}" playwright install --with-deps chromium

RUN install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*

RUN apt-get update \
&& apt-get install -y --no-install-recommends shellcheck \
Expand Down
9 changes: 9 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
"version": "1.10.1",
"resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:698f5900fac471e046c6e9d4333fec88c0e8d952c713494fa0712dce80baeca0",
"integrity": "sha256:698f5900fac471e046c6e9d4333fec88c0e8d952c713494fa0712dce80baeca0"
}
}
}
25 changes: 21 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
{
"name": "digital",
"init": true,
"build": {
"dockerfile": "Dockerfile",
"args": {
"NODE_VERSION": "24",
"PACKAGE_MANAGER_PIN": "pnpm@11.17.0",
"PLAYWRIGHT_CLI_PACKAGE": "@playwright/test@^1.61.1"
"NODE_VERSION": "24.21.0",
"PLAYWRIGHT_CLI_PACKAGE": "@playwright/test@1.62.0"
}
},
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"containerEnv": {
"PNPM_CONFIG_STORE_DIR": "/pnpm/store",
"DIGITAL_DEPLOYMENT_BUILD_NETWORK": "host"
},
"mounts": [
{
"source": "${devcontainerId}-pnpm-store",
"target": "/pnpm/store",
"type": "volume"
}
],
"customizations": {
"vscode": {
"extensions": ["oxc.oxc-vscode"]
}
}
},
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
"workspaceFolder": "${localWorkspaceFolder}",
"runArgs": ["--network=host"]
}
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
.git
.scratch
.digital
.env
.env.local
.env.*.local
*.env
credentials/
secrets/
**/node_modules
**/dist
**/*.tsbuildinfo
Expand Down
19 changes: 9 additions & 10 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
# CI 运行与证据

默认 `Check` 工作流在三个独立的 GitHub 托管工作区并发运行:根检查仍为 `pnpm run check`;CLI 仍调用本地 `test:e2e` 的受控旅程;Web 仍调用真实 HTTP 浏览器入口。默认范围不需要个人来源凭据、私有语料、开发监听或 Docker。
每次 PR 和推送到 main,`Check` 在独占检出中调用同一个根治理实现,先运行轻检查,再运行全部默认重检查。默认范围包括受控默认资料、正式服务 HTTP/SQLite、安装客户端和 standalone/Compose 部署;不需要个人来源凭据或私有素材。Docker daemon、挂载权限及安装客户端验收所需的浏览器是受支持运行平台的前置条件,缺失时检查失败。

本地可按相同阶段复现(先冻结安装,CLI/Web 先按正式 Turbo 图构建依赖,Web 安装 Chromium):
本地完整验收使用 `pnpm check:full`。CI 收集与上传交接可在独占的新检出中按同一阶段复现:

```sh
pnpm install --frozen-lockfile
pnpm exec turbo run build --filter=@digital/web --filter=@digital/cli --filter=@digital/db-migrations
node --conditions=digital-source scripts/ci-environment.ts run cli
node --conditions=digital-source scripts/ci-environment.ts collect cli
TURBO_CONCURRENCY=2 DIGITAL_AUTOMATION_WORKSPACE="$PWD" pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts run
DIGITAL_AUTOMATION_WORKSPACE="$PWD" pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts collect
```

将 `cli` 换为 `web` 可运行浏览器链;`root` 需要独占的新工作区,运行前会登记环境目录为空。证据默认保存到 `.scratch/ci/<root|cli|web>`,可用 `DIGITAL_CI_EVIDENCE` 选择本次独立目录。每次重跑应使用新目录,避免覆盖前次证据。
`run` 直接调用公共门禁的治理实现;包内原生准备任务负责客户端和正式镜像,不在工作流复制构建逻辑。Turbo 并发限制为 2,镜像构建继续使用 Docker/BuildKit 的层缓存。每次重跑使用新的独占检出和证据目录。

CI 根检查按托管运行器的资源预算将 Turbo 任务并发限制为 2,CLI/Web 保持默认 10;本地相同条件用 `TURBO_CONCURRENCY=2 node --conditions=digital-source scripts/ci-environment.ts run root` 复现,内部仍执行 `pnpm run check`,测试和作业期限保持不变。
证据默认保存到 `.scratch/ci/full`,可用 `DIGITAL_CI_EVIDENCE` 指定目录。相同变量传到服务、环境与部署验收入口:它们保留实际产品数据、归属和场景结果,成功后把环境交给 CI 收尾;本地未启用 CI 交接时,成功场景按正式生命周期回收,失败现场保留。

CLI 的 `--retain-on-success` 保持成功结局,停止并确认退出后暂不删除产品数据;不提供该选项时仍按原本行为回收。Web 使用已有 `--keep-on-success true`,成功后服务仍运行,后续 `collect` 会经公共 stop/inspect 停止并独立确认退出,然后收集整个真实产品数据根和停止后的完整日志。适用的浏览器诊断随场景结果保留。收集失败不会阻止其他明确自有环境的停止。
`collect` 独立停止并检查本次自有服务和容器,收集整个真实产品数据根及停止后的日志。部署通过公共容器管理接口保存数据,客户端诊断和验收结果一并进入本次证据目录。执行失败或取消后仍运行停止、收集和上传;一个环境收集失败不阻止其他自有环境停止。

CI 在执行失败、超时或取消后仍进入独立停止、收集与上传步骤。上传要求有真实文件;只有实际 upload-artifact 成功,并通过 GitHub API 核实产物 ID、名称、运行归属和有效字节后,才调用公共 delete。保存或上传失败不执行删除;未知退出状态保留载体,执行、保存和清理结果分别报告。删除后的结果另写入实际作业日志,避免只留在已经上传之后的本地文件里。本地 `collect` 成功仅证明收集完成,不能代替真实上传。
只有实际上传成功,并通过 GitHub API 核实产物 ID、名称、运行归属及有效字节后,`delete` 才调用正式删除。保存或上传失败保留原载体,未知退出状态不删除。执行、保存、上传和清理结果分别报告,删除后的结果也写入实际作业日志。本地 `collect` 成功不能替代真实上传核验。

Docker 部署只在手工触发时显式选择 `deployment`。该作业先使用现有 Dockerfile 构建 standalone/runtime 两目标,向原 `check:deployment` 传入两份明确镜像引用,再运行既有部署断言。未选择或未执行该作业不表示部署通过。完整真实资料与来源验收继续使用各自显式入口和既有条件,不纳入默认 CI。
真实资料、真实来源和 provided 服务继续使用所属包的显式条件验收入口;普通门禁报告这些任务未执行。完整受控验收通过不代表真实来源验收通过。
8 changes: 4 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
version: 2

updates:
- package-ecosystem: devcontainers
directory: /
schedule:
interval: weekly
- package-ecosystem: npm
directory: /
schedule:
Expand Down Expand Up @@ -31,7 +35,3 @@ updates:
- dependency-name: mcr.microsoft.com/devcontainers/typescript-node
update-types:
- version-update:semver-major
- package-ecosystem: docker
directory: /apps/web
schedule:
interval: weekly
103 changes: 31 additions & 72 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ on:
push:
branches:
- main
workflow_dispatch:
inputs:
deployment:
description: 显式构建镜像并执行已有 Docker 部署入口
type: boolean
default: false

permissions:
contents: read
Expand All @@ -22,15 +16,12 @@ concurrency:

jobs:
check:
name: ${{ matrix.capability }}
name: 完整轻重验收
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
capability: [root, cli, web]
timeout-minutes: 150
env:
DIGITAL_CI_EVIDENCE: ${{ github.workspace }}/.scratch/ci/${{ matrix.capability }}
DIGITAL_AUTOMATION_WORKSPACE: ${{ github.workspace }}
DIGITAL_CI_EVIDENCE: ${{ github.workspace }}/.scratch/ci/full
steps:
- name: 检出独立工作区
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
Expand All @@ -48,90 +39,58 @@ jobs:
cache: true
- name: 准备本作业临时目录
run: |
ci_tmp="$RUNNER_TEMP/d18-${{ matrix.capability }}"
mkdir -m 700 -p "$ci_tmp"
# 私有控制通道使用 Unix socket,作业临时根保持短路径。
ci_tmp="$(mktemp -d /tmp/digital-ci.XXXXXXXX)"
printf 'TMPDIR=%s\n' "$ci_tmp" >> "$GITHUB_ENV"
- name: 安装锁定依赖
run: pnpm install --frozen-lockfile
- name: 安装 Chromium
run: pnpm --filter @digital/web exec playwright install --with-deps chromium
- name: 构建场景正式依赖
if: matrix.capability != 'root'
run: pnpm exec turbo run build --filter=@digital/web --filter=@digital/cli --filter=@digital/db-migrations
- name: 运行相同本地入口
id: execution
timeout-minutes: 30
timeout-minutes: 120
env:
TURBO_CONCURRENCY: ${{ matrix.capability == 'root' && '2' || '10' }}
run: node --conditions=digital-source scripts/ci-environment.ts run ${{ matrix.capability }}
TURBO_CONCURRENCY: "2"
run: pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts run
- name: 停止自有环境并收集真实数据及完整日志
id: collection
if: always()
run: node --conditions=digital-source scripts/ci-environment.ts collect ${{ matrix.capability }}
run: pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts collect
- name: 上传场景证据
id: upload
if: always()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: ci-${{ matrix.capability }}-${{ github.run_id }}-${{ github.run_attempt }}
name: ci-full-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ env.DIGITAL_CI_EVIDENCE }}
include-hidden-files: true
if-no-files-found: error
retention-days: 7
- name: 上传可独立下载的产品数据与完整日志
id: product_upload
if: always()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: ci-product-data-and-logs-${{ github.run_id }}-${{ github.run_attempt }}
# 完整包保留容器文件系统快照;此包只排除这些大快照。
path: |
${{ env.DIGITAL_CI_EVIDENCE }}
!${{ env.DIGITAL_CI_EVIDENCE }}/**/*-stopped-container.tar
!${{ env.DIGITAL_CI_EVIDENCE }}/**/*-stopped.tar
include-hidden-files: true
if-no-files-found: error
retention-days: 7
- name: 核实上传产物后显式删除自有环境
id: deletion
if: always() && steps.collection.outcome == 'success' && steps.upload.outcome == 'success'
if: always() && steps.collection.outcome == 'success' && steps.upload.outcome == 'success' && steps.product_upload.outcome == 'success'
env:
GITHUB_TOKEN: ${{ github.token }}
DIGITAL_CI_ARTIFACT_ID: ${{ steps.upload.outputs.artifact-id }}
DIGITAL_CI_ARTIFACT_NAME: ci-${{ matrix.capability }}-${{ github.run_id }}-${{ github.run_attempt }}
run: node --conditions=digital-source scripts/ci-environment.ts delete ${{ matrix.capability }}
DIGITAL_CI_ARTIFACT_NAME: ci-full-${{ github.run_id }}-${{ github.run_attempt }}
run: pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts delete
- name: 分别报告执行、保存、上传与清理结果
if: always()
env:
DIGITAL_CI_EXECUTION_OUTCOME: ${{ steps.execution.outcome }}
DIGITAL_CI_COLLECTION_OUTCOME: ${{ steps.collection.outcome }}
DIGITAL_CI_UPLOAD_OUTCOME: ${{ steps.upload.outcome }}
DIGITAL_CI_UPLOAD_OUTCOME: ${{ steps.upload.outcome == 'success' && steps.product_upload.outcome == 'success' && 'success' || 'failure' }}
DIGITAL_CI_DELETION_OUTCOME: ${{ steps.deletion.outcome }}
run: node --conditions=digital-source scripts/ci-environment.ts result ${{ matrix.capability }}

deployment:
name: 显式 Docker 部署
if: github.event_name == 'workflow_dispatch' && inputs.deployment
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version-file: package.json
package-manager-cache: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
with:
cache: true
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: 安装依赖与浏览器
run: |
pnpm install --frozen-lockfile
pnpm --filter @digital/web exec playwright install --with-deps chromium
- name: 构建正式部署镜像与 checker 依赖
run: |
pnpm exec turbo run build --filter=@digital/web --filter=@digital/cli --filter=@digital/db-migrations
docker build -f apps/web/Dockerfile --target standalone -t completion-ci-standalone:${{ github.run_id }} .
docker build -f apps/web/Dockerfile --target runtime -t completion-ci-runtime:${{ github.run_id }} .
- name: 调用原部署入口
env:
DIGITAL_DEPLOYMENT_STANDALONE_IMAGE: completion-ci-standalone:${{ github.run_id }}
DIGITAL_DEPLOYMENT_RUNTIME_IMAGE: completion-ci-runtime:${{ github.run_id }}
DIGITAL_DEPLOYMENT_ARTIFACTS: ${{ github.workspace }}/.scratch/ci/deployment/standalone
DIGITAL_COMPOSE_CHECK_ARTIFACTS: ${{ github.workspace }}/.scratch/ci/deployment/compose
run: pnpm run check:deployment
- name: 上传部署诊断
if: always()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: deployment-${{ github.run_id }}-${{ github.run_attempt }}
path: .scratch/ci/deployment
if-no-files-found: error
retention-days: 7
run: pnpm --filter @digital/workspace-automation exec node --conditions=digital-source scripts/ci-environment.ts result
71 changes: 0 additions & 71 deletions apps/cli/package.json

This file was deleted.

Loading
Loading