diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index c34b987e..312f2653 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -1,5 +1,5 @@ name: "Setup Build Environment" -description: "Python, Rust, uv, just, and optional legacy protoc for Persisting CI" +description: "Python, optional Rust, and pinned build tools for Persisting CI" inputs: python-version: description: "Python version" @@ -9,10 +9,14 @@ inputs: description: "Rust toolchain" required: false default: "stable" + install-rust: + description: "Install Rust for jobs that compile or check Rust code" + required: false + default: "true" components: description: "Rust components (comma-separated)" required: false - default: "rustfmt, clippy, rust-src" + default: "rustfmt, clippy" install-dioxus: description: "Install the pinned Dioxus CLI and wasm target" required: false @@ -21,10 +25,6 @@ inputs: description: "Install cargo-nextest" required: false default: "true" - install-zig: - description: "Install Zig for the macOS-to-Linux guest init cross-build" - required: false - default: "false" runs: using: "composite" @@ -35,6 +35,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Install Rust toolchain + if: inputs.install-rust == 'true' uses: dtolnay/rust-toolchain@master with: toolchain: ${{ inputs.rust-toolchain }} @@ -51,18 +52,11 @@ runs: tool: just@1.57.0 - name: Install cargo-nextest - if: inputs.install-nextest == 'true' + if: inputs.install-rust == 'true' && inputs.install-nextest == 'true' uses: taiki-e/install-action@v2 with: tool: cargo-nextest@0.9.137 - - name: Install Linux guest init cross-compiler - if: runner.os == 'macOS' && inputs.install-zig == 'true' - shell: bash - run: | - brew install zig - zig version - - name: Add Dioxus Web target if: inputs.install-dioxus == 'true' shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d85a344c..1e8804de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" components: "rustfmt" - name: Rust formatting @@ -44,7 +43,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" components: "clippy" - uses: Swatinem/rust-cache@v2 @@ -64,7 +62,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" components: "clippy" - uses: Swatinem/rust-cache@v2 @@ -87,7 +84,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" components: "clippy" - uses: Swatinem/rust-cache@v2 @@ -107,8 +103,8 @@ jobs: - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: + install-rust: "false" install-nextest: "false" - install-zig: "false" components: "" - name: Ruff @@ -130,14 +126,12 @@ jobs: persisting-overlaynet event_control: true smoke_pchronicle: false - install_zig: false - platform: Linux os: ubuntu-latest shard: gateway packages: persisting-gateway event_control: false smoke_pchronicle: false - install_zig: false - platform: Linux os: ubuntu-latest shard: pchronicle @@ -146,7 +140,6 @@ jobs: persisting-pchronicle-cli event_control: false smoke_pchronicle: true - install_zig: false - platform: macOS os: macos-latest shard: capture @@ -157,7 +150,6 @@ jobs: persisting-overlaynet event_control: false smoke_pchronicle: false - install_zig: false - platform: macOS os: macos-latest shard: pchronicle @@ -166,14 +158,12 @@ jobs: persisting-pchronicle-cli event_control: false smoke_pchronicle: true - install_zig: false steps: - uses: actions/checkout@v4 - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: - install-zig: "${{ matrix.install_zig }}" components: "" - uses: Swatinem/rust-cache@v2 @@ -201,7 +191,6 @@ jobs: - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: - install-zig: "false" components: "" - uses: Swatinem/rust-cache@v2 @@ -223,7 +212,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-dioxus: "true" - install-zig: "false" components: "" - uses: Swatinem/rust-cache@v2 @@ -238,32 +226,46 @@ jobs: name: pChronicle S3 Contract / MinIO runs-on: ubuntu-latest timeout-minutes: 30 + env: + MINIO_VERSION: RELEASE.2025-07-23T15-54-02Z + MC_VERSION: RELEASE.2025-08-13T08-35-41Z + MINIO_GO_VERSION: "1.24.2" steps: - uses: actions/checkout@v4 - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: - install-zig: "false" components: "" - uses: Swatinem/rust-cache@v2 with: shared-key: ci-pchronicle-s3 + - name: Cache pinned MinIO tools + id: minio-tools + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/minio-bin + key: minio-v1-${{ runner.os }}-${{ runner.arch }}-go${{ env.MINIO_GO_VERSION }}-${{ env.MINIO_VERSION }}-${{ env.MC_VERSION }} + - uses: actions/setup-go@v5 + if: steps.minio-tools.outputs.cache-hit != 'true' with: - go-version: "1.24.2" + go-version: ${{ env.MINIO_GO_VERSION }} cache: false # Upstream no longer distributes the public container images or binaries. - name: Build pinned MinIO tools from source + if: steps.minio-tools.outputs.cache-hit != 'true' env: GOBIN: ${{ runner.temp }}/minio-bin run: | - go install github.com/minio/minio@RELEASE.2025-07-23T15-54-02Z - go install github.com/minio/mc@RELEASE.2025-08-13T08-35-41Z - echo "$GOBIN" >> "$GITHUB_PATH" + go install "github.com/minio/minio@$MINIO_VERSION" + go install "github.com/minio/mc@$MC_VERSION" + + - name: Add MinIO tools to PATH + run: echo "$RUNNER_TEMP/minio-bin" >> "$GITHUB_PATH" - name: Start pinned MinIO server env: @@ -301,7 +303,7 @@ jobs: - name: Show MinIO logs after failure if: failure() - run: cat "$RUNNER_TEMP/minio.log" + run: test ! -f "$RUNNER_TEMP/minio.log" || cat "$RUNNER_TEMP/minio.log" - name: Stop MinIO if: always() @@ -319,8 +321,8 @@ jobs: - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: + install-rust: "false" install-nextest: "false" - install-zig: "false" components: "" - name: Sync Python deps @@ -330,42 +332,28 @@ jobs: run: just test-py examples: - name: Examples / ${{ matrix.shard }} + name: Examples / pchronicle runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - shard: pchronicle - command: just examples-pchronicle steps: - uses: actions/checkout@v4 - - name: Setup Python for examples - id: setup-python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - name: Setup Build Environment uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" components: "" - uses: Swatinem/rust-cache@v2 with: - shared-key: ci-examples-${{ matrix.shard }} + shared-key: ci-examples-pchronicle - name: Install jq run: sudo apt-get update -qq && sudo apt-get install -y -qq jq - - name: Run ${{ matrix.shard }} examples + - name: Run pChronicle examples env: - WORK_ROOT: ${{ runner.temp }}/${{ matrix.shard }}-examples - PYTHON_BIN: ${{ steps.setup-python.outputs.python-path }} - run: ${{ matrix.command }} + WORK_ROOT: ${{ runner.temp }}/pchronicle-examples + run: just examples-pchronicle ci: name: CI diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2b7f8861..c14102d7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ permissions: contents: read concurrency: - group: pages + group: docs-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5c64e3c7..f8eee83e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -64,6 +64,9 @@ jobs: - name: Setup Build Environment uses: ./.github/actions/setup-build-env + with: + install-nextest: "false" + components: "" - name: Install hyperfine uses: taiki-e/install-action@v2 @@ -73,6 +76,7 @@ jobs: - uses: Swatinem/rust-cache@v2 with: shared-key: pchronicle-benchmark + workspaces: . -> target/pchronicle-benchmark-ci - name: Run and render benchmark run: | diff --git a/.github/workflows/pchronicle-benchmark.yml b/.github/workflows/pchronicle-benchmark.yml index 0ff9c3d6..08e20edb 100644 --- a/.github/workflows/pchronicle-benchmark.yml +++ b/.github/workflows/pchronicle-benchmark.yml @@ -30,7 +30,6 @@ jobs: uses: ./.github/actions/setup-build-env with: install-nextest: "false" - install-zig: "false" - name: Install hyperfine uses: taiki-e/install-action@v2 @@ -40,6 +39,7 @@ jobs: - uses: Swatinem/rust-cache@v2 with: shared-key: pchronicle-benchmark + workspaces: . -> target/pchronicle-benchmark-ci - name: Resolve main baseline id: baseline @@ -71,11 +71,13 @@ jobs: run: | set -euo pipefail baseline_repo="${{ steps.baseline.outputs.repo }}" + # Run revisions sequentially; reuse dependencies and copy each report + # before the next run replaces the Criterion output in the shared target. mkdir -p "${RUNNER_TEMP}/pchronicle-benchmark/main" python3 "$baseline_repo/benchmark/pchronicle/bench.py" run \ --repo "$baseline_repo" \ --suite smoke \ - --target-dir "${GITHUB_WORKSPACE}/target/pchronicle-benchmark-ci-main" \ + --target-dir "${GITHUB_WORKSPACE}/target/pchronicle-benchmark-ci" \ --output "${RUNNER_TEMP}/pchronicle-benchmark/main" - name: Benchmark candidate @@ -86,7 +88,7 @@ jobs: python3 benchmark/pchronicle/bench.py run \ --repo . \ --suite smoke \ - --target-dir "${GITHUB_WORKSPACE}/target/pchronicle-benchmark-ci-candidate" \ + --target-dir "${GITHUB_WORKSPACE}/target/pchronicle-benchmark-ci" \ --output "${RUNNER_TEMP}/pchronicle-benchmark/candidate" - name: Upload benchmark data diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml index fe1580be..32253755 100644 --- a/.github/workflows/wheel.yml +++ b/.github/workflows/wheel.yml @@ -32,8 +32,14 @@ jobs: with: python-version: "3.12" install-dioxus: "true" + install-nextest: "false" + components: "" - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . -> target + pchronicle-web -> target - name: Build pChronicle Web assets run: just chronicle-web-build diff --git a/docs/src/en/project/engineering.md b/docs/src/en/project/engineering.md index 0f97f1b5..4a1c484a 100644 --- a/docs/src/en/project/engineering.md +++ b/docs/src/en/project/engineering.md @@ -17,8 +17,9 @@ Run these from the repository root. `just --list` shows the full recipe set. | `just docs-serve-dirty` | Local Zensical preview when automatic reload stalls | | `just docs-build` | Build the static documentation site | | `just examples` | pChronicle product example suite | -| `just gate` | Format, lint, and the full Rust test workspace | -| `just dev` | Scoped runtime-crate check; not the full workspace matrix | +| `just dev` | Apply formatting, lint, then run Rust tests | +| `just ci` | Check lint without rewriting files, run Rust/Python tests and property tests, then build | +| `just check-quick` | Check core runtime crates and pChronicle without default features | `just test` uses the debug nextest profile for faster iteration. Pass a Cargo package name or a short crate alias (`pchronicle`, @@ -36,8 +37,7 @@ The no-argument form also runs `just test-py`. ## Fast local builds -The repository `rust-toolchain.toml` selects stable for normal compiler validation -and opt-in diagnostics. Normal development, test, and release builds all use +The repository `rust-toolchain.toml` selects stable with rustfmt and Clippy. Normal development, test, and release builds all use the toolchain's default LLVM backend. Rust tests use `cargo nextest` for process isolation and parallel test @@ -49,31 +49,24 @@ Local and ordinary CI builds use the platform's default linker. Linux wheels use the manylinux_2_28 image (glibc 2.28) so rustc libstd can link `statx` / `copy_file_range`. -`just dev` is intentionally scoped to runtime crates and a no-default-feature -pChronicle check. Use `just gate` or the CI workflows for the full workspace, -all-targets, and storage-feature matrix. +`just check-quick` checks core runtime crates and pChronicle without default +features. `just dev` formats files before lint and Rust tests; `just ci` uses +read-only lint checks and also runs Python and property tests. The GitHub Actions +workflow additionally covers platform shards, Web builds, S3, and examples. `cargo nextest` does not run doctests. Keep documentation tests on the regular Cargo runner when needed, for example `cargo test --doc -p `. -### Nightly diagnostics (opt-in) +### CI build reuse -The repository keeps two expensive/nightly diagnostics out of the normal -edit loop: +MinIO tools are cached by OS, architecture, Go version, and pinned source releases. +Benchmark revisions run sequentially on one runner and reuse a Cargo target +directory; their reports are saved separately. Wheel builds cache both Rust +workspaces, and the embedded Web resource fingerprint includes its lockfile. -- `just build-analysis persisting-pchronicle` enables Cargo's `-Z build-analysis` - for one package and writes per-session JSONL metrics under `$CARGO_HOME/log`. - Inspect them with `just build-analysis-report` (or pass `report=timings` or - `report=rebuilds`). The dedicated target directory prevents diagnostic - artifacts from polluting the normal incremental cache. -- `just sanitize address persisting-agentctl` runs the selected crate's tests - with LLVM AddressSanitizer. The recipe uses `-Z build-std`, so the nightly - `rust-src` component is required. Other supported values are `leak`, - `thread`, and `undefined`; availability depends on the host platform. - -Sanitizer builds are deliberately not part of `just dev`/CI's default path: -they rebuild the standard library and are intended for focused debugging -sessions. +Python-only jobs skip Rust installation. Build-only jobs skip cargo-nextest. +Documentation runs use a per-ref concurrency group so a PR cannot cancel a +main-branch documentation deployment. For supported behavior, start with [pChronicle Guides](../pchronicle/guides/index.md), and consult [System Design](../system-design/index.md) for the rationale behind an diff --git a/docs/src/zh/project/engineering.md b/docs/src/zh/project/engineering.md index b3fd6bb3..0ff47695 100644 --- a/docs/src/zh/project/engineering.md +++ b/docs/src/zh/project/engineering.md @@ -16,8 +16,9 @@ | `just docs-serve-dirty` | 自动重载卡住时重新启动 Zensical 预览 | | `just docs-build` | 构建静态文档站点 | | `just examples` | pChronicle 产品示例套件 | -| `just gate` | 格式化、lint 以及完整 Rust 测试工作区 | -| `just dev` | 限定范围的 runtime crate 检查;不是完整工作区矩阵 | +| `just dev` | 自动格式化、lint,然后运行 Rust 测试 | +| `just ci` | 只读 lint 检查、Rust/Python 测试、性质测试,然后构建 | +| `just check-quick` | 核心 runtime crate 与无默认 feature 的 pChronicle 检查 | `just test` 使用 debug nextest profile 以便更快迭代。传入 Cargo package 名 或短 crate 别名(`pchronicle`、`pchronicle-cli`、 @@ -35,7 +36,7 @@ shard 一致);只要 CLI 时用 `just test pchronicle-cli`。无参数形式 ## 快速本地构建 -仓库的 `rust-toolchain.toml` 为常规编译器校验和可选诊断选择 stable。正常 +仓库的 `rust-toolchain.toml` 选择 stable、rustfmt 与 Clippy。正常 开发、测试和发布构建都使用该 toolchain 的默认 LLVM backend。 Rust 测试用 `cargo nextest` 做进程隔离和并行执行;用 @@ -46,28 +47,21 @@ Rust 测试用 `cargo nextest` 做进程隔离和并行执行;用 镜像(glibc 2.28),以便 rustc libstd 能链接 `statx` / `copy_file_range`。 -`just dev` 刻意限定在 runtime crate 以及无默认 feature 的 pChronicle 检查。 -完整工作区、all-targets 和 storage-feature 矩阵请用 `just gate` 或 CI -工作流。 +`just check-quick` 检查核心 runtime crate 以及无默认 feature 的 pChronicle。 +`just dev` 先格式化文件,再运行 lint 和 Rust 测试;`just ci` 使用只读 lint 检查, +并额外运行 Python 和性质测试。GitHub Actions 还覆盖平台分片、Web 构建、S3 与示例。 `cargo nextest` 不跑 doctest。需要时把文档测试留在常规 Cargo runner 上, 例如 `cargo test --doc -p `。 -### Nightly 诊断(可选) +### CI 构建复用 -仓库把两项昂贵 / nightly 诊断排除在日常编辑循环之外: +MinIO 工具按操作系统、架构、Go 版本和固定源码版本缓存。基准测试在同一 runner 上 +顺序执行两个版本,复用 Cargo 构建目录,分别保存报告。Wheel 构建缓存两个 Rust +工作区,内嵌 Web 资源的缓存签名包含锁文件。 -- `just build-analysis persisting-pchronicle` 为一个 package 启用 Cargo 的 - `-Z build-analysis`,并把每会话 JSONL 指标写到 `$CARGO_HOME/log`。用 - `just build-analysis-report` 查看(也可传 `report=timings` 或 - `report=rebuilds`)。独立的 target 目录避免诊断产物污染普通增量缓存。 -- `just sanitize address persisting-agentctl` 用 LLVM AddressSanitizer 跑 - 所选 crate 的测试。该 recipe 使用 `-Z build-std`,因此需要 nightly - `rust-src` 组件。其他支持值是 `leak`、`thread` 和 `undefined`;可用性 - 取决于宿主平台。 - -Sanitizer 构建故意不进入 `just dev` / CI 的默认路径:它们会重建标准库, -只适合聚焦的调试会话。 +纯 Python 任务跳过 Rust 安装,只构建的任务跳过 cargo-nextest。文档任务按 ref +划分并发组,PR 构建不会取消主分支的文档部署。 支持的行为请从 [pChronicle 指南](../pchronicle/guides/index.md) 开始,实现理由见 [系统架构](../system-design/index.md)。 diff --git a/justfile b/justfile index 43c6cd77..d28ca65c 100644 --- a/justfile +++ b/justfile @@ -263,6 +263,7 @@ build-wheel-debug: clean: cargo clean + cargo clean --manifest-path pchronicle-web/Cargo.toml rm -rf dist target/wheels .venv htmlcov .coverage coverage.xml # ── 格式化 / Lint ───────────────────────────────────────────────────────────── @@ -346,7 +347,8 @@ ci-lint: # CI 近似:功能门禁 + Proptest 回归 + 构建 ci: - just dev + just ci-lint + just test just proptest pchronicle just build diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a63bdfd1..9d33a0e5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,4 @@ [toolchain] # Stable is the default compiler for local development and normal CI builds. -# The opt-in `just build-analysis` and `just sanitize` recipes install/use -# nightly explicitly because they depend on unstable Cargo/rustc flags. channel = "stable" -# rust-src is used by the opt-in `just sanitize` recipe (`-Z build-std`). -components = ["rustfmt", "clippy", "rust-src"] +components = ["rustfmt", "clippy"] diff --git a/scripts/packaging/stage_wheel_binaries.py b/scripts/packaging/stage_wheel_binaries.py index 63a1a696..978185c1 100644 --- a/scripts/packaging/stage_wheel_binaries.py +++ b/scripts/packaging/stage_wheel_binaries.py @@ -183,7 +183,7 @@ def _build(options: BuildOptions) -> dict[str, Path]: def _web_inputs_digest() -> str: """Hash the inputs that affect the generated Dioxus public directory.""" digest = hashlib.sha256() - inputs = [WEB_ROOT / "Cargo.toml", WEB_ROOT / "Dioxus.toml"] + inputs = [WEB_ROOT / "Cargo.toml", WEB_ROOT / "Cargo.lock", WEB_ROOT / "Dioxus.toml"] inputs.extend(sorted((WEB_ROOT / "src").rglob("*"))) inputs.extend(sorted((WEB_ROOT / "assets").rglob("*"))) for path in inputs: diff --git a/tests/test_release_packaging.py b/tests/test_release_packaging.py index 2946a571..05e4ab8d 100644 --- a/tests/test_release_packaging.py +++ b/tests/test_release_packaging.py @@ -181,6 +181,30 @@ def test_build_backend_options_accept_explicit_cargo_settings() -> None: assert options.jobs == "3" +def test_web_assets_become_stale_after_lockfile_changes( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + web = tmp_path / "web" + public = tmp_path / "public" + web.mkdir() + public.mkdir() + (web / "Cargo.lock").write_text("version = 3\n", encoding="utf-8") + (public / "index.html").write_text("", encoding="utf-8") + monkeypatch.setattr(wheel_stage, "WEB_ROOT", web) + monkeypatch.setattr(wheel_stage, "WEB_PUBLIC", public) + manifest = public / "embedded.manifest" + manifest.write_text( + f"__PCHRONICLE_EMBEDDED_WEB_ASSETS_V1__\n{wheel_stage._web_inputs_digest()}\n", + encoding="utf-8", + ) + assert wheel_stage._web_assets_are_current(manifest, wheel_stage._web_inputs_digest()) + + (web / "Cargo.lock").write_text("version = 4\n", encoding="utf-8") + + assert not wheel_stage._web_assets_are_current(manifest, wheel_stage._web_inputs_digest()) + + def test_staging_replaces_old_component_payloads( monkeypatch: pytest.MonkeyPatch, tmp_path: Path,