ci(test): add restricted-environment hermetic validation lane

This commit is contained in:
argenis de la rosa 2026-03-05 02:50:14 -05:00 committed by Argenis
parent 69232d0eaa
commit 7a07f2b90f
3 changed files with 98 additions and 4 deletions

View File

@ -225,6 +225,32 @@ jobs:
if-no-files-found: ignore
retention-days: 14
restricted-hermetic:
name: Restricted Hermetic Validation
needs: [changes]
if: needs.changes.outputs.rust_changed == 'true'
runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner]
timeout-minutes: 45
env:
CARGO_HOME: ${{ github.workspace }}/.ci-rust/${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}/cargo
RUSTUP_HOME: ${{ github.workspace }}/.ci-rust/${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}/rustup
CARGO_TARGET_DIR: ${{ github.workspace }}/.ci-rust/${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}/target
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Self-heal Rust toolchain cache
shell: bash
run: ./scripts/ci/self_heal_rust_toolchain.sh 1.92.0
- uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
with:
toolchain: 1.92.0
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v3
with:
prefix-key: ci-run-restricted-hermetic
cache-bin: false
- name: Run restricted-profile hermetic subset
shell: bash
run: ./scripts/ci/restricted_profile.sh
build:
name: Build (Smoke)
needs: [changes]
@ -501,7 +527,7 @@ jobs:
ci-required:
name: CI Required Gate
if: always()
needs: [changes, lint, workspace-check, package-check, test, build, cross-platform-vm, linux-distro-container, docker-smoke, docs-only, non-rust, docs-quality, lint-feedback, license-file-owner-guard]
needs: [changes, lint, workspace-check, package-check, test, restricted-hermetic, build, cross-platform-vm, linux-distro-container, docker-smoke, docs-only, non-rust, docs-quality, lint-feedback, license-file-owner-guard]
runs-on: [self-hosted, Linux, X64, aws-india, light, cpu40]
steps:
- name: Enforce required status
@ -558,6 +584,7 @@ jobs:
workspace_check_result="${{ needs.workspace-check.result }}"
package_check_result="${{ needs.package-check.result }}"
test_result="${{ needs.test.result }}"
restricted_hermetic_result="${{ needs.restricted-hermetic.result }}"
build_result="${{ needs.build.result }}"
cross_platform_vm_result="${{ needs.cross-platform-vm.result }}"
linux_distro_container_result="${{ needs.linux-distro-container.result }}"
@ -567,6 +594,7 @@ jobs:
echo "workspace-check=${workspace_check_result}"
echo "package-check=${package_check_result}"
echo "test=${test_result}"
echo "restricted-hermetic=${restricted_hermetic_result}"
echo "build=${build_result}"
echo "cross-platform-vm=${cross_platform_vm_result}"
echo "linux-distro-container=${linux_distro_container_result}"
@ -576,8 +604,8 @@ jobs:
check_pr_governance
if [ "$lint_result" != "success" ] || [ "$workspace_check_result" != "success" ] || [ "$package_check_result" != "success" ] || [ "$test_result" != "success" ] || [ "$build_result" != "success" ] || [ "$cross_platform_vm_result" != "success" ] || [ "$linux_distro_container_result" != "success" ] || [ "$docker_smoke_result" != "success" ]; then
echo "Required CI jobs did not pass: lint=${lint_result} workspace-check=${workspace_check_result} package-check=${package_check_result} test=${test_result} build=${build_result} cross-platform-vm=${cross_platform_vm_result} linux-distro-container=${linux_distro_container_result} docker-smoke=${docker_smoke_result}"
if [ "$lint_result" != "success" ] || [ "$workspace_check_result" != "success" ] || [ "$package_check_result" != "success" ] || [ "$test_result" != "success" ] || [ "$restricted_hermetic_result" != "success" ] || [ "$build_result" != "success" ] || [ "$cross_platform_vm_result" != "success" ] || [ "$linux_distro_container_result" != "success" ] || [ "$docker_smoke_result" != "success" ]; then
echo "Required CI jobs did not pass: lint=${lint_result} workspace-check=${workspace_check_result} package-check=${package_check_result} test=${test_result} restricted-hermetic=${restricted_hermetic_result} build=${build_result} cross-platform-vm=${cross_platform_vm_result} linux-distro-container=${linux_distro_container_result} docker-smoke=${docker_smoke_result}"
exit 1
fi

View File

@ -12,7 +12,8 @@ Merge-blocking checks should stay small and deterministic. Optional checks are u
- `.github/workflows/ci-run.yml` (`CI`)
- Purpose: Rust validation (`cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D clippy::correctness`, strict delta lint gate on changed Rust lines, `test`, release build smoke) + docs quality checks when docs change (`markdownlint` blocks only issues on changed lines; link check scans only links added on changed lines)
- Additional behavior: for Rust-impacting PRs and pushes, `CI Required Gate` requires `lint` + `test` + `build` (no PR build-only bypass)
- Additional behavior: for Rust-impacting PRs and pushes, `CI Required Gate` requires `lint` + `test` + `restricted-hermetic` + `build` (no PR build-only bypass)
- Additional behavior: includes `Restricted Hermetic Validation` lane (`./scripts/ci/restricted_profile.sh`) that runs a capability-aware subset with isolated `HOME`/workspace/config roots and no external provider credentials
- Additional behavior: rust-cache is partitioned per job role via `prefix-key` to reduce cache churn across lint/test/build/flake-probe lanes
- Additional behavior: emits `test-flake-probe` artifact from single-retry probe when tests fail; optional blocking can be enabled with repository variable `CI_BLOCK_ON_FLAKE_SUSPECTED=true`
- Additional behavior: PRs that change `.github/workflows/**` require at least one approving review from a login in `WORKFLOW_OWNER_LOGINS` (repository variable fallback: `theonlyhennygod,willsarg`)
@ -137,6 +138,7 @@ Merge-blocking checks should stay small and deterministic. Optional checks are u
- Keep required check naming stable and documented in `docs/operations/required-check-mapping.md` before changing branch protection settings.
- Follow `docs/release-process.md` for verify-before-publish release cadence and tag discipline.
- Keep merge-blocking rust quality policy aligned across `.github/workflows/ci-run.yml`, `dev/ci.sh`, and `.githooks/pre-push` (`./scripts/ci/rust_quality_gate.sh` + `./scripts/ci/rust_strict_delta_gate.sh`).
- Reproduce restricted/hermetic CI behavior locally with `./scripts/ci/restricted_profile.sh` before changing workspace/home-sensitive runtime code.
- Use `./scripts/ci/rust_strict_delta_gate.sh` (or `./dev/ci.sh lint-delta`) as the incremental strict merge gate for changed Rust lines.
- Run full strict lint audits regularly via `./scripts/ci/rust_quality_gate.sh --strict` (for example through `./dev/ci.sh lint-strict`) and track cleanup in focused PRs.
- Keep docs markdown gating incremental via `./scripts/ci/docs_quality_gate.sh` (block changed-line issues, report baseline issues separately).

View File

@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail
# Restricted-profile CI lane:
# - isolates HOME/XDG paths into a throwaway directory
# - forces workspace/config roots away from developer machine defaults
# - runs capability-aware tests that should not require external network access
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
cd "${REPO_ROOT}"
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/zeroclaw-restricted-profile.XXXXXX")"
cleanup() {
rm -rf "${TMP_ROOT}"
}
trap cleanup EXIT
RESTRICTED_HOME="${TMP_ROOT}/home"
RESTRICTED_WORKSPACE="${TMP_ROOT}/workspace-root"
mkdir -p "${RESTRICTED_HOME}" "${RESTRICTED_WORKSPACE}"
chmod 700 "${RESTRICTED_HOME}" "${RESTRICTED_WORKSPACE}"
ORIGINAL_HOME="${HOME:-}"
if [ -z "${RUSTUP_HOME:-}" ] && [ -n "${ORIGINAL_HOME}" ]; then
export RUSTUP_HOME="${ORIGINAL_HOME}/.rustup"
fi
if [ -z "${CARGO_HOME:-}" ] && [ -n "${ORIGINAL_HOME}" ]; then
export CARGO_HOME="${ORIGINAL_HOME}/.cargo"
fi
if [ -n "${CARGO_HOME:-}" ] && [ -d "${CARGO_HOME}/bin" ]; then
case ":${PATH}:" in
*":${CARGO_HOME}/bin:"*) ;;
*) export PATH="${CARGO_HOME}/bin:${PATH}" ;;
esac
fi
export HOME="${RESTRICTED_HOME}"
export USERPROFILE="${RESTRICTED_HOME}"
export XDG_CONFIG_HOME="${RESTRICTED_HOME}/.config"
export XDG_CACHE_HOME="${RESTRICTED_HOME}/.cache"
export XDG_DATA_HOME="${RESTRICTED_HOME}/.local/share"
export ZEROCLAW_WORKSPACE="${RESTRICTED_WORKSPACE}"
mkdir -p "${XDG_CONFIG_HOME}" "${XDG_CACHE_HOME}" "${XDG_DATA_HOME}"
# Keep credential/network assumptions explicit for this lane.
unset GEMINI_OAUTH_CLIENT_ID GEMINI_OAUTH_CLIENT_SECRET OPENAI_API_KEY ANTHROPIC_API_KEY
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY
export NO_PROXY="127.0.0.1,localhost"
tests=(
"skills::tests::load_skills_with_config_reads_open_skills_dir_without_network"
"onboard::wizard::tests::run_models_refresh_uses_fresh_cache_without_network"
"onboard::wizard::tests::quick_setup_respects_zero_claw_workspace_env_layout"
"config::schema::tests::load_or_init_workspace_override_uses_workspace_root_for_config"
)
echo "Running restricted-profile hermetic subset (${#tests[@]} tests)"
for test_name in "${tests[@]}"; do
echo "==> cargo test --locked --lib ${test_name}"
cargo test --locked --lib "${test_name}"
done
echo "Restricted-profile hermetic subset completed successfully."