Skip to content

feat(parser): reusable workflow limits and prefix canonicalization #370

feat(parser): reusable workflow limits and prefix canonicalization

feat(parser): reusable workflow limits and prefix canonicalization #370

Workflow file for this run

name: ci
permissions:
contents: read
on:
push:
branches: [main]
pull_request:
paths-ignore:
- "**.md"
schedule:
- cron: "0 2 * * *"
workflow_dispatch:
inputs:
profile:
description: "Property profile: fast (default) or intensive"
required: false
default: "fast"
jobs:
rust:
runs-on: [self-hosted, preloop-cpane]
timeout-minutes: 60
env:
CARGO_BUILD_JOBS: "2"
PROPTEST_CASES: "8"
RUST_TEST_THREADS: "2"
RUSTFLAGS: "-C link-arg=-fuse-ld=lld"
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Materialize conformance goldens
run: |
git lfs install --local
git lfs pull --include=".runner-watch/golden/**"
fixture=".runner-watch/golden/v2.335.1/06-multi-step/flows.jsonl"
test -s "$fixture"
if grep -q '^version https://git-lfs.github.com/spec/' "$fixture"; then
echo "LFS fixture was not materialized: $fixture" >&2
exit 1
fi
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.97"
components: rustfmt,clippy
- name: Install lld linker
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends lld
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- run: cargo fmt --all --check
- run: |
cargo clippy --locked -p preloop-gha-protocol -p preloop-gha-parser -p preloop-gha-expressions -p preloop-cache -p preloop-artifacts -p runner-watch -p preloop-conformance --all-targets -- -D warnings
cargo clippy --locked -p preloop-runner -p preloop-runner-server -p preloop-runner-client -p preloop-dap --all-targets
- run: cargo test --locked --workspace
env:
RUSTFLAGS: "-A warnings -C link-arg=-fuse-ld=lld"
property-tests-fast:
runs-on: [self-hosted, preloop-cpane]
timeout-minutes: 30
if: github.event_name != 'schedule'
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.97"
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Verify concurrency_properties filter matches at least one test
run: |
count=$(PROPTEST_CASES=1 cargo test -p preloop-runner-server -- --list 2>/dev/null | grep -c 'concurrency_properties' || true)
if [ "$count" -eq 0 ]; then
echo "ERROR: filter 'concurrency_properties' matched zero tests in preloop-runner-server" >&2
exit 1
fi
echo "concurrency_properties: $count test(s) found"
- name: Verify property filters match tests
run: |
server=$(PROPTEST_CASES=1 cargo test -p preloop-runner-server -- --list 2>/dev/null | grep -Ec 'concurrency::properties|concurrency_properties|concurrency_http_properties' || true)
runner=$(cargo test -p preloop-runner -- --list 2>/dev/null | grep -c 'timespan_tests' || true)
parser=$(cargo test -p preloop-gha-parser -- --list 2>/dev/null | grep -c 'concurrency_' || true)
if [ "$server" -eq 0 ] || [ "$runner" -eq 0 ] || [ "$parser" -eq 0 ]; then
echo "ERROR: zero matching tests (server=$server runner=$runner parser=$parser)" >&2
exit 1
fi
- name: Check no sleep() in property test files
run: |
files="crates/preloop-runner-server/src/concurrency_properties.rs crates/preloop-runner-server/src/concurrency_http_properties.rs"
bad=0
for f in $files; do
if [ -f "$f" ] && grep -q 'sleep(' "$f"; then
echo "ERROR: sleep() found in $f" >&2
bad=1
fi
done
if [ "$bad" -ne 0 ]; then exit 1; fi
echo "Sleep guard: OK"
- name: Pure concurrency properties
run: |
PROPTEST_CASES=256 cargo test -p preloop-runner-server \
'concurrency::properties' -- --test-threads=1
PROPTEST_CASES=256 cargo test -p preloop-runner-server \
concurrency_properties::pure -- --test-threads=1
- name: Scheduler model properties
run: |
PROPTEST_CASES=256 cargo test -p preloop-runner-server \
concurrency_properties::state_machine -- --test-threads=1
- name: Expression and parser properties
run: |
PROPTEST_CASES=256 cargo test -p preloop-gha-expressions -- --test-threads=1
cargo test -p preloop-gha-parser concurrency_ -- --test-threads=1
- name: Runner dispatcher properties
run: |
cargo test -p preloop-runner timespan_tests -- --test-threads=1
- name: HTTP sequence properties
run: |
PROPTEST_CASES=64 cargo test -p preloop-runner-server \
concurrency_http_properties -- --test-threads=1
- name: Differential harness dry-run (schema + algebraic validation)
run: |
python3 benchmarks/real-world/run-concurrency-property-probes.py \
--dry-run \
--corpus benchmarks/real-world/concurrency-property-cases.json
- name: Differential harness rejects contaminated fixture
run: |
set +e
python3 benchmarks/real-world/run-concurrency-property-probes.py \
--dry-run \
--corpus benchmarks/real-world/fixtures/contaminated-case.json
status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "ERROR: harness did not reject contaminated fixture (expected nonzero exit)" >&2
exit 1
fi
echo "Contamination rejection: OK (exit $status)"
- name: Upload proptest regressions
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: proptest-regressions-fast
path: |
crates/**/proptest-regressions/
if-no-files-found: ignore
property-tests-intensive:
runs-on: [self-hosted, preloop-cpane]
timeout-minutes: 120
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.profile == 'intensive')
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.97"
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Verify concurrency_properties filter matches at least one test
run: |
count=$(PROPTEST_CASES=1 cargo test -p preloop-runner-server -- --list 2>/dev/null | grep -c 'concurrency_properties' || true)
if [ "$count" -eq 0 ]; then
echo "ERROR: filter 'concurrency_properties' matched zero tests" >&2
exit 1
fi
- name: Verify property filters match tests
run: |
server=$(PROPTEST_CASES=1 cargo test -p preloop-runner-server -- --list 2>/dev/null | grep -Ec 'concurrency::properties|concurrency_properties|concurrency_http_properties' || true)
runner=$(cargo test -p preloop-runner -- --list 2>/dev/null | grep -c 'timespan_tests' || true)
parser=$(cargo test -p preloop-gha-parser -- --list 2>/dev/null | grep -c 'concurrency_' || true)
if [ "$server" -eq 0 ] || [ "$runner" -eq 0 ] || [ "$parser" -eq 0 ]; then
echo "ERROR: zero matching tests (server=$server runner=$runner parser=$parser)" >&2
exit 1
fi
- name: Check no sleep() in property test files
run: |
bad=0
for f in \
crates/preloop-runner-server/src/concurrency_properties.rs \
crates/preloop-runner-server/src/concurrency_http_properties.rs; do
if [ -f "$f" ] && grep -q 'sleep(' "$f"; then
echo "ERROR: sleep() found in $f" >&2
bad=1
fi
done
if [ "$bad" -ne 0 ]; then exit 1; fi
- name: Intensive property profile (release)
run: |
PROPTEST_CASES=10000 PROPTEST_MAX_SHRINK_ITERS=100000 \
cargo test -p preloop-runner-server 'concurrency::properties' \
--release -- --test-threads=1
PROPTEST_CASES=10000 PROPTEST_MAX_SHRINK_ITERS=100000 \
cargo test -p preloop-runner-server concurrency_properties \
--release -- --test-threads=1
- name: Intensive runner timing profile (release)
run: cargo test -p preloop-runner timespan_tests --release -- --test-threads=1
- name: Intensive expression / parser profile (release)
run: |
PROPTEST_CASES=10000 cargo test -p preloop-gha-expressions \
--release -- --test-threads=1
cargo test -p preloop-gha-parser concurrency_ \
--release -- --test-threads=1
- name: Intensive HTTP sequence profile (release)
run: |
PROPTEST_CASES=1000 PROPTEST_MAX_SHRINK_ITERS=100000 \
cargo test -p preloop-runner-server concurrency_http_properties \
--release -- --test-threads=1
- name: Differential harness dry-run
run: |
python3 benchmarks/real-world/run-concurrency-property-probes.py \
--dry-run \
--corpus benchmarks/real-world/concurrency-property-cases.json
- name: Differential harness rejects contaminated fixture
run: |
set +e
python3 benchmarks/real-world/run-concurrency-property-probes.py \
--dry-run \
--corpus benchmarks/real-world/fixtures/contaminated-case.json
status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "ERROR: harness did not reject contaminated fixture" >&2
exit 1
fi
- name: Upload proptest regressions
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: proptest-regressions-intensive
path: |
crates/**/proptest-regressions/
if-no-files-found: ignore