From 42b42cb6143b7778867a24c1c24222eb35630846 Mon Sep 17 00:00:00 2001 From: sammiller Date: Mon, 21 Sep 2026 11:49:08 -0700 Subject: [PATCH] Reduce CI to build and offline validation under P2-017 --- .github/workflows/native-abi.yml | 23 ++-------- .github/workflows/package-validation.yml | 43 ------------------ .github/workflows/pr-gate.yml | 48 +++++--------------- .pre-commit-config.yaml | 30 ------------- AGENTS.md | 9 ++++ CONTRIBUTING.md | 16 ++++--- README.md | 16 +++---- deploy/README.md | 15 +++---- docs/build-identity.md | 8 +++- docs/design-policy.md | 6 +-- docs/native-package-release.md | 3 ++ docs/platform-bootstrap.md | 2 +- eng/packaging/README.md | 57 ++++++++++-------------- eng/packaging/native.py | 11 ----- eng/packaging/native_consumer.py | 2 + eng/packaging/packages.py | 4 +- 16 files changed, 88 insertions(+), 205 deletions(-) diff --git a/.github/workflows/native-abi.yml b/.github/workflows/native-abi.yml index 65dc4bc..abefcfd 100644 --- a/.github/workflows/native-abi.yml +++ b/.github/workflows/native-abi.yml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -name: Native ABI +name: Native build on: workflow_call: outputs: @@ -133,7 +133,7 @@ jobs: restore-keys: | sccache-${{ runner.os }}-${{ runner.arch }}-${{ env.SCCACHE_VERSION }}-${{ steps.vcpkg-toolchain.outputs.fingerprint }}- - run: dotnet restore DesktopPlatform.slnx --locked-mode - - name: Build, test, and stage both CMake profiles + - name: Build and stage both CMake profiles shell: cmd run: | @echo on @@ -155,7 +155,6 @@ jobs: for %%p in (runtime-shared shim-static) do ( cmake --preset win-x64-%%p || exit /b 1 cmake --build --preset win-x64-%%p || exit /b 1 - ctest --preset win-x64-%%p || exit /b 1 cmake --install artifacts/cmake/win-x64/%%p || exit /b 1 ) @@ -173,23 +172,7 @@ jobs: if ($nonCacheable -gt 0) { throw "sccache reported $nonCacheable non-cacheable compilations; debug information must stay Embedded." } - - name: Run managed P/Invoke over app-local CMake artifacts - shell: pwsh - run: | - dotnet build tests/NativeAbiTests/ArcForges.Tests.NativeAbiTests.csproj -c Release --no-restore - if ($LASTEXITCODE) { throw 'Managed native ABI test build failed.' } - $runtime = Join-Path '${{ github.workspace }}' 'artifacts/stage/native/win-x64/native' - $testOutput = Join-Path '${{ github.workspace }}' 'artifacts/bin/dotnet/windows/ArcForges.Tests.NativeAbiTests/Release/net10.0' - $nativeDlls = @(Get-ChildItem -LiteralPath $runtime -File -Filter '*.dll') - if (-not $nativeDlls.Count) { throw 'CMake staged no native runtime DLLs.' } - try { - Copy-Item -LiteralPath $nativeDlls.FullName -Destination $testOutput - & (Join-Path $testOutput 'ArcForges.Tests.NativeAbiTests.exe') - if ($LASTEXITCODE) { throw 'Managed P/Invoke over CMake artifacts failed.' } - } finally { - $nativeDlls | ForEach-Object { Remove-Item -LiteralPath (Join-Path $testOutput $_.Name) -Force -ErrorAction SilentlyContinue } - } - - name: Stage audited native runtime packages from the tested CMake outputs + - name: Stage audited native runtime packages from the compiled CMake outputs id: native-artifact shell: pwsh run: | diff --git a/.github/workflows/package-validation.yml b/.github/workflows/package-validation.yml index ef07cd1..bf2c0db 100644 --- a/.github/workflows/package-validation.yml +++ b/.github/workflows/package-validation.yml @@ -53,8 +53,6 @@ jobs: with: name: ${{ inputs.native_artifact_name }} path: artifacts/native-packages - - name: Verify the native artifact belongs to this source commit - run: python eng/packaging/native.py verify --commit "$GITHUB_SHA" - run: dotnet restore DesktopPlatform.slnx --locked-mode - run: dotnet format DesktopPlatform.slnx --verify-no-changes --no-restore - name: Build and test platform ownership @@ -78,44 +76,3 @@ jobs: path: artifacts/packages/ if-no-files-found: error retention-days: 30 - consumer: - needs: pack - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6 - with: - global-json-file: global.json - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 - with: - name: ${{ needs.pack.outputs.artifact_name }} - path: artifacts/packages - - name: Consume exact candidate outside the repository with an empty cache - env: - PACKAGE_VERSION: ${{ needs.pack.outputs.version }} - shell: pwsh - run: | - python eng/packaging/packages.py smoke --version $env:PACKAGE_VERSION --commit $env:GITHUB_SHA - if ($LASTEXITCODE) { throw 'Independent NuGet consumer verification failed.' } - - name: Publish and run Native AOT consumers of every actual Windows runtime package - if: runner.os == 'Windows' - env: - PACKAGE_VERSION: ${{ needs.pack.outputs.version }} - shell: pwsh - run: | - python eng/packaging/native_consumer.py --version $env:PACKAGE_VERSION --commit $env:GITHUB_SHA - if ($LASTEXITCODE) { throw 'Packaged native C ABI / Native AOT consumer verification failed.' } - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 - if: always() && runner.os == 'Windows' - with: - name: native-consumer-evidence-${{ github.run_id }}-${{ github.run_attempt }} - path: artifacts/native-consumer-evidence/ - if-no-files-found: ignore - retention-days: 30 diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index c6654c9..d92ef19 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -78,11 +78,7 @@ jobs: run: pre-commit run --all-files --show-diff-on-failure --color=always design-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -100,17 +96,13 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: design-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: design-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/design-policy.json if-no-files-found: error retention-days: 30 licence-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -131,17 +123,13 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: licence-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: licence-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/licence-*.json if-no-files-found: error retention-days: 30 reference-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -157,17 +145,13 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: reference-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: reference-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/reference-baselines.json if-no-files-found: error retention-days: 30 runtime-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -187,17 +171,13 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: runtime-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: runtime-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/runtime-ownership.json if-no-files-found: error retention-days: 30 reconciliation-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -214,17 +194,13 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: reconciliation-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: reconciliation-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/reconciliation.json if-no-files-found: error retention-days: 30 provenance-policy: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -246,7 +222,7 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: always() with: - name: provenance-policy-${{ matrix.os }}-${{ github.run_id }}-${{ github.run_attempt }} + name: provenance-policy-ubuntu-latest-${{ github.run_id }}-${{ github.run_attempt }} path: artifacts/evidence/provenance.json if-no-files-found: error retention-days: 30 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9dc9151..c0a0430 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,33 +8,3 @@ repos: - id: clang-format files: ^native/.*\.(c|h|cpp|hpp|cc|hh|cxx|hxx|ipp|tpp|inc|m|mm)$ types_or: [c++, c, objective-c] - - - repo: local - hooks: - # win.slnx is maintained independently of CMake and is deliberately not built by CI: the hosted - # Windows job builds the CMake presets only. Without a local gate the two Windows entry points would - # drift silently, so this runs on push, on Windows, where MSBuild and the user-wide vcpkg integration - # actually exist. - # - # It is a pre-push hook rather than pre-commit because a full native link is far too slow to sit in - # front of every commit. The Ubuntu repository-hooks job runs the default (pre-commit) stage and so - # never reaches this hook; the os.name guard makes the skip explicit rather than incidental, so the - # hook is also harmless if someone runs every stage on Linux. - # - # The body is an inline command by design: repository policy forbids tracked .ps1/.sh helpers, and - # RepositoryPolicyTests enforces that. - - id: win-slnx-release-x64 - name: win.slnx Release|x64 (Windows only) - language: system - stages: [pre-push] - pass_filenames: false - always_run: true - entry: >- - python -c 'import os,subprocess,sys; - (sys.exit(0) if os.name != "nt" else None); - vswhere = os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"); - found = subprocess.run([vswhere, "-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild", - "-find", r"MSBuild\**\Bin\MSBuild.exe"], capture_output=True, text=True).stdout.splitlines(); - (sys.exit("MSBuild was not found; install the Visual Studio C++ workload.") if not found else None); - sys.exit(subprocess.run([found[0].strip(), "win.slnx", "-m", "-restore:false", - "-p:Configuration=Release", "-p:Platform=x64", "-v:minimal", "-clp:Summary"]).returncode)' diff --git a/AGENTS.md b/AGENTS.md index 92c614d..78f6f50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,3 +17,12 @@ The accepted architecture lives in [ArcForges-Design](https://github.com/ArcForg Do not add tracked PowerShell or shell helper scripts. - Record what actually ran. A successful package-policy smoke does not prove native capability AOT, product behavior, release signing or NuGet publication. Run the checks documented in the README. + +## Validation policy (P2-017) + +Follow the [current CI/local authority](https://github.com/ArcForges/ArcForges-Design/blob/47db6670a727317939b91245e8c0b288834acf99/docs/assurance/ci-and-local-validation-policy.md). +- Never add or execute macOS CI, native/runtime/package-consumer execution, GUI, browser, device, live-service or published-package installation tests in any CI trigger or nested build script. +- Keep necessary Windows/Linux compilation, packaging, targeted offline unit/static checks and non-duplicated security checks. Runtime diagnostics are explicit local opt-in only for affected behavior using existing tools. Do not silently build/test from commit or push hooks. +- Preserve locks, required signatures, licence/provenance and one identity/integrity check at each real trust handoff. Do not routinely download public artifacts or repeat archive/hash/consumer verification after publication. Narrow FFmpeg licence/configuration and Windows signature inspection remain legal admission checks. +- Do not reinstall vcpkg, SDKs or toolchains to expand validation. Do not create tags or republish solely for verification. Stop on a network failure and report the exact operation; no proxy configuration, port 7890, wsl.exe or WSL wrappers. +- Record removed coverage honestly. Review the complete latest PR and merge only after applicable checks succeed. Post-merge work stops after commit/job/publication status and a clean primary fast-forward; retain branches/worktrees. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7f6c75..c108ad7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,13 @@ Use an isolated `.worktree/` branch and the [README verification commands](READM Shared mechanism changes belong here; product rules and business RPC schemas belong to their owners. Keep C# 14, C++20, explicit source provenance, committed dependency locks and warnings-as-errors. -PR CI validates the managed solution, package metadata and a clean package consumer on Windows and Linux, -Windows native CMake/ABI probes, formatting, dependency review and secret scanning. Weekly/manual deep -checks retain C# CodeQL and Linux native clang-tidy, sanitizers and fuzzers. Native publication is a later -capability admission with per-RID AOT, licence and dependency evidence, not a consequence of passing probes. +PR CI compiles/stages Windows native outputs, builds/packages the managed solution on Linux, +and runs targeted offline architecture/policy tests, formatting, dependency review and secret scanning. +Weekly/manual Deep check retains C# CodeQL only. No macOS, native runtime, installed-consumer, +GUI/device/browser or live-service CI is permitted under [AGENTS.md](AGENTS.md). -Run `pre-commit run --all-files`; the Windows `win-slnx-release-x64` pre-push hook checks the independent -IDE build. Include the actual commands/results and any unverified boundary in the PR description. -The [packaging guide](eng/packaging/README.md) explains candidate and release flows. +Run relevant format/static checks once. The independent IDE build and runtime diagnostics are explicit +local opt-in when affected, never automatic pre-push work. Reuse installed tools and dependencies. +Include actual results and untested coverage in the PR description. Publication completion uses +provider status, without public package re-downloads or repeated hash/consumer checks. +The [packaging guide](eng/packaging/README.md) explains the reduced candidate and release flows. diff --git a/README.md b/README.md index 9723239..63bdb97 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ maintained in [ArcForges-Design](https://github.com/ArcForges/ArcForges-Design). `eng/policy` contains generated glossary and invariant data from the exact Design commit in `design-source.json`. The portable checker compares fresh exports and validates document links, scoped citations, occurrence classifications and the -work-package graph. CI requires these checks on Windows and Linux before packaging. +work-package graph. CI requires these platform-independent checks once on Linux before packaging. ```text python -m unittest discover -s eng -p test_design_policy.py -v @@ -55,8 +55,8 @@ python eng/licence_boundary.py --evaluate-managed ## Build and verify Source reuse and the existing native artifact closure are enforced by the -[provenance records and review process](docs/provenance.md). Windows and Linux CI -check the real inventory, immutable history and failure tests before packaging. +[provenance records and review process](docs/provenance.md). Linux policy CI +checks the real inventory, immutable history and failure tests before packaging. Install the .NET SDK selected by `global.json` and Python 3.11 or newer. No Mobile/Web workloads are needed. @@ -69,12 +69,12 @@ dotnet test --project tests/ArchitectureTests/ArcForges.Tests.ArchitectureTests. CMake 4.3.3, Ninja 1.13.1, sccache and a C++20 compiler are needed only for native producer builds. Follow [native prerequisites and commands](deploy/README.md). `win.slnx` additionally builds the native Windows projects and stages their DLLs for `NativeAbiTests`; CI uses the independent CMake path. -After native staging, follow the [complete package verification](eng/packaging/README.md) to pack and -run independent C#, Native AOT and C17 consumers. See [native package scope and evidence](docs/native-package-release.md). +After native staging, follow [package production](eng/packaging/README.md). C#, Native AOT and C17 +consumer diagnostics are explicit local opt-in only when an affected behavior needs them. See [native package scope and evidence](docs/native-package-release.md). The scheduled/manual [Deep check](.github/workflows/deep-check.yml) runs C# CodeQL only. -C++ verification uses the PR/release native compilation, CMake/CTest, managed ABI tests and isolated -package consumers; Deep check no longer runs native clang-tidy, sanitizers or fuzzers. To validate the +C++ CI compiles and stages the Windows native libraries. CTest, managed ABI execution and isolated +package consumers are local opt-in only; no macOS or hosted runtime testing is permitted. To validate the current configuration, use Actions → Deep check → Run workflow and select the desired branch. Re-running a historical workflow uses its original commit and can still execute the removed jobs. @@ -84,7 +84,7 @@ Re-running a historical workflow uses its original commit and can still execute NuGet trusted-publisher setup and consumer examples. Only admitted packages are published; shared placeholders remain excluded. These runtime packages expose the existing ABI, not future product APIs. Every push to `main`, including a merged PR, automatically allocates a prerelease version. Native -compilation/tests and source checks must finish before packing; isolated package consumers and the +compilation and source checks must finish before packing; targeted offline checks and the aggregate gate must pass before the same package bytes are published to nuget.org through OIDC. No manual workflow run, version entry or publish checkbox is required. diff --git a/deploy/README.md b/deploy/README.md index d62c636..8e59c59 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -83,22 +83,18 @@ under the standard triplets named by their presets. The optional macOS shader-to app-local for both Debug and Release. The generated test executable therefore runs directly from its output directory without native-runtime path or build-mode environment variables. -CI never builds `win.slnx`; the hosted Windows job builds the CMake presets only. The Windows-only -`win-slnx-release-x64` `pre-push` hook in [.pre-commit-config.yaml](../.pre-commit-config.yaml) builds it as -`Release|x64` locally, which is what keeps the two Windows entry points from drifting apart. It locates -MSBuild through `vswhere` and skips itself on non-Windows hosts, so the Ubuntu repository-hooks job is -unaffected. +CI builds the Windows CMake presets only. `win.slnx` remains a local IDE entry point; +its full build is an explicit command when relevant, not an automatic pre-push hook. +CTest and native ABI execution are local opt-in diagnostics, never CI gates. -Build, test and stage each profile from the configured compiler shell: +Build and stage each profile from the existing configured compiler shell: ```powershell cmake --preset win-x64-runtime-shared cmake --build --preset win-x64-runtime-shared -ctest --preset win-x64-runtime-shared cmake --install artifacts/cmake/win-x64/runtime-shared cmake --preset win-x64-shim-static cmake --build --preset win-x64-shim-static -ctest --preset win-x64-shim-static cmake --install artifacts/cmake/win-x64/shim-static python eng/packaging/native.py stage --vcpkg-root C:/vcpkg ``` @@ -107,4 +103,5 @@ The final command audits the PE import/export closure, supplies app-local Visual copies licences/SBOMs and matching upstream sources, then seals `artifacts/native-packages` with hashes and the source commit. It rejects an existing populated output; use a new `--directory` for another development candidate. Continue with [package production](../eng/packaging/README.md). This stage is -an input to NuGet packing; public upload waits for the independent package consumers as well. +an input to NuGet packing; public upload waits for the reduced build/static/package gate. +Run CTest manually only for a relevant native behavior change. No macOS CI is produced. diff --git a/docs/build-identity.md b/docs/build-identity.md index 8233dda..73bb8b8 100644 --- a/docs/build-identity.md +++ b/docs/build-identity.md @@ -22,11 +22,15 @@ No third-party library is rewritten or rebuilt to add ArcForges identity. The source timestamp is commit time, not wall-clock compilation time. Native and managed jobs have their own preserved build/run records; a later consumer retry does not manufacture a new identity. -The ordinary and Native AOT package-only C# consumers retrieve managed assembly and native export -metadata. The independent C17 caller checks the same native suffix using packaged headers/libraries. +Optional local ordinary/Native AOT consumers can retrieve managed assembly and native export +metadata, and the local C17 diagnostic can inspect the suffix. CI does not execute these consumers +or load owned DLLs during staging. The FFmpeg legal configuration probe remains a narrow licence check. Run `python -m unittest discover -s eng -p test_build_identity.py -v` for synthetic nine-source mutation, invalid/absent axes, duplicate subjects, aliases and real Git identity rejection tests. These mechanism fixtures do not assert production implementations for absent axes. Run the README build/architecture suite and the packaging guide for actual binary and package evidence. Build-local results and CI/public package evidence remain distinct. + +Current validation follows [AGENTS.md](../AGENTS.md) and P2-017: no macOS/runtime-consumer CI or +post-publication download/hash cycle. Historical WP02.04 runtime receipts do not require repetition. diff --git a/docs/design-policy.md b/docs/design-policy.md index 8bd1529..0657c30 100644 --- a/docs/design-policy.md +++ b/docs/design-policy.md @@ -70,9 +70,9 @@ They exercise dirty/wrong-pin refusal, preview immutability, deprecated-body exc sentence punctuation, same-spelled rules in different documents and the CLI's real failure exit/report. A current-corpus pass alone is insufficient. -Both CI platforms run the fixtures and fetch/verify the pinned real corpus before +The Linux policy job runs these platform-independent fixtures and verifies the pinned corpus before package creation. Their full reports are retained as `design-policy-*` artifacts. -Existing native, managed, independent package-consumer and publication gates remain -required. Local immutable export verification passed; PR CI, merge and publication +Native/managed compilation, package and publication checks remain required under P2-017; +package consumers and native execution are local opt-in only. Local immutable export verification passed; PR CI, merge and publication remain pending until the corresponding run artifacts are reviewed. Policy checks establish no provider, device, product behavior or commercial activation evidence. diff --git a/docs/native-package-release.md b/docs/native-package-release.md index a929ac8..c659330 100644 --- a/docs/native-package-release.md +++ b/docs/native-package-release.md @@ -1,5 +1,8 @@ # Existing native ABI package release +> Historical implementation and evidence. Current CI/local execution is governed by +> [AGENTS.md](../AGENTS.md) and P2-017; the runtime and package-consumer gates recorded below are no longer required. + ## Scope fixed before implementation Base: DesktopPlatform `7bdbf6c4656721088081cdf85c9f1ec8d78bc848`. The requested delivery is the diff --git a/docs/platform-bootstrap.md b/docs/platform-bootstrap.md index 185bcd9..cb97ab4 100644 --- a/docs/platform-bootstrap.md +++ b/docs/platform-bootstrap.md @@ -56,7 +56,7 @@ authenticated through OIDC and uploaded `ArcForges.Build.Policy` `1.0.0-ci.2.1`. ## Automatic publication after main updates The publication workflow now runs on every push to `main`, including merged PRs. It allocates an -immutable prerelease version, completes the reusable native/source/package/consumer gate, +immutable prerelease version, completes the reusable native-build/source/package gate, then publishes the tested bytes. No manual release form or approval step is part of the configured flow. Independent runs prevent newer merges from replacing queued releases. See the current [package production instructions](../eng/packaging/README.md) for numbering, setup and recovery. diff --git a/eng/packaging/README.md b/eng/packaging/README.md index ee40776..f682e78 100644 --- a/eng/packaging/README.md +++ b/eng/packaging/README.md @@ -16,46 +16,35 @@ Windows release. See [scope and validation evidence](../../docs/native-package-r ## Local and PR candidates -Build/test/install both Windows CMake profiles and stage their audited native payload as described in -[native producer builds](../../deploy/README.md). Then run from this repository: +Use existing native dependencies and build/stage the Windows CMake profiles as described in +[native producer builds](../../deploy/README.md). CI builds/stages native binaries on Windows, +then performs locked managed build, architecture tests and one package production pass on Linux. +Source, licence, provenance and security checks precede publication. No macOS outputs are produced. ```powershell dotnet restore DesktopPlatform.slnx --locked-mode dotnet build DesktopPlatform.slnx -c Release --no-restore dotnet test --project tests/ArchitectureTests/ArcForges.Tests.ArchitectureTests.csproj -c Release --no-build python eng/packaging/packages.py pack --version 1.0.0-ci.local.1 -python -m unittest discover -s eng/packaging -p 'test_*.py' -v +``` + +Packing validates the native input at its handoff, builds the explicit allowlist and validates +the completed candidate once. Publication checks the source/version and retained package integrity +at the credentialed handoff, then pushes those bytes. No public package download or consumer rerun +is part of completion. The candidate artifact remains `nuget-candidate--`. + +Use a new empty `--directory` for each candidate; do not overwrite or rebuild a version solely +for verification. Runtime CTest, P/Invoke, Build.Policy, JIT/AOT and C17 consumer tools remain +explicit local diagnostics for affected behavior, outside default CI/build/release commands: + +```powershell python eng/packaging/packages.py smoke --version 1.0.0-ci.local.1 python eng/packaging/native_consumer.py --version 1.0.0-ci.local.1 ``` -Use a new empty `--directory` for each subsequent candidate and pass it to verification/consumption; -use `--native-directory` when packing from an alternate staged payload. For guard tests set -`ARCFORGES_PACKAGE_DIRECTORY` to that candidate directory. The tools reject overwriting candidates. -Consumers retain temporary directories outside the repository for inspecting projects and lock files. - -PR and main CI share one reusable gate, in this order: - -1. Compile/test both native profiles, execute producer C# ABI tests, audit dependency/source closure - and upload `native-stage--`. Source/secret checks run alongside native build. -2. After native and source gates succeed, download and hash-check that source commit's native artifact. - Restore/build/test managed projects, pack the allowlist once, and verify content and negative fixtures. -3. Consume those exact packages in independent Windows/Linux Build.Policy projects. On Windows, also - build/run JIT and Native AOT callers of each native package separately and all four together, link/run - a C17 caller from packaged headers/import libraries, and reject wrong RID and missing/tampered DLLs. -4. The aggregate gate requires every job to succeed. Only the main workflow can then authenticate and - upload the same candidate bytes. Failed or skipped prerequisites cannot reach publication. - -CI records source commit/version/package SHA256 and native artifact digest in `manifest.json` and tests -those bytes. Consumers use an empty NuGet cache and source mapping forcing ArcForges -packages to the candidate feed. Their build/run and negative central-version/compiler/lock fixtures -must pass. CI retains the candidate as `nuget-candidate--` for 30 days. -Windows also retains consumer projects, locks and the hashed success report as -`native-consumer-evidence--`; a failed run may retain partial fixtures without a success report. -The producer exports that exact artifact name to consumers and publication, so retrying a consumer -continues to use the original tested bytes and rebuilding in another attempt has a distinct artifact. These artifacts are -internal test inputs, not stable public feed availability. Local builds with uncommitted changes are -only development evidence; public candidates always come from a clean CI checkout. +These commands reject CI execution. Use the existing local toolchain; do not install tools or +repeat passing tests to expand coverage. Candidate guard fixtures are offline unit tests, not +a requirement to download and inspect published packages. See [AGENTS.md](../../AGENTS.md). ## First-time nuget.org setup @@ -103,7 +92,7 @@ and must run through all gates. Local builds and PR candidates never upload to t Duplicate versions fail; there is deliberately no `--skip-duplicate`. NuGet cannot atomically publish ten packages. If upload partially succeeds, inspect the registry and retained manifest and re-run all jobs to allocate a new complete version; do not promote a partial release set or retry it blindly. -Retrying only failed consumers before upload keeps the original candidate. A bad published version is superseded by a new +Retrying a diagnosed failed publication uses the retained candidate. A bad published version is superseded by a new version; consumers retain their prior exact version/lock until the upgrade is approved. ## Consume @@ -167,9 +156,9 @@ NuGet and npm SDK packages; this repository does not duplicate that schema gener ## Admit further capabilities Add only implemented, verified packages with explicit metadata, version compatibility, licence/source -closure and a real isolated package consumer. Extend the validator and consumer proof with +closure and relevant local behavior evidence. Extend the validator for that capability. Native capabilities require one explicit managed package and matching per-RID runtime package, complete transitive native assets under `runtimes//native`, ABI/AOT evidence, and NOTICE/SBOM. New content-parsing APIs also require the relevant sandbox acceptance; -the existing metadata/error queries do not accept untrusted media. New RIDs need native producer and -real package consumer execution on those targets before admission. +the existing metadata/error queries do not accept untrusted media. New RIDs need producer builds and honest platform-specific evidence before admission; +macOS CI and hosted runtime consumers remain prohibited. diff --git a/eng/packaging/native.py b/eng/packaging/native.py index a9429c5..c7f944d 100644 --- a/eng/packaging/native.py +++ b/eng/packaging/native.py @@ -279,17 +279,6 @@ def stage(directory, vcpkg, installed_root): if not system_dependency(dependency): pending.append(dependency) owned = selected[entry["library"].lower() + ".dll"] - library = ctypes.CDLL(str(runtime / owned['name']), winmode=0x900) - class Buffer(ctypes.Structure): - _fields_ = [('data', ctypes.c_void_p), ('capacity', ctypes.c_uint64), ('required', ctypes.c_uint64)] - probe = getattr(library, entry['prefix'] + '_get_build_info') - probe.argtypes = [ctypes.POINTER(Buffer)] - probe.restype = ctypes.c_int32 - output = ctypes.create_string_buffer(4096) - buffer = Buffer(ctypes.cast(output, ctypes.c_void_p), len(output), 0) - require(probe(ctypes.byref(buffer)) == 0 and buffer.required < len(output), 'Native build identity probe failed.') - require(output.raw[:buffer.required].decode('utf-8').endswith(build_identity.native_suffix(identity)), - 'Native binary build identity differs from the actual producer.') require(set(owned["exports"]) == {entry["prefix"] + suffix for suffix in ["_get_abi_version", "_get_build_info", "_get_last_error"]}, "Owned native export set differs from the admitted ABI.") for original, relative in [(ROOT / entry["header"], "include/arc/" + Path(entry["header"]).name), diff --git a/eng/packaging/native_consumer.py b/eng/packaging/native_consumer.py index f1c40db..24f229d 100644 --- a/eng/packaging/native_consumer.py +++ b/eng/packaging/native_consumer.py @@ -29,6 +29,8 @@ def execute(executable, directory, env, failure=False): def consume(directory, version, commit): + packages.require(not os.environ.get("GITHUB_ACTIONS") and os.environ.get("CI", "").lower() != "true", + "Native consumers are explicit local diagnostics only; CI execution is prohibited.") packages.require(os.name == "nt", "win-x64 native consumers require Windows.") manifest = packages.verify(directory, version, commit) root = Path(tempfile.mkdtemp(prefix="arcforges-native-consumer-")).resolve() diff --git a/eng/packaging/packages.py b/eng/packaging/packages.py index 37ea3e7..2cd3bf2 100644 --- a/eng/packaging/packages.py +++ b/eng/packaging/packages.py @@ -182,7 +182,7 @@ def pack(directory, package_version, native_directory=ROOT / "artifacts/native-p report = {'schema': 'arcforges.build-identity.v1', 'owner': 'DesktopPlatform', 'artifact': {'id': entry['id'], 'version': package_version}, 'build': identity, 'axes': axes} archive.writestr('build-identity.json', build_identity.canonical(report)) - digest = inspect(directory / name, entry, package_version, commit) + digest = hashlib.sha256((directory / name).read_bytes()).hexdigest() packages.append({"id": entry["id"], "version": package_version, "file": name, "sha256": digest}) native_artifact = (native_directory / "native-artifact.json").read_bytes() (directory / "native-artifact.json").write_bytes(native_artifact) @@ -239,6 +239,8 @@ def verify(directory, package_version, commit=None): def smoke(directory, package_version, commit=None): + require(not os.environ.get("GITHUB_ACTIONS") and os.environ.get("CI", "").lower() != "true", + "Package consumers are explicit local diagnostics only; CI execution is prohibited.") verify(directory, package_version, commit) smoke_policy(directory, package_version)