diff --git a/.circleci/config.yml b/.circleci/config.yml index c8457e28ca0..40b47b27376 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -84,6 +84,27 @@ parameters: go-cache-version: type: string default: "v0.1" + c-go_fresh_tests: + type: boolean + default: false + c-contract_coverage_replay: + type: boolean + default: false + c-nut_provenance_full: + type: boolean + default: false + c-selector_upload_replay: + type: boolean + default: false + c-flaky_report_replay: + type: boolean + default: false + c-l2_fork_parity_replay: + type: boolean + default: false + c-l2_fork_parity_block: + type: string + default: "" orbs: continuation: circleci/continuation@2.0.1 @@ -157,6 +178,7 @@ jobs: c-github-event-base64: << pipeline.parameters.github-event-base64 >> c-go-cache-version: << pipeline.parameters.go-cache-version >> c-publish_contract_artifacts_ref: << pipeline.parameters.publish_contract_artifacts_ref >> + c-l2_fork_parity_block_effective: << pipeline.parameters.c-l2_fork_parity_block >> # Same as above but for booleans. Separate step because CircleCI renders # boolean pipeline params as 0/1 in environment blocks — the "bool" mode # normalizes them to JSON true/false. @@ -166,6 +188,12 @@ jobs: name: Store boolean pipeline parameters in JSON file command: .circleci/scripts/collect-params.sh bool environment: + c-go_fresh_tests_effective: << pipeline.parameters.c-go_fresh_tests >> + c-contract_coverage_replay_effective: << pipeline.parameters.c-contract_coverage_replay >> + c-nut_provenance_full_effective: << pipeline.parameters.c-nut_provenance_full >> + c-selector_upload_replay_effective: << pipeline.parameters.c-selector_upload_replay >> + c-flaky_report_replay_effective: << pipeline.parameters.c-flaky_report_replay >> + c-l2_fork_parity_replay_effective: << pipeline.parameters.c-l2_fork_parity_replay >> c-main_dispatch: << pipeline.parameters.main_dispatch >> c-fault_proofs_dispatch: << pipeline.parameters.fault_proofs_dispatch >> c-reproducibility_dispatch: << pipeline.parameters.reproducibility_dispatch >> diff --git a/.circleci/continue/main.yml b/.circleci/continue/main.yml index 1c691c02aa4..e2367a3acfa 100644 --- a/.circleci/continue/main.yml +++ b/.circleci/continue/main.yml @@ -17,6 +17,60 @@ parameters: c-go-cache-version: type: string default: "v0.1" + c-go_fresh_tests: + type: boolean + default: false + c-go_fresh_tests_effective: + type: boolean + default: false + c-contract_coverage_replay: + type: boolean + default: false + c-contract_coverage_replay_effective: + type: boolean + default: false + c-nut_provenance_full: + type: boolean + default: false + c-nut_provenance_full_effective: + type: boolean + default: false + c-run_nut_provenance_replay: + type: boolean + default: false + c-selector_upload_replay: + type: boolean + default: false + c-selector_upload_replay_effective: + type: boolean + default: false + c-run_selector_upload_replay: + type: boolean + default: false + c-flaky_report_replay: + type: boolean + default: false + c-flaky_report_replay_effective: + type: boolean + default: false + c-run_flaky_report_replay: + type: boolean + default: false + c-l2_fork_parity_replay: + type: boolean + default: false + c-l2_fork_parity_replay_effective: + type: boolean + default: false + c-run_l2_fork_parity_replay: + type: boolean + default: false + c-l2_fork_parity_block: + type: string + default: "" + c-l2_fork_parity_block_effective: + type: string + default: "" c-publish_contract_artifacts_ref: type: string default: "" @@ -703,8 +757,18 @@ commands: # time, so it needs no RUSTFLAGS change and does not perturb the sccache # compile-cache key. The CI env is controlled, so a missing mold must # fail loudly rather than silently falling back to the default linker. - mold -run cargo build $PROFILE $TARGET $PACKAGE $FEATURES $BINARY + if [ "<< parameters.directory >>" = rust ] && [ "<< parameters.profile >>" = debug ] && [ -z "<< parameters.package >>" ] && [ -z "<< parameters.binary >>" ] && [ "<< parameters.features >>" = default ] && [ -z "$TARGET" ]; then + bash ../ops/ci/rust-workspace.sh build + elif [ "<< parameters.directory >>" = rust ] && [ "<< parameters.profile >>" = release ] && [ -z "<< parameters.package >>" ] && [ -z "<< parameters.binary >>" ] && [ "<< parameters.features >>" = default ] && [ -z "$TARGET" ]; then + bash ../ops/ci/rust-e2e-release.sh + else + mold -run cargo build $PROFILE $TARGET $PACKAGE $FEATURES $BINARY + fi no_output_timeout: 30m + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - when: condition: << parameters.save_cache >> steps: @@ -906,22 +970,23 @@ jobs: mkdir -p ./tmp/test-results mkdir -p ./tmp/testlogs - run: - name: Cannon Go lint + name: Cannon Go lint and 64-bit tests + no_output_timeout: <> + environment: + CI_GO_FRESH_TESTS: << pipeline.parameters.c-go_fresh_tests_effective >> command: | - just lint - working_directory: cannon + python3 ops/ci/cannon-go.py --provider circleci --skip-slow-tests "<>" - run: - name: Cannon Go 64-bit tests - no_output_timeout: <> + name: Retain complete Cannon Go reports after failures + when: always command: | - export SKIP_SLOW_TESTS="<>" - TIMEOUT="10m" - if [ "$SKIP_SLOW_TESTS" = "false" ]; then - TIMEOUT="45m" + if [[ -f .ci/cannon-go/run/junit.xml ]]; then + cp .ci/cannon-go/run/junit.xml tmp/test-results/cannon-64.xml fi - ../ops/scripts/gotestsum-split.sh --format=testname --junitfile=../tmp/test-results/cannon-64.xml --jsonfile=../tmp/testlogs/log-64.json \ - -- -timeout=$TIMEOUT -parallel=$(nproc) ./... - working_directory: cannon + if [[ -f .ci/cannon-go/run/original.json ]]; then + cp .ci/cannon-go/run/original.json tmp/testlogs/log-64.json + fi + cp -a .ci/cannon-go/run/. tmp/testlogs/ - go-save-cache: namespace: cannon-go-lint-and-test - store_test_results: @@ -929,6 +994,9 @@ jobs: - store_artifacts: path: ./tmp/testlogs when: always + - store_artifacts: + path: .ci/cannon-go/run + when: always - when: condition: <> steps: @@ -1027,6 +1095,18 @@ jobs: command: | just copy-contract-artifacts working_directory: op-deployer + - run: + name: Bind E2E contract dependency evidence + command: | + if [[ "<>" == ci && "<>" == "--skip test" ]]; then + export CI_COMMIT_SHA="$CIRCLE_SHA1" + python3 ops/ci/go-artifacts.py pack contracts-e2e packages/contracts-bedrock/cache \ + packages/contracts-bedrock/artifacts packages/contracts-bedrock/forge-artifacts \ + op-deployer/pkg/deployer/artifacts/forge-artifacts + fi + - store_artifacts: + path: .ci/go-tests/dependencies/contracts-e2e + when: always - when: condition: << parameters.use_caches >> steps: @@ -1057,13 +1137,11 @@ jobs: - setup_remote_docker: docker_layer_caching: true - run: - name: Run Kontrol build - command: just kontrol-summary-full - working_directory: packages/contracts-bedrock - - run: - name: Build Kontrol summary files - command: just forge-build ./test/kontrol/proofs - working_directory: packages/contracts-bedrock + name: Run original Kontrol summary and proof build with complete evidence + command: python3 ops/ci/kontrol-build.py --provider circleci + - store_artifacts: + path: .ci/kontrol-build/run + when: always - notify-failures-on-develop: mentions: "" @@ -1072,6 +1150,9 @@ jobs: - image: <> resource_class: 2xlarge.gen2 parameters: + parity_suite: + type: string + default: standard test_list: description: List of test files to run type: string @@ -1127,34 +1208,35 @@ jobs: - setup-features: features: <> - run: - name: Run tests + name: Run tests with complete original parity reports command: | - TEST_FILES=$(<>) - TEST_FILES=$(echo "$TEST_FILES" | circleci tests split --split-by=timings) - TEST_FILES=$(echo "$TEST_FILES" | sed 's|^test/||') - MATCH_PATH="./test/{$(echo "$TEST_FILES" | paste -sd "," -)}" - mkdir -p results - forge test --match-path "$MATCH_PATH" --junit > results/results.xml - # Forge exits 0 when the filter matches nothing; fail if no tests ran. - ./scripts/checks/check-junit-tests-ran.sh results/results.xml + python3 ops/ci/contract-suites.py run \ + --suite "<>" --feature "${CI_CONTRACT_FEATURE:-main}" environment: - FOUNDRY_PROFILE: <> - working_directory: packages/contracts-bedrock + CI_CONTRACT_FEATURE: "<>" + CI_CONTRACT_PROFILE: <> + CI_CONTRACT_TEST_LIST: <> + CI_CONTRACT_PROVIDER: circleci no_output_timeout: <> - run: - name: Print failed test traces - command: just test-rerun + name: Print failed dependency traces + command: | + if [ ! -f "../../.ci/contract-suites/<>-${CI_CONTRACT_FEATURE:-main}/run/tests.stage.json" ] \ + && [ -s cache/test-failures ]; then + just test-rerun + fi environment: + CI_CONTRACT_FEATURE: "<>" FOUNDRY_PROFILE: <> working_directory: packages/contracts-bedrock when: on_fail - store_test_results: path: packages/contracts-bedrock/results when: always - - run: - name: Lint forge test names - command: just lint-forge-tests-check-no-build - working_directory: packages/contracts-bedrock + - store_artifacts: + path: .ci/contract-suites + destination: contract-suites + when: always - notify-failures-on-develop: mentions: "" @@ -1257,9 +1339,12 @@ jobs: - run: name: Write pinned block number for cache key command: | - just print-pinned-block-number > ./pinnedBlockNumber.txt - cat pinnedBlockNumber.txt - working_directory: packages/contracts-bedrock + python3 ops/ci/contract-coverage.py preflight + jq -er '.number | if type == "number" and . >= 0 then tostring else error("invalid pinned block") end' \ + .ci/contract-coverage/preflight/block.json > packages/contracts-bedrock/pinnedBlockNumber.txt + cat packages/contracts-bedrock/pinnedBlockNumber.txt + environment: + CI_CONTRACT_RPC_ENV_VAR: <> - restore_cache: name: Restore forked state key: forked-state-contracts-bedrock-tests-upgrade-{{ checksum @@ -1268,24 +1353,23 @@ jobs: features: <> - go-restore-cache: namespace: contracts-bedrock-coverage - - run: - name: Build go-ffi - command: just build-go-ffi - working_directory: packages/contracts-bedrock - - run: - name: Build contract source - command: just build-source - working_directory: packages/contracts-bedrock - run: name: Run coverage tests - command: just coverage-lcov-all + command: | + python3 ops/ci/contract-coverage.py run \ + --feature "<>" --block .ci/contract-coverage/preflight/block.json environment: - FOUNDRY_PROFILE: <> - working_directory: packages/contracts-bedrock + CI_CONTRACT_PROVIDER: circleci + CI_CONTRACT_PROFILE: <> + CI_CONTRACT_COVERAGE_REPLAY: << pipeline.parameters.c-contract_coverage_replay_effective >> + CI_CONTRACT_RPC_ENV_VAR: <> no_output_timeout: <> - run: name: Print failed test traces - command: just test-rerun | tee failed-test-traces.log + command: | + if [[ -s cache/test-failures && ! -f "../../.ci/contract-coverage/<>/run/ordinary/tests.stage.json" ]]; then + just test-rerun | tee failed-test-traces.log + fi environment: FOUNDRY_PROFILE: <> working_directory: packages/contracts-bedrock @@ -1302,6 +1386,13 @@ jobs: - store_artifacts: path: packages/contracts-bedrock/failed-test-traces.log when: on_fail + - store_artifacts: + path: .ci/contract-coverage + destination: contract-coverage + when: always + - store_test_results: + path: .ci/contract-coverage + when: always - notify-failures-on-develop: mentions: "" @@ -1323,6 +1414,10 @@ jobs: description: Profile to use for testing type: string default: ci + parity_variant: + description: Shared full-workload parity identity; empty preserves the generic adapter + type: string + default: "" features: description: Comma-separated list of features to enable (e.g., "OPTIMISM_PORTAL_INTEROP", "CUSTOM_GAS_TOKEN") @@ -1369,9 +1464,14 @@ jobs: - run: name: Run tests command: | - mkdir -p results - just test-upgrade + if [ -n "$CI_CONTRACT_UPGRADE_VARIANT" ]; then + python3 ../../ops/ci/contract-upgrades.py run --variant "$CI_CONTRACT_UPGRADE_VARIANT" + else + mkdir -p results + just test-upgrade + fi environment: + CI_CONTRACT_UPGRADE_VARIANT: <> JUNIT_TEST_PATH: results/results.xml FOUNDRY_FUZZ_SEED: 42424242 FOUNDRY_FUZZ_RUNS: 1 @@ -1382,8 +1482,15 @@ jobs: no_output_timeout: 15m - run: name: Print failed test traces - command: just test-upgrade-rerun | tee failed-test-traces.log + command: | + if [ -n "$CI_CONTRACT_UPGRADE_VARIANT" ]; then + # The shared runner retains the original verdict and separate traces. + cat ../../.ci/contract-upgrades/"$CI_CONTRACT_UPGRADE_VARIANT"/run/rerun.log 2>/dev/null || true + else + just test-upgrade-rerun | tee failed-test-traces.log + fi environment: + CI_CONTRACT_UPGRADE_VARIANT: <> FOUNDRY_FUZZ_SEED: 42424242 FOUNDRY_FUZZ_RUNS: 1 FOUNDRY_PROFILE: <> @@ -1412,10 +1519,19 @@ jobs: - store_test_results: path: packages/contracts-bedrock/results when: always + - store_artifacts: + path: .ci/contract-upgrades + when: always + - store_test_results: + path: .ci/contract-upgrades + when: always - notify-failures-on-develop: mentions: "" contracts-bedrock-tests-l2-fork: parameters: + parity_shadow: + type: boolean + default: false fork_op_chain: description: L2 chain to fork for testing type: string @@ -1450,6 +1566,19 @@ jobs: steps: - utils/checkout-with-mise: enable-mise-cache: true + - when: + condition: + or: + - <> + - equal: [codex/rwx-ci-pilot, <>] + steps: + - run: + name: Validate public L2 archive inputs before compilation + command: | + test "$CIRCLE_BRANCH" = codex/rwx-ci-pilot + python3 ops/ci/contract-l2-fork.py preflight --fork-block "$CI_L2_FORK_BLOCK" + environment: + CI_L2_FORK_BLOCK: <> - go-restore-cache: namespace: contracts-bedrock-tests-l2-fork - install-contracts-dependencies @@ -1491,16 +1620,24 @@ jobs: - run: name: Get latest block number if not specified command: | - if [ "<>" = "latest" ]; then + if [ "$CIRCLE_BRANCH" = codex/rwx-ci-pilot ] && [ -f ../../.ci/contract-l2-fork/preflight/block.json ]; then + BLOCK_NUMBER=$(jq -er '.number' ../../.ci/contract-l2-fork/preflight/block.json) + printf 'export L2_FORK_BLOCK_NUMBER=%s\n' "$BLOCK_NUMBER" >> "$BASH_ENV" + printf 'export L2_FORK_RPC_URL=%q\n' 'https://mainnet.optimism.io' >> "$BASH_ENV" + printf '%s\n' "$BLOCK_NUMBER" > ./l2ForkBlockNumber.txt + echo "Using shared pilot fork block: $BLOCK_NUMBER" + elif [ "$CI_L2_FORK_BLOCK" = "latest" ]; then BLOCK_NUMBER=$(cast block-number --rpc-url "$L2_FORK_RPC_URL") - echo "export L2_FORK_BLOCK_NUMBER=$BLOCK_NUMBER" >> $BASH_ENV + echo "export L2_FORK_BLOCK_NUMBER=$BLOCK_NUMBER" >> "$BASH_ENV" echo "Using latest block number: $BLOCK_NUMBER" echo "$BLOCK_NUMBER" > ./l2ForkBlockNumber.txt else - echo "export L2_FORK_BLOCK_NUMBER=<>" >> $BASH_ENV - echo "Using specified block number: <>" - echo "<>" > ./l2ForkBlockNumber.txt + printf 'export L2_FORK_BLOCK_NUMBER=%q\n' "$CI_L2_FORK_BLOCK" >> "$BASH_ENV" + echo "Using specified block number: $CI_L2_FORK_BLOCK" + printf '%s\n' "$CI_L2_FORK_BLOCK" > ./l2ForkBlockNumber.txt fi + environment: + CI_L2_FORK_BLOCK: <> working_directory: packages/contracts-bedrock - restore_cache: name: Restore forked L2 state @@ -1510,23 +1647,48 @@ jobs: features: <> - run: name: Check NUT bundle is up-to-date - command: just nut-bundle-check-no-build + command: | + if [ "$CI_L2_PARITY_SHADOW" = "true" ] || [ "$CI_L2_PARITY_SHADOW" = "1" ] || [ "$CIRCLE_BRANCH" = codex/rwx-ci-pilot ]; then + status=0 + python3 ../../ops/ci/contract-l2-fork.py prepare > ../../.ci/contract-l2-fork/prepare.console.log 2>&1 || status=$? + tail -n 20 ../../.ci/contract-l2-fork/prepare.console.log + exit "$status" + else + just nut-bundle-check-no-build + fi + environment: + CI_L2_PARITY_SHADOW: <> working_directory: packages/contracts-bedrock + no_output_timeout: 30m - run: name: Run L2 fork tests command: | - mkdir -p results - JUNIT_TEST_PATH=results/results.xml just test-l2-fork-upgrade + if [ "$CI_L2_PARITY_SHADOW" = "true" ] || [ "$CI_L2_PARITY_SHADOW" = "1" ] || [ "$CIRCLE_BRANCH" = codex/rwx-ci-pilot ]; then + status=0 + python3 ../../ops/ci/contract-l2-fork.py run --prepared ../../.ci/contract-l2-fork/prepare \ + --block ../../.ci/contract-l2-fork/preflight/block.json > ../../.ci/contract-l2-fork/run.console.log 2>&1 || status=$? + tail -n 20 ../../.ci/contract-l2-fork/run.console.log + exit "$status" + else + mkdir -p results + JUNIT_TEST_PATH=results/results.xml just test-l2-fork-upgrade + fi environment: FOUNDRY_PROFILE: <> + CI_L2_PARITY_SHADOW: <> working_directory: packages/contracts-bedrock no_output_timeout: 20m - run: name: Print failed test traces command: | - just test-l2-fork-upgrade-rerun | tee failed-test-traces-l2-fork.log + if [ "$CI_L2_PARITY_SHADOW" = "true" ] || [ "$CI_L2_PARITY_SHADOW" = "1" ] || [ "$CIRCLE_BRANCH" = codex/rwx-ci-pilot ]; then + tail -n 20 ../../.ci/contract-l2-fork/run/rerun.log 2>/dev/null || true + else + just test-l2-fork-upgrade-rerun | tee failed-test-traces-l2-fork.log + fi environment: FOUNDRY_PROFILE: <> + CI_L2_PARITY_SHADOW: <> working_directory: packages/contracts-bedrock when: on_fail - go-save-cache: @@ -1544,10 +1706,34 @@ jobs: - store_test_results: path: packages/contracts-bedrock/results when: always + - when: + condition: + or: + - <> + - equal: [codex/rwx-ci-pilot, <>] + steps: + - run: + name: Archive complete original L2 fork evidence + when: always + command: | + if [ -d .ci/contract-l2-fork ]; then + tar -C .ci/contract-l2-fork -czf .ci/contract-l2-fork-originals.tar.gz . + fi + - store_artifacts: + path: .ci/contract-l2-fork-originals.tar.gz + destination: contract-l2-fork-originals.tar.gz + when: always + - store_test_results: + path: .ci/contract-l2-fork/run + when: always - notify-failures-on-develop: mentions: "" contracts-bedrock-upload: + parameters: + selector_shadow: + type: boolean + default: false machine: true resource_class: large.gen2 steps: @@ -1558,10 +1744,58 @@ jobs: - check-changed: patterns: contracts-bedrock - get-target-branch - - run: - name: upload selectors - command: just update-selectors - working_directory: packages/contracts-bedrock + - unless: + condition: << parameters.selector_shadow >> + steps: + - run: + name: upload selectors + command: just update-selectors + working_directory: packages/contracts-bedrock + - when: + condition: << parameters.selector_shadow >> + steps: + - run: + name: Prepare private selector registry and complete ABI inventory + command: | + test "$CIRCLE_BRANCH" = codex/rwx-ci-pilot + mkdir -p .ci/selector-upload + # Keep the whole console stream; Circle's log API caps a + # step at 400 kB while this complete ABI table is larger. + { + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + bash .circleci/scripts/apt-install.sh iproute2 openssl + python3 ops/ci/selector-registry.py images .ci/selector-registry + python3 ops/ci/selector-upload.py prepare .ci/selector-upload/prepare + } > .ci/selector-upload/prepare.console.log 2>&1 + - run: + name: Run original selector publisher against private official registry + command: | + python3 ops/ci/selector-upload.py run .ci/selector-upload/run .ci/selector-upload/prepare \ + .ci/selector-registry/sourcify .ci/selector-registry/images.json + - run: + name: Archive complete original selector reports + when: always + command: | + # Circle's directory artifact uploader omits empty files. + # Preserve every original byte and path in one archive too. + mkdir -p .ci/selector-upload + tar -C .ci/selector-upload -czf .ci/selector-upload-originals.tar.gz . + - store_artifacts: + path: .ci/selector-upload-originals.tar.gz + destination: selector-upload-originals.tar.gz + when: always + - run: + name: Archive complete original registry preparation + when: always + command: | + if [ -d .ci/selector-registry ]; then + tar -C .ci -czf .ci/selector-registry-originals.tar.gz selector-registry + fi + - store_artifacts: + path: .ci/selector-registry-originals.tar.gz + destination: selector-registry-originals.tar.gz + when: always required-contracts-ci: docker: @@ -1593,8 +1827,14 @@ jobs: command: forge --version - run: name: Run checks - command: just check-fast + command: python3 ../../ops/ci/pr-checks.py contracts-fast working_directory: packages/contracts-bedrock + - store_test_results: + path: .ci/pr-checks/contracts-fast + when: always + - store_artifacts: + path: .ci/pr-checks/contracts-fast + when: always - notify-failures-on-develop: mentions: "" @@ -1606,7 +1846,13 @@ jobs: enable-mise-cache: false - run: name: Check l2-rpcs.json and daily matrix are in sync - command: bash .circleci/scripts/check-l2-chains-sync.sh + command: python3 ops/ci/main-checks.py l2-chains-sync-check + - store_artifacts: + path: .ci/main-checks/l2-chains-sync-check + when: always + - store_test_results: + path: .ci/main-checks/l2-chains-sync-check + when: always todo-issues: parameters: @@ -1621,9 +1867,18 @@ jobs: enable-mise-cache: true - run: name: Check TODO issues - command: ./ops/scripts/todo-checker.sh --verbose --strict - <<#parameters.check_closed>> --check-closed - <> + command: | + if [ "<>" = "true" ]; then + ./ops/scripts/todo-checker.sh --verbose --strict --check-closed + else + python3 ops/ci/main-checks.py todo-issues-check + fi + - store_artifacts: + path: .ci/main-checks/todo-issues-check + when: always + - store_test_results: + path: .ci/main-checks/todo-issues-check + when: always - notify-failures-on-develop go-lint: @@ -1691,8 +1946,7 @@ jobs: enable-mise-cache: true - run: name: check op-geth version - command: | - just check-op-geth-version + command: python3 ops/ci/main-checks.py check-op-geth-version # Builds op-core/superchain/superchain-configs.zip from the pinned commit and # makes it available to downstream Go-build jobs via persist_to_workspace. @@ -1704,6 +1958,13 @@ jobs: # ~10s) and most of the job's wall-clock is mise restore anyway. Always # rebuilding gives strong consistency (every run validates the committed # .sha256 against a freshly-built zip) without cache-key complexity. + - store_artifacts: + path: .ci/main-checks/check-op-geth-version + when: always + - store_test_results: + path: .ci/main-checks/check-op-geth-version + when: always + prep-superchain: docker: - image: <> @@ -1747,21 +2008,15 @@ jobs: - run: name: Download Go modules command: | - # proxy.golang.org intermittently returns transient stream errors - # (INTERNAL_ERROR). Retry with exponential backoff so a flaky - # download doesn't fail this shared prep job and everything behind - # it. - n=0 - until go mod download; do - n=$((n+1)) - if [ "$n" -ge 5 ]; then - echo "go mod download failed after $n attempts" >&2 - exit 1 - fi - backoff=$((5 * 2 ** (n - 1))) - echo "go mod download failed (attempt $n); retrying in ${backoff}s..." >&2 - sleep "$backoff" - done + # Preserve the five downloads and exponential backoff, verify all + # downloaded modules, and retain the complete original module graph. + python3 ops/ci/pr-checks.py go-modules + - store_test_results: + path: .ci/pr-checks/go-modules + when: always + - store_artifacts: + path: .ci/pr-checks/go-modules + when: always - save-go-mod-cache check-nut-locks: @@ -1776,8 +2031,13 @@ jobs: namespace: check-nut-locks - run: name: check nut locks - command: | - go run ./ops/scripts/check-nut-locks + command: python3 ops/ci/main-checks.py check-nut-locks + - store_artifacts: + path: .ci/main-checks/check-nut-locks + when: always + - store_test_results: + path: .ci/main-checks/check-nut-locks + when: always - go-save-cache: namespace: check-nut-locks @@ -1796,9 +2056,15 @@ jobs: - run: name: check NUT pre-fork states command: | - just _check-nut-prefork-states + python3 ops/ci/nut-prefork.py --provider circleci - go-save-cache: namespace: check-nut-prefork-states + - store_test_results: + path: .ci/nut-prefork/run/native.junit.xml + when: always + - store_artifacts: + path: .ci/nut-prefork/run + when: always nut-provenance-verify: docker: @@ -1808,13 +2074,22 @@ jobs: - utils/checkout-with-mise: enable-mise-cache: true - install-contracts-dependencies - - check-changed: - patterns: op-core/nuts + - when: + condition: + not: << pipeline.parameters.c-nut_provenance_full_effective >> + steps: + - check-changed: + patterns: op-core/nuts # The verify script runs `go run ./ops/scripts/nut-provenance-verify`. - restore-go-mod-cache - run: name: verify NUT bundle provenance command: ./ops/scripts/nut-provenance-verify-changed.sh + environment: + CI_NUT_PROVENANCE_FULL: << pipeline.parameters.c-nut_provenance_full_effective >> + - store_artifacts: + path: .ci/nut-provenance/run + when: always go-tests: parameters: @@ -1872,6 +2147,12 @@ jobs: <> export TEST_TIMEOUT=<> just <> + environment: + CI_GO_FRESH_TESTS: << pipeline.parameters.c-go_fresh_tests_effective >> + - store_artifacts: + path: ./tmp/test-results + destination: go-junit + when: always - go-save-cache: namespace: go-tests - store_test_results: @@ -2104,6 +2385,14 @@ jobs: - run: name: Build prestates command: just reproducible-prestate + - run: + name: Bind original prestate hashes + command: | + export CI_COMMIT_SHA="$CIRCLE_SHA1" + python3 ops/ci/go-artifacts.py pack prestate rust/kona/prestate-artifacts-* + - store_artifacts: + path: .ci/go-tests/dependencies/prestate + when: always - persist_to_workspace: root: . paths: @@ -2163,6 +2452,9 @@ jobs: semgrep-scan: parameters: + parity_job: + type: string + default: "" diff_branch: type: string default: develop @@ -2175,7 +2467,7 @@ jobs: SEMGREP_BRANCH: << pipeline.git.branch >> SEMGREP_COMMIT: << pipeline.git.revision >> docker: - - image: returntocorp/semgrep + - image: returntocorp/semgrep@sha256:32e459968daabe7ab86968184a29109b9564aa00392401156f9788452b42786b resource_class: xlarge.gen2 steps: - checkout # no need to use mise here since the docker image contains the only dependency @@ -2200,11 +2492,39 @@ jobs: # --timeout (in seconds) limits the time per rule and file. # SEMGREP_TIMEOUT is the same, but docs have conflicting defaults (5s in CLI flag, 1800 in some places) # https://semgrep.dev/docs/troubleshooting/semgrep-app#if-the-job-is-aborted-due-to-taking-too-long - command: << parameters.scan_command >> + command: | + if [[ -n "<>" ]]; then + python3 ops/ci/static-checks.py "<>" + else + << parameters.scan_command >> + fi # If semgrep hangs, stop the scan after 20m, to prevent a useless 5h job no_output_timeout: 20m + - store_artifacts: + path: .ci/static-checks + destination: static-checks + when: always + - store_test_results: + path: .ci/static-checks + when: always - notify-failures-on-develop + pr-shell-check: + docker: + - image: cimg/base@sha256:eba1e6c828517f50eb61bbfdc0c5ba51c6b8fb52ea1dc5101ed79ea9617ab3da + steps: + - checkout + - run: + name: Run complete original ShellCheck orb command + command: python3 ops/ci/static-checks.py shell-check + - store_artifacts: + path: .ci/static-checks/shell-check + destination: static-checks/shell-check + when: always + - store_test_results: + path: .ci/static-checks/shell-check + when: always + check-generated-mocks-op-node: docker: - image: <> @@ -2219,7 +2539,13 @@ jobs: namespace: check-generated-mocks-op-node - run: name: check-generated-mocks - command: just generate-mocks-op-node && git diff --exit-code + command: python3 ops/ci/main-checks.py check-generated-mocks-op-node + - store_artifacts: + path: .ci/main-checks/check-generated-mocks-op-node + when: always + - store_test_results: + path: .ci/main-checks/check-generated-mocks-op-node + when: always - go-save-cache: namespace: check-generated-mocks-op-node @@ -2237,7 +2563,13 @@ jobs: namespace: check-generated-mocks-op-service - run: name: check-generated-mocks - command: just generate-mocks-op-service && git diff --exit-code + command: python3 ops/ci/main-checks.py check-generated-mocks-op-service + - store_artifacts: + path: .ci/main-checks/check-generated-mocks-op-service + when: always + - store_test_results: + path: .ci/main-checks/check-generated-mocks-op-service + when: always - go-save-cache: namespace: check-generated-mocks-op-service @@ -2249,8 +2581,13 @@ jobs: checkout-method: blobless enable-mise-cache: true - run: - command: just check-forge-version - working_directory: op-deployer + command: python3 ops/ci/main-checks.py op-deployer-forge-version + - store_artifacts: + path: .ci/main-checks/op-deployer-forge-version + when: always + - store_test_results: + path: .ci/main-checks/op-deployer-forge-version + when: always kontrol-tests: docker: @@ -2402,23 +2739,11 @@ jobs: enable-mise-cache: true - install-contracts-dependencies - run: - name: Build contracts - command: | - just build-contracts - working_directory: op-fetcher - - run: - name: Compare forge artifacts - command: | - diff -qr "packages/contracts-bedrock/forge-artifacts/FetchChainInfo.s.sol" \ - "op-fetcher/pkg/fetcher/fetch/forge-artifacts/FetchChainInfo.s.sol" - - if [ $? -ne 0 ]; then - echo "ERROR: The checked-in forge artifacts for FetchChainInfo.s.sol do not match the ci build." - echo "Please run 'just build-contracts' in the op-fetcher directory and commit the changes." - exit 1 - fi - - echo "✅ Checked-in forge artifacts match the ci build" + name: Compile contracts and compare untouched embedded artifacts + command: python3 ops/ci/fetcher-artifacts.py --provider circleci + - store_artifacts: + path: .ci/fetcher-artifacts/run + when: always stale-check: machine: @@ -2471,26 +2796,76 @@ jobs: - run: name: Generate flaky acceptance tests report command: | - # Create reports directory - mkdir -p ./op-acceptance-tests/reports - - # Make the script executable - chmod +x ./op-acceptance-tests/scripts/generate-flaky-tests-report.sh - - # Run the script - ./op-acceptance-tests/scripts/generate-flaky-tests-report.sh \ - --branch "${CIRCLE_BRANCH:-develop}" \ - --org "${CIRCLE_PROJECT_USERNAME}" \ - --repo "${CIRCLE_PROJECT_REPONAME}" \ - --token "${CIRCLE_API_TOKEN}" \ - --output-dir "./op-acceptance-tests/reports" + python3 ops/ci/flaky-report.py run .ci/flaky-report/run + - run: + name: Archive complete original flaky-test report + when: always + command: | + if [ -d .ci/flaky-report/run ]; then + tar -C .ci/flaky-report -czf .ci/flaky-report-originals.tar.gz run + fi + - store_artifacts: + path: .ci/flaky-report-originals.tar.gz + destination: flaky-report-originals.tar.gz + when: always # Store the flaky test reports - store_artifacts: - path: ./op-acceptance-tests/reports + path: .ci/flaky-report/run/reports destination: flaky-test-reports + when: always workflows: + l2-fork-parity-replay: + when: << pipeline.parameters.c-run_l2_fork_parity_replay >> + jobs: + - prep-go-modules: + context: + - circleci-repo-readonly-authenticated-github-token + - contracts-bedrock-tests-l2-fork: + name: contracts-bedrock-tests-l2-fork op-mainnet + parity_shadow: true + fork_op_chain: op-mainnet + l2_fork_rpc: https://mainnet.optimism.io + l2_fork_block_number: <> + test_profile: ci + features: main + requires: + - prep-go-modules + context: + - circleci-repo-readonly-authenticated-github-token + + flaky-report-replay: + when: << pipeline.parameters.c-run_flaky_report_replay >> + jobs: + - generate-flaky-report: + name: generate-flaky-tests-report + context: + - circleci-repo-readonly-authenticated-github-token + - circleci-api-token + + selector-upload-replay: + when: << pipeline.parameters.c-run_selector_upload_replay >> + jobs: + - contracts-bedrock-upload: + selector_shadow: true + context: + - circleci-repo-readonly-authenticated-github-token + + # Isolated full replay of the original job and original module dependency. + # Shared routing selects this only for explicit pilot API benchmarks. + nut-provenance-replay: + when: << pipeline.parameters.c-run_nut_provenance_replay >> + jobs: + - prep-go-modules: + context: + - circleci-repo-readonly-authenticated-github-token + - nut-provenance-verify: + requires: + - prep-go-modules + context: + - circleci-repo-readonly-authenticated-github-token + main: when: << pipeline.parameters.c-run_main >> jobs: @@ -2531,12 +2906,14 @@ workflows: - circleci-repo-readonly-authenticated-github-token - semgrep-scan: name: semgrep-scan-local + parity_job: semgrep-scan-local scan_command: semgrep scan --timeout=100 --config .semgrep/rules/ --error . context: - slack - circleci-repo-readonly-authenticated-github-token - semgrep-scan: name: semgrep-test + parity_job: semgrep-test scan_command: semgrep scan --test --config .semgrep/rules/ .semgrep/tests/ context: - slack @@ -2620,13 +2997,8 @@ workflows: context: - circleci-repo-readonly-authenticated-github-token - slack - - shellcheck/check: + - pr-shell-check: name: shell-check - # We don't need the `exclude` key as the orb detects the `.shellcheckrc` - dir: . - ignore-dirs: | - ./packages/contracts-bedrock/lib - ./docs/public-docs context: - circleci-repo-readonly-authenticated-github-token # Acceptance test jobs (formerly in separate acceptance-tests workflow) @@ -3024,6 +3396,7 @@ workflows: './test/**/*.t.sol' | sed 's|packages/contracts-bedrock/||' test_timeout: 1h test_profile: ciheavy + parity_suite: modified features: <> matrix: parameters: @@ -3087,6 +3460,7 @@ workflows: # On PRs, run upgrade tests with lite profile for better build times. - contracts-bedrock-tests-upgrade: name: contracts-bedrock-tests-upgrade op-mainnet <> + parity_variant: feature-<> requires: - prep-go-modules fork_op_chain: op @@ -3106,6 +3480,7 @@ workflows: # On develop, run upgrade tests with ci profile to mirror production. - contracts-bedrock-tests-upgrade: name: contracts-bedrock-tests-upgrade-develop op-mainnet <> + parity_variant: feature-<> requires: - prep-go-modules fork_op_chain: op @@ -3125,6 +3500,7 @@ workflows: # On PRs, run chain-specific upgrade tests with lite profile for better build times. - contracts-bedrock-tests-upgrade: name: contracts-bedrock-tests-upgrade <>-mainnet + parity_variant: chain-<> requires: - prep-go-modules fork_op_chain: <> @@ -3143,6 +3519,7 @@ workflows: # On develop, run chain-specific upgrade tests with ci profile to mirror production. - contracts-bedrock-tests-upgrade: name: contracts-bedrock-tests-upgrade-develop <>-mainnet + parity_variant: chain-<> requires: - prep-go-modules fork_op_chain: <> diff --git a/.circleci/continue/rust-ci.yml b/.circleci/continue/rust-ci.yml index ca488748604..fc9a826e3e9 100644 --- a/.circleci/continue/rust-ci.yml +++ b/.circleci/continue/rust-ci.yml @@ -87,6 +87,10 @@ jobs: environment: RUSTFLAGS: -Dwarnings command: <> + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *clippy-cache-args # Shared cargo deny job @@ -140,7 +144,16 @@ jobs: - run: name: Check feature propagation working_directory: <> - command: <> + command: | + if [[ "<>" == rust && "<>" == 'zepter run check' ]]; then + bash ../ops/ci/rust-workspace.sh zepter + else + <> + fi + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # No cache save: downloads no crates. # Shared typos check job @@ -161,7 +174,16 @@ jobs: - run: name: Check for typos working_directory: <> - command: typos + command: | + if [[ "<>" == rust ]]; then + bash ../ops/ci/rust-workspace.sh typos + else + typos + fi + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # No cache save: downloads no crates. # Shared no_std compatibility check job @@ -188,6 +210,10 @@ jobs: working_directory: <> no_output_timeout: 30m command: <> + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *no-std-cache-args # Shared documentation build job @@ -214,6 +240,10 @@ jobs: working_directory: <> no_output_timeout: 30m command: <> + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *docs-cache-args # Shared doc test job @@ -239,6 +269,10 @@ jobs: working_directory: <> no_output_timeout: 30m command: <> + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *doctest-cache-args # Shared cargo tests job @@ -272,7 +306,12 @@ jobs: name: Run cargo tests working_directory: <> no_output_timeout: 40m - command: just <> <> + command: | + if [ "<>" = rust ] && [ "<>" = test ] && [ -z "<>" ]; then + bash ../ops/ci/rust-workspace.sh tests + else + just <> <> + fi # Surface individual test results in the CircleCI UI, including on failure. # nextest writes JUnit to /target/nextest/default/junit.xml. - store_test_results: @@ -332,6 +371,10 @@ jobs: echo "slack response: $resp" echo "$resp" | grep -q '"ok":true' || echo "SLACK POST FAILED (non-fatal) — check token/channel/mentions" exit 0 + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *tests-cache-args # Cross-language differential test: runs the Go and Rust dumps of the Interop @@ -350,14 +393,13 @@ jobs: # builds Go code. Restore the shared Go module cache to avoid a cold # `go mod download` (slow, and a per-pipeline proxy.golang.org flake risk). - restore-go-mod-cache - # Build the superchain-configs.zip the Go dumper //go:embeds (no prep-superchain job here). - - run: - name: Build superchain bundle - command: just build-superchain-go - run: - name: Cross-language Interop activation diff - command: | - bash ops/scripts/test-interop-deposits-diff.sh + name: Build superchain bundle and compare original Interop dumps + command: bash ops/ci/rust-workspace.sh interop + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *interop-diff-cache-args # Shared unused dependencies check job @@ -384,6 +426,10 @@ jobs: working_directory: <> no_output_timeout: 40m command: <> + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *udeps-cache-args # Shared cargo hack build job (cross-compile targets like WASM) @@ -411,7 +457,18 @@ jobs: name: Build for <> working_directory: <> no_output_timeout: 40m - command: rustup target add <> && cargo hack build --target <> <> + command: | + if [[ "<>" == rust && "<>" == wasm32-unknown-unknown && "<>" == '-p op-alloy-consensus -p op-alloy-rpc-types -p op-alloy-rpc-types-engine -p alloy-op-evm --no-default-features' ]]; then + bash ../ops/ci/rust-workspace.sh wasm-unknown + elif [[ "<>" == rust && "<>" == wasm32-wasip1 && "<>" == '-p op-alloy-consensus -p op-alloy-rpc-types-engine -p alloy-op-evm' ]]; then + bash ../ops/ci/rust-workspace.sh wasm-wasi + else + rustup target add <> && cargo hack build --target <> <> + fi + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *hack-build-cache-args # Shared cargo hack job @@ -433,31 +490,14 @@ jobs: directory: rust features: "all" - run: - name: Run cargo hack - working_directory: rust - no_output_timeout: 60m - command: | - PARTITION_FLAG="" - if [ "$CIRCLE_NODE_TOTAL" -gt 1 ]; then - PARTITION_FLAG="$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL" - fi - just hack "$PARTITION_FLAG" "true" "$CIRCLE_WORKFLOW_ID" - # Check each crate's test targets in isolation with default features. The - # lib-only `hack` step above never compiles test code, so a crate whose - # tests rely on a feature only enabled via workspace feature unification - # passes the workspace test build yet fails when built alone. This step - # catches that class (whether the missing feature is a dependency's or the - # crate's own). - - run: - name: Run cargo hack (test targets) + name: Run library features and isolated test targets working_directory: rust no_output_timeout: 60m - command: | - PARTITION_FLAG="" - if [ "$CIRCLE_NODE_TOTAL" -gt 1 ]; then - PARTITION_FLAG="$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL" - fi - just hack-tests-default "$PARTITION_FLAG" "true" "$CIRCLE_WORKFLOW_ID" + command: bash ../ops/ci/rust-workspace.sh features + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always - rust-save-build-cache: *hack-cache-args # -------------------------------------------------------------------------- @@ -534,37 +574,15 @@ jobs: extra_flags: "--no-install-recommends" - rust-prepare - run: - name: Build cannon - command: | - cd cannon && just cannon - sudo mv ./bin/cannon /usr/local/bin/ - - run: - name: Set run environment - command: | - echo 'export BLOCK_NUMBER=47600000' >> $BASH_ENV - echo 'export L2_CLAIM=0x6ac07d241d170ddd504532a2c8127bb85426e604a360f8ba2d976be3cc0aa7f0' >> $BASH_ENV - echo 'export L2_OUTPUT_ROOT=0x9c799a1b767f43d6c654f83e18ae20bfc2953e94e5093ef59664ae8244ffdc96' >> $BASH_ENV - echo 'export L2_HEAD=0x0084ac82023844b6f286783f3ea0ffddb8e597211ffbe01b56d8873f36a8bacb' >> $BASH_ENV - echo 'export L1_HEAD=0xf82badb3d56a53c373b501ec24e1e9ab2797feaab552c0d683849b1234308a2d' >> $BASH_ENV - echo 'export L2_CHAIN_ID=11155420' >> $BASH_ENV - - run: - name: Run host + client offline - working_directory: rust/kona/bin/client + name: Full Cannon host/client offline run and fresh guest-state verdict no_output_timeout: 40m - command: | - # Pre-create the workspace target dir as the circleci user. Without - # this, `just build-cannon-client` (invoked inside run-client-cannon-offline) - # runs Docker as root and creates `rust/target/` with root ownership, - # which then breaks the subsequent native `cargo build --bin kona-host` - # with EACCES when it tries to create `target/debug`. - mkdir -p ../../../target - just run-client-cannon-offline \ - $BLOCK_NUMBER \ - $L2_CLAIM \ - $L2_OUTPUT_ROOT \ - $L2_HEAD \ - $L1_HEAD \ - $L2_CHAIN_ID + command: bash ops/ci/rust-cannon.sh offline + - store_test_results: + path: .ci/rust-workspace/cannon-offline/checks.junit.xml + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # Kona Rust CI - Lint (cannon target) kona-cargo-lint: @@ -588,7 +606,18 @@ jobs: working_directory: rust/kona no_output_timeout: 40m command: | - just lint-<> + if [[ "<>" == cannon ]]; then + bash ../../ops/ci/rust-cannon.sh lint + else + just lint-<> + fi + + - store_test_results: + path: .ci/rust-workspace/cannon-lint/checks.junit.xml + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # Kona Build FPVM targets kona-build-fpvm: @@ -610,7 +639,18 @@ jobs: working_directory: rust/kona no_output_timeout: 40m command: | - just build-<> + if [[ "<>" == cannon-client ]]; then + bash ../../ops/ci/rust-cannon.sh build + else + just build-<> + fi + + - store_test_results: + path: .ci/rust-workspace/cannon-build/checks.junit.xml + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # Verifies that the committed `etc/{chainList,configs,depsets}.json` snapshots # in `kona-registry` match what `KONA_SYNC_SUPERCHAIN=true cargo build -p kona-registry` @@ -628,26 +668,12 @@ jobs: - rust-prepare-and-restore-cache: directory: rust - run: - name: Regenerate kona-registry snapshots - working_directory: rust/kona - environment: - KONA_SYNC_SUPERCHAIN: "true" - command: cargo build -p kona-registry - - run: - name: Assert committed snapshots match regenerated output - command: | - if ! git diff --exit-code -- \ - rust/kona/crates/protocol/registry/etc/; then - echo - echo "kona-registry etc/ snapshots are out of sync with the" - echo "superchain-registry submodule. Run:" - echo - echo " KONA_SYNC_SUPERCHAIN=true cargo build -p kona-registry" - echo - echo "from rust/kona and commit the resulting changes to" - echo "rust/kona/crates/protocol/registry/etc/." - exit 1 - fi + name: Regenerate and verify all kona-registry snapshots freshly + command: bash ops/ci/rust-workspace.sh registry + - store_artifacts: + path: .ci/rust-workspace + destination: rust-workspace + when: always # No cache save: single-package build, partial registry. # Verifies that op-reth's committed superchain-configs.tar.sha256 and the @@ -957,7 +983,7 @@ workflows: - rust-ci-clippy: name: rust-clippy directory: rust - command: "cargo clippy --workspace --all-targets --all-features --locked" + command: "bash ../ops/ci/rust-workspace.sh clippy" context: *rust-ci-context - rust-ci-deny: @@ -1002,16 +1028,19 @@ workflows: - rust-ci-doctest: name: rust-doctest directory: rust + command: "bash ../ops/ci/rust-workspace.sh doctest" context: *rust-ci-context - rust-ci-docs: name: rust-docs directory: rust + command: "bash ../ops/ci/rust-workspace.sh docs" context: *rust-ci-context - rust-ci-udeps: name: rust-udeps directory: rust + command: "bash ../ops/ci/rust-workspace.sh udeps" context: *rust-ci-context - rust-ci-cargo-hack: @@ -1037,8 +1066,7 @@ workflows: - rust-ci-check-no-std: name: rust-check-no-std directory: rust - command: | - just check-no-std + command: bash ../ops/ci/rust-workspace.sh no-std context: *rust-ci-context - rust-ci-cargo-hack-build: diff --git a/.circleci/continue/rust-e2e.yml b/.circleci/continue/rust-e2e.yml index 3bb2b4596e8..147b1ac53c2 100644 --- a/.circleci/continue/rust-e2e.yml +++ b/.circleci/continue/rust-e2e.yml @@ -64,7 +64,7 @@ jobs: echo "Running tests..." export RUST_BINARY_PATH_OP_RETH="$WD/rust/target/release/op-reth" export RUST_BINARY_PATH_KONA_NODE="$WD/rust/target/release/kona-node" - cd rust/kona && just test-e2e-sysgo-run node node/common "<>" + bash ops/ci/rust-e2e.sh circle "<>" - when: condition: equal: [true, <>] @@ -81,10 +81,18 @@ jobs: - go-save-cache: namespace: kona-ci - store_test_results: - path: rust/kona/tests/tmp/test-results + path: .ci/rust-e2e/reports - store_artifacts: - path: rust/kona/tests/tmp/testlogs + path: .ci/rust-e2e when: always + - when: + condition: <> + steps: + - store_test_results: + path: rust/kona/tests/tmp/test-results + - store_artifacts: + path: rust/kona/tests/tmp/testlogs + when: always # Kona Node Restart Tests (from node_e2e_sysgo_tests.yaml) rust-restart-sysgo-tests: @@ -115,13 +123,13 @@ jobs: WD=$(pwd) export RUST_BINARY_PATH_OP_RETH="$WD/rust/target/release/op-reth" export RUST_BINARY_PATH_KONA_NODE="$WD/rust/target/release/kona-node" - cd rust/kona && just test-e2e-sysgo-run node node/restart + bash ops/ci/rust-e2e.sh circle restart - go-save-cache: namespace: kona-ci - store_test_results: - path: rust/kona/tests/tmp/test-results + path: .ci/rust-e2e/reports - store_artifacts: - path: rust/kona/tests/tmp/testlogs + path: .ci/rust-e2e when: always # op-reth E2E Sysgo Tests @@ -146,13 +154,13 @@ jobs: no_output_timeout: 60m command: | export RUST_BINARY_PATH_OP_RETH="$(pwd)/../../target/release/op-reth" - just test-e2e-sysgo + bash ../../../ops/ci/rust-e2e.sh circle op-reth - go-save-cache: namespace: op-reth-e2e - store_test_results: - path: rust/op-reth/tests/tmp/test-results + path: .ci/rust-e2e/reports - store_artifacts: - path: rust/op-reth/tests/tmp/testlogs + path: .ci/rust-e2e when: always # Kona Proof Action Tests (from proof.yaml) @@ -185,13 +193,14 @@ jobs: command: | echo "Running action tests" export KONA_HOST_PATH=$(pwd)/../target/release/kona-host - just action-tests-<>-run + if [[ "<>" != single ]]; then echo "Unsupported shared proof kind" >&2; exit 1; fi + bash ../../ops/ci/rust-e2e.sh circle proof - go-save-cache: namespace: kona-ci - store_test_results: - path: rust/kona/tests/proofs/tmp/test-results + path: .ci/rust-e2e/reports - store_artifacts: - path: rust/kona/tests/proofs/tmp/testlogs + path: .ci/rust-e2e when: always # Build SP1 guest ELFs and persist them for future rust-e2e consumers. @@ -213,116 +222,17 @@ jobs: # update changes this checksum and forces a fresh installation. - sp1-toolchain-v1-{{ arch }}-{{ checksum "mise.toml" }} - run: - name: Install or link SP1 toolchain - command: | - install_sp1_toolchain() { - (cd rust/kona/sp1 && just install-sp1-toolchain) - } - - mise_sp1_version="$(yq -r '.tools."github:succinctlabs/sp1".version // ""' mise.toml)" - root_manifest_sp1_sdk="$(yq -r '.workspace.dependencies."sp1-sdk".version // ""' rust/Cargo.toml)" - guest_manifest_sp1_lib="$(yq -r '.workspace.dependencies."sp1-lib".version // ""' rust/kona/sp1/programs/Cargo.toml)" - guest_manifest_sp1_zkvm="$(yq -r '.workspace.dependencies."sp1-zkvm".version // ""' rust/kona/sp1/programs/Cargo.toml)" - sp1_tag="$(cd rust/kona/sp1 && just --evaluate SP1_TAG 2>/dev/null || printf '')" - - lock_data_dir="$(mktemp -d)" - trap 'rm -rf "$lock_data_dir"' EXIT - root_lock_sp1_sdk="" - if yq -p=toml -o=json '.package // []' rust/Cargo.lock > "$lock_data_dir/root.json"; then - if ! root_lock_sp1_sdk="$(jq -er '[.[] | select(.name == "sp1-sdk") | .version] | unique | if length == 1 then .[0] else error("expected exactly one resolved sp1-sdk version") end' "$lock_data_dir/root.json")"; then - root_lock_sp1_sdk="" - fi - fi - - guest_lock_sp1_lib="" - guest_lock_sp1_zkvm="" - if yq -p=toml -o=json '.package // []' rust/kona/sp1/programs/Cargo.lock > "$lock_data_dir/guest.json"; then - if ! guest_lock_sp1_lib="$(jq -er '[.[] | select(.name == "sp1-lib") | .version] | unique | if length == 1 then .[0] else error("expected exactly one resolved sp1-lib version") end' "$lock_data_dir/guest.json")"; then - guest_lock_sp1_lib="" - fi - if ! guest_lock_sp1_zkvm="$(jq -er '[.[] | select(.name == "sp1-zkvm") | .version] | unique | if length == 1 then .[0] else error("expected exactly one resolved sp1-zkvm version") end' "$lock_data_dir/guest.json")"; then - guest_lock_sp1_zkvm="" - fi - fi - - if [ "$root_manifest_sp1_sdk" != "$mise_sp1_version" ] || \ - [ "$guest_manifest_sp1_lib" != "$mise_sp1_version" ] || \ - [ "$guest_manifest_sp1_zkvm" != "$mise_sp1_version" ] || \ - [ "$root_lock_sp1_sdk" != "$mise_sp1_version" ] || \ - [ "$guest_lock_sp1_lib" != "$mise_sp1_version" ] || \ - [ "$guest_lock_sp1_zkvm" != "$mise_sp1_version" ] || \ - [ "$sp1_tag" != "v${mise_sp1_version}" ]; then - printf '%s\n' \ - "ERROR: SP1 version drift; mise.toml cargo-prove is canonical." \ - " mise.toml cargo-prove: ${mise_sp1_version}" \ - " rust/Cargo.toml sp1-sdk: ${root_manifest_sp1_sdk}" \ - " rust/Cargo.lock resolved sp1-sdk: ${root_lock_sp1_sdk}" \ - " rust/kona/sp1/programs/Cargo.toml sp1-lib: ${guest_manifest_sp1_lib}" \ - " rust/kona/sp1/programs/Cargo.toml sp1-zkvm: ${guest_manifest_sp1_zkvm}" \ - " rust/kona/sp1/programs/Cargo.lock resolved sp1-lib: ${guest_lock_sp1_lib}" \ - " rust/kona/sp1/programs/Cargo.lock resolved sp1-zkvm: ${guest_lock_sp1_zkvm}" \ - " rust/kona/sp1/justfile SP1_TAG: ${sp1_tag} (expected v${mise_sp1_version})" >&2 - exit 1 - fi - - toolchain_dir="$(find "$HOME/.sp1/toolchains" -mindepth 1 -maxdepth 1 -type d -print -quit 2>/dev/null || true)" - if [ -n "$toolchain_dir" ]; then - rustup toolchain remove succinct || true - if rustup toolchain link succinct "$toolchain_dir" && rustc +succinct --version >/dev/null; then - echo "Restored SP1 toolchain from $toolchain_dir" - else - echo "Cached SP1 toolchain is invalid; reinstalling" - install_sp1_toolchain - fi - else - install_sp1_toolchain - fi - rustc +succinct --version + name: Install or link SP1 toolchain with original pin checks and evidence + command: python3 ops/ci/sp1-guest.py --provider circleci --phase toolchain - save_cache: name: Save SP1 toolchain cache key: sp1-toolchain-v1-{{ arch }}-{{ checksum "mise.toml" }} paths: - ~/.sp1/toolchains - run: - # Check the committed guest lock before build-elfs-native regenerates it in place and masks drift. - name: Check SP1 guest lock - working_directory: rust - command: just check-sp1-guest-lock - - run: - name: Build SP1 guest ELFs - working_directory: rust/kona/sp1 + name: Build SP1 guest ELFs with complete original artifact evidence no_output_timeout: 40m - command: just build-elfs-native - - run: - # The Go acceptance setup (op-acceptance-tests/tests/proofs/zk, - # loadSuperAggregationVKey) reads this key when running with real - # artifacts (KONA_SP1_ELF_DIR set); renaming it in the vkeys - # manifest breaks that consumer silently, since PR CI runs the - # acceptance tests with stub artifacts. - name: Check vkeys manifest names expected by consumers - command: grep -q '^super-aggregation = "0x' rust/kona/sp1/elf/vkeys.toml - - run: - # The build recipe checks each ELF against the commit it injected; this pins that - # commit to the one under test. Scans permissively so a marker reading `unknown` - # reports as `unknown` rather than as a dropped literal. - name: Check guest ELFs embed the build commit - command: | - set -euo pipefail - EXPECTED=$(git rev-parse HEAD) - for ELF in super-range-elf super-aggregation-elf; do - PATH_TO_ELF="rust/kona/sp1/elf/${ELF}" - set +e - FOUND=$(grep -aoE 'KONA_SP1_BUILD\{git_sha=[0-9A-Za-z._-]*\}' "${PATH_TO_ELF}" | sort -u) - RC=$? - set -e - [ "${RC}" -le 1 ] || { echo "ERROR: grep failed on ${PATH_TO_ELF} (exit ${RC})" >&2; exit 1; } - if [ "${FOUND}" != "KONA_SP1_BUILD{git_sha=${EXPECTED}}" ]; then - echo "ERROR: ${ELF} build markers: ${FOUND:-}" >&2 - echo " expected exactly KONA_SP1_BUILD{git_sha=${EXPECTED}}" >&2 - exit 1 - fi - echo "${ELF}: ${FOUND}" - done + command: python3 ops/ci/sp1-guest.py --provider circleci --phase build - store_artifacts: path: rust/kona/sp1/elf - persist_to_workspace: @@ -330,23 +240,14 @@ jobs: paths: - rust/kona/sp1/elf/ - run: - name: Test SP1 guest programs - working_directory: rust - environment: - RUSTFLAGS: -Dwarnings - command: just test-sp1-guest - - run: - name: Lint SP1 guest programs - working_directory: rust - environment: - RUSTFLAGS: -Dwarnings - command: just lint-sp1-guest - - run: - name: Check + test the range-vkeys crate - working_directory: rust - environment: - RUSTFLAGS: -Dwarnings - command: just check-range-vkeys + name: Run all original SP1 guest, lint and range-vkeys checks freshly + no_output_timeout: 40m + command: python3 ops/ci/sp1-guest.py --provider circleci --phase checks + - store_test_results: + path: .ci/sp1-guest/run + - store_artifacts: + path: .ci/sp1-guest + when: always - rust-save-build-cache: *sp1-guest-checks-cache-args sp1-super-range-elf-smoke: diff --git a/.circleci/routing.yml b/.circleci/routing.yml deleted file mode 100644 index b998c8a11e3..00000000000 --- a/.circleci/routing.yml +++ /dev/null @@ -1,89 +0,0 @@ -# Declarative routing data for the CircleCI setup pipeline. -# -# This file is the single source of truth for WHAT workflows run; the policy -# script (.circleci/scripts/compute-workflow-conditions.sh) decides WHEN they -# run. Keeping the data here means the workflow lists can be read directly by -# tooling (e.g. test-schedule-triggers.js) instead of parsing the script. -# -# Workflow names below map 1:1 to a "c-run_" boolean parameter declared -# in a continuation config under .circleci/continue/. Enabling c-run_ -# makes that continuation workflow execute. - -# Scheduled pipelines: CircleCI schedule name -> workflows it triggers. -# The schedule names must match the triggers configured in the CircleCI UI -# (verified live by test-schedule-triggers.js). -schedules: - build_four_hours: - - scheduled_todo_issues - - scheduled_cannon_full_tests - build_daily: - - scheduled_preimage_reproducibility - - scheduled_stale_check - - scheduled_heavy_fuzz_tests - - scheduled_daily_tests - - scheduled_sp1_elf_smoke - - circleci_schedule_trigger_check - build_weekly: - - scheduled_rust_nightly_bump - -# API triggers: dispatch flag -> workflows enabled when that flag is set. -# Most flags fire on a simple "is the flag true?" check. main_dispatch and -# labeled_pr have more complex conditions (see compute-workflow-conditions.sh), -# but the workflows they enable still live here as data. -api_dispatch: - main_dispatch: - - main - - contracts_feature_tests - fault_proofs_dispatch: - - develop_fault_proofs - kontrol_dispatch: - - develop_kontrol_tests - cannon_full_test_dispatch: - - scheduled_cannon_full_tests - reproducibility_dispatch: - - scheduled_preimage_reproducibility - stale_check_dispatch: - - scheduled_stale_check - heavy_fuzz_dispatch: - - scheduled_heavy_fuzz_tests - publish_contract_artifacts_dispatch: - - publish_contract_artifacts - l2_fork_test_dispatch: - - l2_fork_test - rust_ci_dispatch: - - rust_ci - rust_e2e_dispatch: - - rust_e2e_ci - rust_nightly_bump_dispatch: - - scheduled_rust_nightly_bump - labeled_pr: - - close_issue - -# Change-detection patterns. Each value is a POSIX Extended Regular Expression -# (ERE, like grep -E) matched line-by-line against the changed file list -# (git diff --name-only origin/develop...HEAD). collect-params.sh reads these. -# -# Test a pattern locally: -# git diff --name-only origin/develop...HEAD | grep -E "^your/pattern/" -change_patterns: - # "any": c- is true iff AT LEAST ONE changed file matches. - any: - circleci_changed: "^\\.circleci/" - contracts_changed: "^(packages/contracts-bedrock|op-core/forks|op-core/nuts|\\.circleci|\\.github|ops/check-changed)/|^(package\\.json|mise\\.toml)$" - docs_changes_detected: "^docs/public-docs/" - rust_changes_detected: "^(rust|op-e2e|\\.circleci)/|^mise\\.toml$" - # "all": c- is true iff EVERY changed file matches (and there is at - # least one). Used for the safe-by-default docs-only fast path: any path not - # enumerated still falls through to the full main workflow. - all: - only_docs_changes: "^docs/public-docs/" - -# Pipeline parameters forwarded to continuation configs. Everything else in the -# parameters JSON is stripped before the continuation step (see finalize). -passthrough_params: - - c-default_docker_image - - c-rust_base_image - - c-base_image - - c-github-event-base64 - - c-go-cache-version - - c-publish_contract_artifacts_ref diff --git a/.circleci/routing.yml b/.circleci/routing.yml new file mode 120000 index 00000000000..2ce40295dec --- /dev/null +++ b/.circleci/routing.yml @@ -0,0 +1 @@ +../ops/ci/routing.yml \ No newline at end of file diff --git a/.circleci/scripts/collect-params.sh b/.circleci/scripts/collect-params.sh index 87643d5675e..bbc1432ce1f 100755 --- a/.circleci/scripts/collect-params.sh +++ b/.circleci/scripts/collect-params.sh @@ -1,87 +1,5 @@ #!/usr/bin/env bash -# Collects pipeline parameters from the environment and writes them to a JSON file. -# -# Called once per type with a mode argument: -# collect-params.sh str — emit all c-* env vars as JSON strings -# collect-params.sh bool — emit all c-* env vars as JSON booleans (normalizes 0/1) -# collect-params.sh detect — match routing.yml change_patterns.any against changed files; c- true iff ANY file matches -# collect-params.sh detect_all — match routing.yml change_patterns.all against changed files; c- true iff EVERY file matches (and there is at least one) -# -# str/bool read c-* env vars (CircleCI pipeline params). detect/detect_all read -# their ERE patterns from routing.yml so the patterns are declarative data. -# Each invocation appends to /tmp/pipeline-parameters.json. +# CircleCI compatibility entrypoint for shared parameter/path collection. set -euo pipefail - -MODE="${1:?Usage: collect-params.sh }" -OUTPUT="/tmp/pipeline-parameters.json" -ROUTING="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/routing.yml" - -[ -f "${OUTPUT}" ] || echo '{}' > "${OUTPUT}" - -to_bool() { - case "${1}" in 1|true|True|TRUE) echo "true" ;; *) echo "false" ;; esac -} - -json=$(cat "${OUTPUT}") - -case "${MODE}" in - str) - while IFS='=' read -r key value; do - [[ "${key}" == c-* ]] || continue - json=$(echo "${json}" | jq --arg v "${value}" '. + {"'"${key}"'": $v}') - echo " [str] ${key} = ${value}" - done < <(env | sort) - ;; - - bool) - while IFS='=' read -r key value; do - [[ "${key}" == c-* ]] || continue - json=$(echo "${json}" | jq --argjson v "$(to_bool "${value}")" '. + {"'"${key}"'": $v}') - echo " [bool] ${key} = $(to_bool "${value}")" - done < <(env | sort) - ;; - - detect|detect_all) - [[ "${MODE}" == "detect_all" ]] && section=".change_patterns.all" || section=".change_patterns.any" - - CHANGED=$(git diff --name-only "origin/${BASE_REVISION}...HEAD" 2>/dev/null \ - || git diff --name-only HEAD~1 HEAD || true) - echo "=== Changed files ===" - echo "${CHANGED:-}" - echo "=====================" - - while IFS= read -r name; do - [[ -n "${name}" ]] || continue - pattern=$(yq -r "${section}.\"${name}\"" "${ROUTING}") - if [ -z "${CHANGED}" ]; then - result=false - elif [[ "${MODE}" == "detect_all" ]]; then - # True iff every changed file matches the pattern (i.e., no file fails to match). - if echo "${CHANGED}" | grep -qvE "${pattern}"; then - result=false - else - result=true - fi - else - # detect: true iff at least one changed file matches the pattern. - if echo "${CHANGED}" | grep -qE "${pattern}"; then - result=true - else - result=false - fi - fi - json=$(echo "${json}" | jq --argjson v "${result}" '. + {"c-'"${name}"'": $v}') - echo " [${MODE}] c-${name} = ${result} (pattern: ${pattern})" - done < <(yq -r "${section} | keys | .[]" "${ROUTING}") - ;; - - *) - echo "ERROR: Unknown mode '${MODE}'. Use str, bool, detect, or detect_all." >&2 - exit 1 - ;; -esac - -echo "${json}" > "${OUTPUT}" -echo "=== Parameters so far ===" -cat "${OUTPUT}" -echo "=========================" +export CI_BASE_REVISION="${BASE_REVISION:-develop}" +exec bash "$(dirname "${BASH_SOURCE[0]}")/../../ops/ci/collect-params.sh" "$@" diff --git a/.circleci/scripts/compute-workflow-conditions.sh b/.circleci/scripts/compute-workflow-conditions.sh index 924a6c15680..077f6fae19e 100755 --- a/.circleci/scripts/compute-workflow-conditions.sh +++ b/.circleci/scripts/compute-workflow-conditions.sh @@ -1,157 +1,13 @@ #!/usr/bin/env bash -# Workflow routing policy for the CircleCI setup pipeline. -# -# Decides WHICH continuation workflows run, based on the trigger source, branch, -# tag, schedule name, and the change-detection / dispatch params already written -# to /tmp/pipeline-parameters.json by earlier steps. The workflow lists -# themselves live in routing.yml — this script only holds the conditions. -# -# Each enabled workflow sets c-run_: true in the JSON, which maps 1:1 to -# "when: << pipeline.parameters.c-run_ >>" in a continuation config. -# -# Inputs (set by the config.yml step environment): -# BRANCH, TRIGGER_SOURCE, TAG, SCHEDULE_NAME -# -# Helpers (workflow-helpers.sh): -# run enable workflows by literal name -# run_group enable the workflows listed under routing.yml sec.key -# is_true true if c-x is true in the JSON -# param raw value of c-x from the JSON -# finalize strip intermediate params, keep c-run_* + passthrough -# -# How to add a new workflow: -# 1. Declare "c-run_: {type: boolean, default: false}" in a continuation -# config under .circleci/continue/. -# 2. Wire it in here (literal "run " for a one-off, or add it to the -# relevant routing.yml list and use run_group). +# CircleCI adapter for the shared, provider-neutral workflow routing policy. set -euo pipefail -# shellcheck disable=SC1091 # sourced helper resolved at runtime, not by shellcheck -source "$(dirname "${BASH_SOURCE[0]}")/workflow-helpers.sh" -init_json - -case "${TRIGGER_SOURCE}" in - - # Scheduled pipelines: map schedule name -> workflows (routing.yml schedules). - scheduled_pipeline) - run_group schedules "${SCHEDULE_NAME}" - ;; - - # Webhook (push events) - webhook) - # --- Tag push --- - if [[ -n "${TAG}" ]]; then - run release - - # ========================================================= - # Three mutually exclusive lifecycle stages: - # ========================================================= - - # 1. PR (feature branch push) - # Runs on every push to a feature branch. - # Path-based gating: only changed areas are tested. - # Docs-only changes skip main/release entirely. - # --------------------------------------------------------- - elif [[ "${BRANCH}" != "develop" && ! "${BRANCH}" =~ ^gh-readonly-queue/ ]]; then - if is_true only_docs_changes; then - run ci_gate_skip - run contracts_feature_tests_short - run rust_ci_gate_short - run rust_e2e_gate_skip - else - run main - run release - if is_true contracts_changed; then - run contracts_feature_tests - else - run contracts_feature_tests_short - fi - if is_true rust_changes_detected; then - run rust_ci - run rust_e2e_ci - else - run rust_ci_gate_short - run rust_e2e_gate_skip - fi - if is_true circleci_changed; then - run circleci_schedule_trigger_check - fi - fi - - # 2. Merge queue (pre-merge validation) - # Runs when GitHub merge queue picks up the PR. - # Docs-only changes emit the required gates without running test suites. - # All other changes run full contract tests and path-gated Rust tests. - # --------------------------------------------------------- - elif [[ "${BRANCH}" =~ ^gh-readonly-queue/ ]]; then - if is_true only_docs_changes; then - run ci_gate_skip - run contracts_feature_tests_short - run rust_ci_gate_short - run rust_e2e_gate_skip - else - run main - run release - run contracts_feature_tests - if is_true rust_changes_detected; then - run rust_ci - run rust_e2e_ci - else - run rust_ci_gate_short - run rust_e2e_gate_skip - fi - if is_true circleci_changed; then - run circleci_schedule_trigger_check - fi - fi - - # 3. After merge (develop push) - # Runs after the merge queue completes and pushes to develop. - # Adds expensive post-merge jobs: fault proofs, kontrol, prestate publishing. - # - # Nothing here is path-gated. Change detection diffs against - # origin/${BASE_REVISION}, and BASE_REVISION is develop, so on a develop - # push HEAD is the base and the changed-file list is always empty. Every - # is_true check would therefore be false, making the gated branch dead - # code rather than a conditional. - # --------------------------------------------------------- - elif [[ "${BRANCH}" == "develop" ]]; then - run main - run release - run publish_contract_artifacts - run develop_fault_proofs - run develop_kontrol_tests - run contracts_feature_tests - run rust_ci - run rust_e2e_ci - run kona_publish_prestates - run circleci_schedule_trigger_check - fi - ;; - - # API triggers: dispatch flags select workflows (routing.yml api_dispatch). - api) - run release - # main_dispatch only fires for genuine API dispatches, not github-event triggers. - if is_true main_dispatch && [[ "$(param github-event-type)" == "__not_set__" ]]; then - run_group api_dispatch main_dispatch - fi - # Simple dispatch flags: each enables its workflows when the flag is set. - # main_dispatch and labeled_pr have bespoke conditions, handled separately. - for flag in $(yq -r '.api_dispatch | keys | .[]' "${ROUTING}"); do - # Keep this skip-list in sync with bespoke api_dispatch conditions. - case "${flag}" in - main_dispatch | labeled_pr) continue ;; - esac - if is_true "${flag}"; then - run_group api_dispatch "${flag}" - fi - done - # GitHub "pull_request labeled" event triggers issue-close automation. - if [[ "$(param github-event-type)" == "pull_request" && "$(param github-event-action)" == "labeled" ]]; then - run_group api_dispatch labeled_pr - fi - ;; +case "${TRIGGER_SOURCE:?TRIGGER_SOURCE must be set}" in + webhook) CI_EVENT=push ;; + scheduled_pipeline) CI_EVENT=schedule ;; + api) CI_EVENT=dispatch ;; + *) echo "ERROR: unsupported CircleCI trigger '${TRIGGER_SOURCE}'" >&2; exit 1 ;; esac - -finalize +export CI_EVENT +export CI_BRANCH="${BRANCH:-}" CI_TAG="${TAG:-}" CI_SCHEDULE_NAME="${SCHEDULE_NAME:-}" +exec bash "$(dirname "${BASH_SOURCE[0]}")/../../ops/ci/compute-workflow-conditions.sh" "$@" diff --git a/.circleci/scripts/test-decision-tree.sh b/.circleci/scripts/test-decision-tree.sh index d7a8fad1eee..2c1fa83ee2b 100755 --- a/.circleci/scripts/test-decision-tree.sh +++ b/.circleci/scripts/test-decision-tree.sh @@ -1,236 +1,4 @@ #!/usr/bin/env bash -# Dry-run test for the workflow routing policy (compute-workflow-conditions.sh). -# Seeds the params JSON, sets the trigger/branch/tag/schedule environment, runs -# the routing script, then asserts the expected c-run_* flags are (or are not) -# set in the resulting JSON. -# -# Usage: -# bash .circleci/scripts/test-decision-tree.sh -# -# Requires: jq, yq (same version used in CI) +# Preserve the setup-pipeline command while exercising both provider entrypoints. set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROUTING_SCRIPT="${SCRIPT_DIR}/compute-workflow-conditions.sh" - -# Use an isolated params file so the test never writes the pipeline's real -# /tmp/pipeline-parameters.json. The routing script's finalize writes c-run_* -# flags, which the later collect-params/compute steps would otherwise inherit. -# Exported so the routing script (via workflow-helpers.sh) writes here too. -OUTPUT="$(mktemp)" -export OUTPUT -trap 'rm -f "${OUTPUT}"' EXIT - -# --- Test harness --- -PASS=0 -FAIL=0 - -run_scenario() { - local name="${1}" - local trigger="${2}" - local branch="${3}" - local tag="${4}" - local schedule="${5}" - local json_seed="${6}" - shift 6 - local expected=() - local unexpected=() - local collect_expected=true - for wf in "$@"; do - if [[ "${wf}" == "--not" ]]; then - collect_expected=false - continue - fi - if ${collect_expected}; then - expected+=("${wf}") - else - unexpected+=("${wf}") - fi - done - - # Seed the JSON and run the routing policy as the pipeline would. - echo "${json_seed}" > "${OUTPUT}" - TRIGGER_SOURCE="${trigger}" BRANCH="${branch}" TAG="${tag}" SCHEDULE_NAME="${schedule}" \ - bash "${ROUTING_SCRIPT}" >/dev/null || true - - local _json - _json=$(cat "${OUTPUT}") - - # Check expected workflows are enabled - local all_pass=true - for wf in "${expected[@]}"; do - local val - val=$(echo "${_json}" | jq -r ".\"c-run_${wf}\" // false") - if [[ "${val}" != "true" ]]; then - echo " FAIL: expected c-run_${wf}=true, got ${val}" - all_pass=false - fi - done - for wf in "${unexpected[@]}"; do - local val - val=$(echo "${_json}" | jq -r ".\"c-run_${wf}\" // false") - if [[ "${val}" != "false" ]]; then - echo " FAIL: expected c-run_${wf}=false, got ${val}" - all_pass=false - fi - done - - if ${all_pass}; then - echo "PASS: ${name}" - PASS=$((PASS + 1)) - else - echo "FAIL: ${name}" - FAIL=$((FAIL + 1)) - fi -} - -echo "=== Decision Tree Dry-Run Tests ===" -echo "" - -# --- Scenarios --- - -run_scenario \ - "Tag push → release only" \ - "webhook" "" "v1.0.0" "" \ - '{}' \ - release - -run_scenario \ - "PR (feature branch), rust changed" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": true, "c-contracts_changed": false, "c-docs_changes_detected": false}' \ - main release contracts_feature_tests_short rust_ci rust_e2e_ci - -run_scenario \ - "PR (feature branch), contracts changed" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": true, "c-docs_changes_detected": false}' \ - main release contracts_feature_tests rust_ci_gate_short rust_e2e_gate_skip - -run_scenario \ - "PR (feature branch), docs only" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-docs_changes_detected": true, "c-only_docs_changes": true}' \ - ci_gate_skip contracts_feature_tests_short rust_ci_gate_short rust_e2e_gate_skip - -run_scenario \ - "PR (feature branch), docs + rust changed" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": true, "c-contracts_changed": false, "c-docs_changes_detected": true, "c-only_docs_changes": false}' \ - main release contracts_feature_tests_short rust_ci rust_e2e_ci - -# Footgun guard: a docs PR that also touches code outside the detection regexes -# (e.g., op-node/, op-batcher/, any new top-level dir) MUST run main. Without -# the all-match check, this scenario previously hit the docs-only fast path. -run_scenario \ - "PR (feature branch), docs + undetected code (footgun guard)" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-docs_changes_detected": true, "c-only_docs_changes": false}' \ - main release contracts_feature_tests_short rust_ci_gate_short rust_e2e_gate_skip - -run_scenario \ - "PR (feature branch), nothing changed" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-circleci_changed": false, "c-docs_changes_detected": false, "c-only_docs_changes": false}' \ - main release contracts_feature_tests_short rust_ci_gate_short rust_e2e_gate_skip \ - --not circleci_schedule_trigger_check - -run_scenario \ - "PR (feature branch), CircleCI changed" \ - "webhook" "feat/my-thing" "" "" \ - '{"c-rust_changes_detected": true, "c-contracts_changed": true, "c-circleci_changed": true, "c-docs_changes_detected": false, "c-only_docs_changes": false}' \ - main release contracts_feature_tests rust_ci rust_e2e_ci circleci_schedule_trigger_check - -run_scenario \ - "Merge queue, rust changed" \ - "webhook" "gh-readonly-queue/develop/pr-123" "" "" \ - '{"c-rust_changes_detected": true, "c-contracts_changed": false, "c-docs_changes_detected": false, "c-only_docs_changes": false}' \ - main release contracts_feature_tests rust_ci rust_e2e_ci - -run_scenario \ - "Merge queue, no changes" \ - "webhook" "gh-readonly-queue/develop/pr-123" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-docs_changes_detected": false, "c-only_docs_changes": false}' \ - main release contracts_feature_tests rust_ci_gate_short rust_e2e_gate_skip - -run_scenario \ - "Merge queue, docs only" \ - "webhook" "gh-readonly-queue/develop/pr-123" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-docs_changes_detected": true, "c-only_docs_changes": true}' \ - ci_gate_skip contracts_feature_tests_short rust_ci_gate_short rust_e2e_gate_skip \ - --not main release contracts_feature_tests rust_ci rust_e2e_ci - -# Develop runs the full post-merge set unconditionally. The two scenarios below -# seed opposite change-detection results and assert an identical routing, which -# is what pins that behaviour: on a develop push the changed-file list is always -# empty (BASE_REVISION is develop, so HEAD is the base), so any path gating here -# would be dead code that never fires in production. -run_scenario \ - "After merge (develop), empty change set (production reality)" \ - "webhook" "develop" "" "" \ - '{"c-rust_changes_detected": false, "c-contracts_changed": false, "c-circleci_changed": false, "c-docs_changes_detected": false, "c-only_docs_changes": false}' \ - main release publish_contract_artifacts develop_fault_proofs develop_kontrol_tests contracts_feature_tests rust_ci rust_e2e_ci kona_publish_prestates circleci_schedule_trigger_check \ - --not rust_ci_gate_short rust_e2e_gate_skip ci_gate_skip contracts_feature_tests_short - -run_scenario \ - "After merge (develop), change detection must not alter routing" \ - "webhook" "develop" "" "" \ - '{"c-rust_changes_detected": true, "c-contracts_changed": true, "c-circleci_changed": true, "c-docs_changes_detected": true, "c-only_docs_changes": true}' \ - main release publish_contract_artifacts develop_fault_proofs develop_kontrol_tests contracts_feature_tests rust_ci rust_e2e_ci kona_publish_prestates circleci_schedule_trigger_check \ - --not rust_ci_gate_short rust_e2e_gate_skip ci_gate_skip contracts_feature_tests_short - -run_scenario \ - "Scheduled: build_four_hours" \ - "scheduled_pipeline" "" "" "build_four_hours" \ - '{}' \ - scheduled_todo_issues scheduled_cannon_full_tests - -run_scenario \ - "Scheduled: build_daily" \ - "scheduled_pipeline" "" "" "build_daily" \ - '{}' \ - scheduled_preimage_reproducibility scheduled_stale_check scheduled_heavy_fuzz_tests scheduled_daily_tests scheduled_sp1_elf_smoke circleci_schedule_trigger_check - -run_scenario \ - "Scheduled: build_weekly" \ - "scheduled_pipeline" "" "" "build_weekly" \ - '{}' \ - scheduled_rust_nightly_bump - -run_scenario \ - "API: main_dispatch (no github event)" \ - "api" "" "" "" \ - '{"c-main_dispatch": true, "c-github-event-type": "__not_set__"}' \ - release main contracts_feature_tests - -run_scenario \ - "API: rust_ci_dispatch" \ - "api" "" "" "" \ - '{"c-main_dispatch": false, "c-rust_ci_dispatch": true, "c-github-event-type": "__not_set__"}' \ - release rust_ci - -run_scenario \ - "API: rust_nightly_bump_dispatch" \ - "api" "" "" "" \ - '{"c-main_dispatch": false, "c-rust_nightly_bump_dispatch": true, "c-github-event-type": "__not_set__"}' \ - release scheduled_rust_nightly_bump - -run_scenario \ - "API: publish_contract_artifacts_dispatch" \ - "api" "" "" "" \ - '{"c-main_dispatch": false, "c-publish_contract_artifacts_dispatch": true, "c-github-event-type": "__not_set__"}' \ - release publish_contract_artifacts - -run_scenario \ - "API: github event labeled PR" \ - "api" "" "" "" \ - '{"c-main_dispatch": false, "c-github-event-type": "pull_request", "c-github-event-action": "labeled"}' \ - release close_issue - -# --- Summary --- -echo "" -echo "=== Results: ${PASS} passed, ${FAIL} failed ===" - -if [[ ${FAIL} -gt 0 ]]; then - exit 1 -fi +exec bash "$(dirname "${BASH_SOURCE[0]}")/../../ops/ci/test-decision-tree.sh" "$@" diff --git a/.circleci/scripts/test-schedule-triggers.js b/.circleci/scripts/test-schedule-triggers.js index cddec565127..4f0152aebe9 100755 --- a/.circleci/scripts/test-schedule-triggers.js +++ b/.circleci/scripts/test-schedule-triggers.js @@ -4,7 +4,7 @@ import { readdirSync } from "node:fs"; import path from "node:path"; const repoRoot = path.resolve(import.meta.dir, "../.."); -const routingPath = path.join(repoRoot, ".circleci/routing.yml"); +const routingPath = path.join(repoRoot, "ops/ci/routing.yml"); const continuationDir = path.join(repoRoot, ".circleci/continue"); const apiBase = process.env.CIRCLECI_API_BASE ?? "https://circleci.com/api/v2"; const projectSlug = diff --git a/.circleci/scripts/workflow-helpers.sh b/.circleci/scripts/workflow-helpers.sh index 3e53b552229..af4c1fc6fda 100755 --- a/.circleci/scripts/workflow-helpers.sh +++ b/.circleci/scripts/workflow-helpers.sh @@ -1,66 +1,4 @@ #!/usr/bin/env bash -# Sourceable helper functions for the workflow routing policy in -# compute-workflow-conditions.sh. Provides JSON plumbing and routing.yml -# readers so the policy script only contains the routing logic and routing.yml -# only contains the data. -# -# Usage (from compute-workflow-conditions.sh): -# source .circleci/scripts/workflow-helpers.sh -# init_json -# ...routing logic using run/run_group/param/is_true... -# finalize - -# OUTPUT is overridable so tests can point at an isolated file instead of the -# pipeline's real params file. -OUTPUT="${OUTPUT:-/tmp/pipeline-parameters.json}" -# routing.yml lives one directory up from this script (.circleci/routing.yml), -# resolved from the script location so it works regardless of the caller's cwd. -ROUTING="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/routing.yml" -_json="" - -init_json() { - _json=$(cat "${OUTPUT}") -} - -run() { - for wf in "$@"; do - _json=$(echo "${_json}" | jq '. + {"c-run_'"${wf}"'": true}') - echo " [enable] c-run_${wf}" - done -} - -# run_group
: enable every workflow listed under -# routing.yml's
.. A missing key enables nothing. -run_group() { - local section="${1}" key="${2}" - local wfs - wfs=$(yq -r ".${section}.\"${key}\"[]?" "${ROUTING}") - if [[ -n "${wfs}" ]]; then - # shellcheck disable=SC2086 # intentional word-splitting of the name list - run ${wfs} - fi -} - -param() { - echo "${_json}" | jq -r ".\"c-${1}\"" -} - -is_true() { - [[ "$(param "${1}")" == "true" ]] -} - -# Strip intermediate params, keeping only c-run_* flags and the passthrough -# params declared in routing.yml, then write the final JSON. -finalize() { - local jq_filter - jq_filter=$(yq -r '.passthrough_params[]' "${ROUTING}" | sed 's/.*/"&"/' | paste -sd',' -) - - _json=$(echo "${_json}" | jq "with_entries(select( - .key | startswith(\"c-run_\") or IN(${jq_filter}) - ))") - - echo "${_json}" > "${OUTPUT}" - echo "=== Enabled workflows ===" - echo "${_json}" | jq -r 'to_entries[] | select(.key | startswith("c-run_")) | select(.value == true) | " \(.key)"' - echo "=========================" -} +# Compatibility entrypoint for callers sourcing the former CircleCI helper. +# shellcheck disable=SC1091 # shared helper resolved relative to this wrapper +source "$(dirname "${BASH_SOURCE[0]}")/../../ops/ci/workflow-helpers.sh" diff --git a/.dockerignore b/.dockerignore index a5c202b7f25..516900701fc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,3 +20,12 @@ build/_bin tests/testdata rust/target + +# CI caches/reports and offline runtime witnesses are never build inputs. +.ci +rust/kona/data +rust/kona/state.bin.gz +rust/kona/out.bin.gz +rust/kona/meta.json +rust/kona/bin/client/testdata/*.tar.zst +rust/kona/bin/client/testdata/*.etag diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d4555578ba1..0c3e1f3f023 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -27,6 +27,8 @@ /docs/public-docs/scripts/ @ethereum-optimism/solutions # CI workflows +/.rwx/ @ethereum-optimism/cloud-security +/ops/ci/ @ethereum-optimism/cloud-security /.github/workflows/ @ethereum-optimism/cloud-security /.github/actions/ @ethereum-optimism/cloud-security diff --git a/.gitignore b/.gitignore index d959d09ab9b..a8f4c29b4a3 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,9 @@ packages/contracts-bedrock/deployments/anvil # Local AI planning docs (op-claude and similar agent tooling) .claude/plans +# CI routing metadata generated by the RWX pilot. +.ci/ + # Ignore local fuzzing results **/testdata/fuzz/ diff --git a/.rwx/acceptance.yml b/.rwx/acceptance.yml new file mode 100644 index 00000000000..8a78b79a516 --- /dev/null +++ b/.rwx/acceptance.yml @@ -0,0 +1,497 @@ +# Full acceptance variants. CircleCI retains all required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [go, contracts, kona, op-reth, prestate, sp1-executor, acceptance-tools, discovery-opn, discovery-kona] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + shard-total: "8" + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + shard-total: "8" + +# Cache-only vault, writable by develop and the temporary pilot branch. +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: go-build-tools + use: tools + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: anvil + + - key: rust-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: release + + - key: helper-tests + use: [code, go-build-tools] + if: ${{ init.cache-warm != 'true' }} + run: | + mise exec -- python3 -m unittest discover -s ops/ci -p 'test_acceptance*.py' + python3 -m unittest discover -s ops/ci -p 'test_rust_target_cache.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: go + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-go.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: contracts + use: [source, contract-tools, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: kona + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-kona.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: op-reth + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-op-reth.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: prestate + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-prestate.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: sp1-executor + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-sp1-executor.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: acceptance-tools + use: [go-build-tools, contract-tools] + run: | + bash .circleci/scripts/apt-install.sh --no-install-recommends eatmydata + mise install go:github.com/ethereum/go-ethereum/cmd/geth + printf '%s\n' "$(mise bin-paths | paste -sd: -):$PATH" >"$RWX_ENV/PATH" + timeout: 30m + + - key: discovery-opn + use: [source, go-build-tools, go.build, contracts.build] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-discovery-opn-${{ init.cache-epoch }} + + - key: verdict-opn-0 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '0' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-0-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-1 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '1' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-1-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-2 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '2' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-2-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-3 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '3' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-3-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-4 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '4' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-4-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-5 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '5' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-5-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-6 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '6' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-6-${{ init.cache-epoch }} + memory: 64gb + + - key: verdict-opn-7 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-opn.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '7' + shard-total: ${{ init.shard-total }} + cl-kind: op-node + cache-key: acceptance-runtime-opn-7-${{ init.cache-epoch }} + memory: 64gb + + - key: discovery-kona + use: [source, go-build-tools, go.build, contracts.build] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-discovery-kona-${{ init.cache-epoch }} + + - key: verdict-kona-0 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '0' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-0-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-1 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '1' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-1-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-2 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '2' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-2-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-3 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '3' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-3-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-4 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '4' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-4-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-5 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '5' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-5-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-6 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '6' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-6-${{ init.cache-epoch }} + memory: 32gb + + - key: verdict-kona-7 + use: [source, acceptance-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, sp1-executor.build, discovery-kona.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/acceptance-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '7' + shard-total: ${{ init.shard-total }} + cl-kind: kona-node + cache-key: acceptance-runtime-kona-7-${{ init.cache-epoch }} + memory: 32gb + + - key: main-gate-receipt + use: [code, tools] + after: ${{ (verdict-kona-0.succeeded || verdict-kona-0.failed || verdict-kona-0.skipped) && (verdict-kona-1.succeeded || verdict-kona-1.failed || verdict-kona-1.skipped) && (verdict-kona-2.succeeded || verdict-kona-2.failed || verdict-kona-2.skipped) && (verdict-kona-3.succeeded || verdict-kona-3.failed || verdict-kona-3.skipped) && (verdict-kona-4.succeeded || verdict-kona-4.failed || verdict-kona-4.skipped) && (verdict-kona-5.succeeded || verdict-kona-5.failed || verdict-kona-5.skipped) && (verdict-kona-6.succeeded || verdict-kona-6.failed || verdict-kona-6.skipped) && (verdict-kona-7.succeeded || verdict-kona-7.failed || verdict-kona-7.skipped) && (verdict-opn-0.succeeded || verdict-opn-0.failed || verdict-opn-0.skipped) && (verdict-opn-1.succeeded || verdict-opn-1.failed || verdict-opn-1.skipped) && (verdict-opn-2.succeeded || verdict-opn-2.failed || verdict-opn-2.skipped) && (verdict-opn-3.succeeded || verdict-opn-3.failed || verdict-opn-3.skipped) && (verdict-opn-4.succeeded || verdict-opn-4.failed || verdict-opn-4.skipped) && (verdict-opn-5.succeeded || verdict-opn-5.failed || verdict-opn-5.skipped) && (verdict-opn-6.succeeded || verdict-opn-6.failed || verdict-opn-6.skipped) && (verdict-opn-7.succeeded || verdict-opn-7.failed || verdict-opn-7.skipped) }} + if: ${{ init.cache-warm != 'true' && init.shard-total == '8' }} + run: python3 ops/ci/pr-gate.py receipt main-acceptance + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_KONA_0_SUCCEEDED: ${{ tasks.verdict-kona-0.succeeded }} + TASK_VERDICT_KONA_0_FAILED: ${{ tasks.verdict-kona-0.failed }} + TASK_VERDICT_KONA_0_SKIPPED: ${{ tasks.verdict-kona-0.skipped }} + TASK_VERDICT_KONA_1_SUCCEEDED: ${{ tasks.verdict-kona-1.succeeded }} + TASK_VERDICT_KONA_1_FAILED: ${{ tasks.verdict-kona-1.failed }} + TASK_VERDICT_KONA_1_SKIPPED: ${{ tasks.verdict-kona-1.skipped }} + TASK_VERDICT_KONA_2_SUCCEEDED: ${{ tasks.verdict-kona-2.succeeded }} + TASK_VERDICT_KONA_2_FAILED: ${{ tasks.verdict-kona-2.failed }} + TASK_VERDICT_KONA_2_SKIPPED: ${{ tasks.verdict-kona-2.skipped }} + TASK_VERDICT_KONA_3_SUCCEEDED: ${{ tasks.verdict-kona-3.succeeded }} + TASK_VERDICT_KONA_3_FAILED: ${{ tasks.verdict-kona-3.failed }} + TASK_VERDICT_KONA_3_SKIPPED: ${{ tasks.verdict-kona-3.skipped }} + TASK_VERDICT_KONA_4_SUCCEEDED: ${{ tasks.verdict-kona-4.succeeded }} + TASK_VERDICT_KONA_4_FAILED: ${{ tasks.verdict-kona-4.failed }} + TASK_VERDICT_KONA_4_SKIPPED: ${{ tasks.verdict-kona-4.skipped }} + TASK_VERDICT_KONA_5_SUCCEEDED: ${{ tasks.verdict-kona-5.succeeded }} + TASK_VERDICT_KONA_5_FAILED: ${{ tasks.verdict-kona-5.failed }} + TASK_VERDICT_KONA_5_SKIPPED: ${{ tasks.verdict-kona-5.skipped }} + TASK_VERDICT_KONA_6_SUCCEEDED: ${{ tasks.verdict-kona-6.succeeded }} + TASK_VERDICT_KONA_6_FAILED: ${{ tasks.verdict-kona-6.failed }} + TASK_VERDICT_KONA_6_SKIPPED: ${{ tasks.verdict-kona-6.skipped }} + TASK_VERDICT_KONA_7_SUCCEEDED: ${{ tasks.verdict-kona-7.succeeded }} + TASK_VERDICT_KONA_7_FAILED: ${{ tasks.verdict-kona-7.failed }} + TASK_VERDICT_KONA_7_SKIPPED: ${{ tasks.verdict-kona-7.skipped }} + TASK_VERDICT_OPN_0_SUCCEEDED: ${{ tasks.verdict-opn-0.succeeded }} + TASK_VERDICT_OPN_0_FAILED: ${{ tasks.verdict-opn-0.failed }} + TASK_VERDICT_OPN_0_SKIPPED: ${{ tasks.verdict-opn-0.skipped }} + TASK_VERDICT_OPN_1_SUCCEEDED: ${{ tasks.verdict-opn-1.succeeded }} + TASK_VERDICT_OPN_1_FAILED: ${{ tasks.verdict-opn-1.failed }} + TASK_VERDICT_OPN_1_SKIPPED: ${{ tasks.verdict-opn-1.skipped }} + TASK_VERDICT_OPN_2_SUCCEEDED: ${{ tasks.verdict-opn-2.succeeded }} + TASK_VERDICT_OPN_2_FAILED: ${{ tasks.verdict-opn-2.failed }} + TASK_VERDICT_OPN_2_SKIPPED: ${{ tasks.verdict-opn-2.skipped }} + TASK_VERDICT_OPN_3_SUCCEEDED: ${{ tasks.verdict-opn-3.succeeded }} + TASK_VERDICT_OPN_3_FAILED: ${{ tasks.verdict-opn-3.failed }} + TASK_VERDICT_OPN_3_SKIPPED: ${{ tasks.verdict-opn-3.skipped }} + TASK_VERDICT_OPN_4_SUCCEEDED: ${{ tasks.verdict-opn-4.succeeded }} + TASK_VERDICT_OPN_4_FAILED: ${{ tasks.verdict-opn-4.failed }} + TASK_VERDICT_OPN_4_SKIPPED: ${{ tasks.verdict-opn-4.skipped }} + TASK_VERDICT_OPN_5_SUCCEEDED: ${{ tasks.verdict-opn-5.succeeded }} + TASK_VERDICT_OPN_5_FAILED: ${{ tasks.verdict-opn-5.failed }} + TASK_VERDICT_OPN_5_SKIPPED: ${{ tasks.verdict-opn-5.skipped }} + TASK_VERDICT_OPN_6_SUCCEEDED: ${{ tasks.verdict-opn-6.succeeded }} + TASK_VERDICT_OPN_6_FAILED: ${{ tasks.verdict-opn-6.failed }} + TASK_VERDICT_OPN_6_SKIPPED: ${{ tasks.verdict-opn-6.skipped }} + TASK_VERDICT_OPN_7_SUCCEEDED: ${{ tasks.verdict-opn-7.succeeded }} + TASK_VERDICT_OPN_7_FAILED: ${{ tasks.verdict-opn-7.failed }} + TASK_VERDICT_OPN_7_SKIPPED: ${{ tasks.verdict-opn-7.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-acceptance diff --git a/.rwx/cannon-go.yml b/.rwx/cannon-go.yml new file mode 100644 index 00000000000..554f7d767a7 --- /dev/null +++ b/.rwx/cannon-go.yml @@ -0,0 +1,206 @@ +# Complete Cannon Go PR workload; Circle still owns its required gate. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [modules, contracts] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-modules.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-epoch: ${{ init.cache-epoch }} + + - key: contracts + use: [source, go-tools, contract-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: '' + target-cache-mode: keep + + - key: helper-tests + use: [code, go-tools, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + RWX_LIVE_CANNON_GO_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_cannon_go.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_cannon_go.py' + cache: false + timeout: 15m + outputs: + filesystem: false + + artifacts: + - key: fixtures + path: .ci/cannon-go/helper-fixtures + + - key: tests + use: [source, go-tools, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/cannon-go/build" + mkdir -p "$GOCACHE" + python3 ops/ci/cannon-go.py --provider rwx --skip-slow-tests true --fresh-tests true \ + --module-artifact "$MODULE_ARTIFACT" --contract-artifact "$CONTRACT_ARTIFACT" + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + MODULE_ARTIFACT: ${{ tasks.modules.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: cannon-go-compiler-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/cannon-go/build] + system: [] + test-results: + - path: .ci/cannon-go/run/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/cannon-go/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (tests.succeeded || tests.failed || tests.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-cannon + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_TESTS_SUCCEEDED: ${{ tasks.tests.succeeded }} + TASK_TESTS_FAILED: ${{ tasks.tests.failed }} + TASK_TESTS_SKIPPED: ${{ tasks.tests.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-cannon diff --git a/.rwx/contract-coverage.yml b/.rwx/contract-coverage.yml new file mode 100644 index 00000000000..44b52540734 --- /dev/null +++ b/.rwx/contract-coverage.yml @@ -0,0 +1,306 @@ +# Complete contract coverage passes; Circle retains its required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [compile-main, compile-CUSTOM_GAS_TOKEN, compile-OPTIMISM_PORTAL_INTEROP, compile-ZK_DISPUTE_GAME] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + # Match Circle checkout depth: absolute artifact paths affect coverage loop hits. + path: project + + - key: bootstrap-inputs + use: code + # Normalize only tool inputs; coverage source stays at Circle's project path. + run: | + mkdir -p ops/ci .circleci/scripts + cp project/mise.toml mise.toml + cp project/.circleci/scripts/apt-install.sh .circleci/scripts/apt-install.sh + for script in rwx-prepare.sh rwx-contracts-prepare.sh rwx-rust-prepare.sh op-reth-shadow.sh op-reth-report.py rust-target-cache.py; do + cp "project/ops/ci/$script" "ops/ci/$script" + done + filter: [project/mise.toml, project/ops/ci, project/.circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: project/mise.toml + - key: ci-scripts + path: project/ops/ci + - key: apt-script + path: project/.circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: | + cd project + bash ops/ci/rwx-metadata.sh + jq -er '."c-run_contracts_feature_tests" | if type == "boolean" then tostring else error("missing contracts route") end' \ + .ci/pipeline-parameters.json >"$RWX_VALUES/run-contracts" + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: project/.ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: rpc-check + use: [code, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + cd project + python3 ops/ci/contract-coverage.py preflight + printf 'true\n' >"$RWX_VALUES/ready" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + cache: false + timeout: 10m + outputs: + filesystem: + filter: + workspace: [project/.ci/contract-coverage/preflight] + system: [] + artifacts: + - key: block + path: project/.ci/contract-coverage/preflight/block.json + - key: reports + path: project/.ci/contract-coverage/preflight + + - key: go-modules + use: [code, go-tools] + # Allow an intentionally skipped preflight only for compiler-only warming. + after: ${{ rpc-check.succeeded || rpc-check.skipped }} + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + cd project + export GOMODCACHE="$PWD/.ci/go-cache/contract-coverage/modules" + mkdir -p "$GOMODCACHE" + python3 ops/ci/pr-checks.py go-modules + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: contract-coverage-modules-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [project/.ci/go-cache/contract-coverage/modules] + system: [] + artifacts: + - key: reports + path: project/.ci/pr-checks/go-modules + + - key: helper-tests + use: [code, go-tools, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + cd project + RWX_LIVE_CONTRACT_COVERAGE_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_contract_coverage.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_contract_coverage.py' + env: + RWX_COVERAGE_FIXTURE_RETAIN_DIR: .ci/coverage-fixture-evidence + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: original-failure-fixtures + path: project/.ci/coverage-fixture-evidence + + # Coverage performs its instrumented compilation inside the fresh verdict. + # This producer builds the original source prerequisite and complete signatures. + - key: compile-main + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-coverage-compile-main-${{ init.cache-epoch }} + feature: main + + - key: verdict-main + use: [code, go-tools, contract-tools, go-modules, compile-main, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-coverage/main/prepare + archive-preflight: .ci/contract-coverage/preflight + cache-key: contract-coverage-runtime-main-${{ init.cache-epoch }} + feature: main + + - key: compile-CUSTOM_GAS_TOKEN + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-coverage-compile-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + feature: CUSTOM_GAS_TOKEN + + - key: verdict-CUSTOM_GAS_TOKEN + use: [code, go-tools, contract-tools, go-modules, compile-CUSTOM_GAS_TOKEN, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-coverage/CUSTOM_GAS_TOKEN/prepare + archive-preflight: .ci/contract-coverage/preflight + cache-key: contract-coverage-runtime-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + feature: CUSTOM_GAS_TOKEN + + - key: compile-OPTIMISM_PORTAL_INTEROP + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-coverage-compile-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + feature: OPTIMISM_PORTAL_INTEROP + + - key: verdict-OPTIMISM_PORTAL_INTEROP + use: [code, go-tools, contract-tools, go-modules, compile-OPTIMISM_PORTAL_INTEROP, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-coverage/OPTIMISM_PORTAL_INTEROP/prepare + archive-preflight: .ci/contract-coverage/preflight + cache-key: contract-coverage-runtime-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + feature: OPTIMISM_PORTAL_INTEROP + + - key: compile-ZK_DISPUTE_GAME + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-coverage-compile-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + feature: ZK_DISPUTE_GAME + + - key: verdict-ZK_DISPUTE_GAME + use: [code, go-tools, contract-tools, go-modules, compile-ZK_DISPUTE_GAME, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-coverage-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-coverage/ZK_DISPUTE_GAME/prepare + archive-preflight: .ci/contract-coverage/preflight + cache-key: contract-coverage-runtime-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + feature: ZK_DISPUTE_GAME + + - key: contracts-gate-receipt + use: [code, tools] + after: ${{ (verdict-CUSTOM_GAS_TOKEN.succeeded || verdict-CUSTOM_GAS_TOKEN.failed || verdict-CUSTOM_GAS_TOKEN.skipped) && (verdict-OPTIMISM_PORTAL_INTEROP.succeeded || verdict-OPTIMISM_PORTAL_INTEROP.failed || verdict-OPTIMISM_PORTAL_INTEROP.skipped) && (verdict-ZK_DISPUTE_GAME.succeeded || verdict-ZK_DISPUTE_GAME.failed || verdict-ZK_DISPUTE_GAME.skipped) && (verdict-main.succeeded || verdict-main.failed || verdict-main.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: | + cd project + python3 ops/ci/pr-gate.py receipt contracts-coverage + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-contracts }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_CUSTOM_GAS_TOKEN_SUCCEEDED: ${{ tasks.verdict-CUSTOM_GAS_TOKEN.succeeded }} + TASK_VERDICT_CUSTOM_GAS_TOKEN_FAILED: ${{ tasks.verdict-CUSTOM_GAS_TOKEN.failed }} + TASK_VERDICT_CUSTOM_GAS_TOKEN_SKIPPED: ${{ tasks.verdict-CUSTOM_GAS_TOKEN.skipped }} + TASK_VERDICT_OPTIMISM_PORTAL_INTEROP_SUCCEEDED: ${{ tasks.verdict-OPTIMISM_PORTAL_INTEROP.succeeded }} + TASK_VERDICT_OPTIMISM_PORTAL_INTEROP_FAILED: ${{ tasks.verdict-OPTIMISM_PORTAL_INTEROP.failed }} + TASK_VERDICT_OPTIMISM_PORTAL_INTEROP_SKIPPED: ${{ tasks.verdict-OPTIMISM_PORTAL_INTEROP.skipped }} + TASK_VERDICT_ZK_DISPUTE_GAME_SUCCEEDED: ${{ tasks.verdict-ZK_DISPUTE_GAME.succeeded }} + TASK_VERDICT_ZK_DISPUTE_GAME_FAILED: ${{ tasks.verdict-ZK_DISPUTE_GAME.failed }} + TASK_VERDICT_ZK_DISPUTE_GAME_SKIPPED: ${{ tasks.verdict-ZK_DISPUTE_GAME.skipped }} + TASK_VERDICT_MAIN_SUCCEEDED: ${{ tasks.verdict-main.succeeded }} + TASK_VERDICT_MAIN_FAILED: ${{ tasks.verdict-main.failed }} + TASK_VERDICT_MAIN_SKIPPED: ${{ tasks.verdict-main.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: project/.ci/pr-gates/groups/contracts-coverage diff --git a/.rwx/contract-l2-fork.yml b/.rwx/contract-l2-fork.yml new file mode 100644 index 00000000000..6272af66ec8 --- /dev/null +++ b/.rwx/contract-l2-fork.yml @@ -0,0 +1,211 @@ +# Full OP Mainnet L2 fork suite with original NUT verification and fresh verdicts. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: compile + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + fork-block: latest + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + fork-block: latest + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: {cpus: 2, memory: 8gb} + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + - key: mise + call: mise/install 1.1.0 + with: {mise-version: "2026.2.2", install: "false"} + - key: tools + use: [code, mise] + run: bash ops/ci/rwx-prepare.sh + timeout: 30m + - key: route + use: [code, tools] + run: | + bash ops/ci/rwx-metadata.sh + jq -er '."c-run_contracts_feature_tests" | if type == "boolean" then tostring else error("missing contracts route") end' \ + .ci/pipeline-parameters.json >"$RWX_VALUES/run-contracts" + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + cache: false + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + - key: rpc-check + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + python3 ops/ci/contract-l2-fork.py preflight --fork-block "$CI_L2_FORK_BLOCK" + printf 'true\n' >"$RWX_VALUES/ready" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_L2_FORK_BLOCK: ${{ init.fork-block }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: block + path: .ci/contract-l2-fork/preflight/block.json + - key: originals + path: .ci/contract-l2-fork/preflight + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + after: ${{ rpc-check.succeeded || rpc-check.skipped }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: tools + after: ${{ rpc-check.succeeded || rpc-check.skipped }} + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: bash ops/ci/rwx-contracts-prepare.sh tools + timeout: 30m + - key: go-modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/l2-fork/modules" + mkdir -p "$GOMODCACHE" + python3 ops/ci/pr-checks.py go-modules + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: l2-fork-go-modules-${{ init.cache-epoch }} + timeout: 30m + runner: {cpus: 4, memory: 8gb} + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/l2-fork/modules] + system: [] + artifacts: + - key: originals + path: .ci/pr-checks/go-modules + - key: source + use: [code, tools] + after: ${{ rpc-check.succeeded || rpc-check.skipped }} + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + timeout: 20m + - key: helper-tests + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: python3 -m unittest discover -s ops/ci -p 'test*l2*.py' + cache: false + timeout: 10m + outputs: {filesystem: false} + - key: compile + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/l2-fork/modules" + export GOCACHE="$PWD/.ci/go-cache/l2-fork/compile" + python3 ops/ci/contract-l2-fork.py prepare + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CONTRACT_PROVIDER: rwx + tool-cache: l2-fork-compiled-ci-${{ init.cache-epoch }} + timeout: 40m + runner: {cpus: 16, memory: 32gb} + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/forge-artifacts, packages/contracts-bedrock/cache/solidity-files-cache.json, packages/contracts-bedrock/artifacts/build-info, packages/contracts-bedrock/scripts/go-ffi/go-ffi, .ci/go-cache/l2-fork/compile] + system: [/root/.svm, /home/*/.svm] + artifacts: + - key: compiled + path: .ci/contract-l2-fork/prepare + - key: verdict + use: [source, go-tools, contract-tools, go-modules, compile] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/l2-fork/modules" + export GOCACHE="$PWD/.ci/go-cache/l2-fork/runtime" + python3 ops/ci/contract-l2-fork.py run --prepared "$COMPILED" --block "$PINNED_BLOCK" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CONTRACT_PROVIDER: rwx + COMPILED: ${{ tasks.compile.artifacts.compiled }} + PINNED_BLOCK: ${{ tasks.rpc-check.artifacts.originals }}/block.json + cache: false + tool-cache: l2-fork-runtime-state-${{ init.cache-epoch }} + timeout: 60m + runner: {cpus: 16, memory: 32gb} + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/l2-fork/runtime] + system: [/root/.foundry/cache/rpc, /home/*/.foundry/cache/rpc] + test-results: + - path: .ci/contract-l2-fork/run/original.junit.xml + options: {language: Solidity, framework: Foundry} + artifacts: + - key: originals + path: .ci/contract-l2-fork/run + + # Bind every complete Contracts prerequisite to fresh engine terminal states. + - key: contracts-gate-receipt + use: [code, tools] + after: ${{ (verdict.succeeded || verdict.failed || verdict.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt contracts-l2-fork + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-contracts }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_SUCCEEDED: ${{ tasks.verdict.succeeded }} + TASK_VERDICT_FAILED: ${{ tasks.verdict.failed }} + TASK_VERDICT_SKIPPED: ${{ tasks.verdict.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/contracts-l2-fork diff --git a/.rwx/contract-upgrades.yml b/.rwx/contract-upgrades.yml new file mode 100644 index 00000000000..665b55634c0 --- /dev/null +++ b/.rwx/contract-upgrades.yml @@ -0,0 +1,360 @@ +# Complete L1 upgrade matrices; Circle retains its required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [compile-feature-main, compile-feature-CUSTOM_GAS_TOKEN, compile-feature-OPTIMISM_PORTAL_INTEROP, compile-feature-ZK_DISPUTE_GAME, compile-chain-op, compile-chain-ink, compile-chain-unichain] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: | + bash ops/ci/rwx-metadata.sh + jq -er '."c-run_contracts_feature_tests" | if type == "boolean" then tostring else error("missing contracts route") end' \ + .ci/pipeline-parameters.json >"$RWX_VALUES/run-contracts" + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: rpc-check + use: [code, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + python3 ops/ci/contract-upgrades.py preflight + printf 'true\n' >"$RWX_VALUES/ready" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + cache: false + timeout: 10m + outputs: + filesystem: + filter: + workspace: [.ci/contract-upgrades/preflight] + system: [] + artifacts: + - key: block + path: .ci/contract-upgrades/preflight/block.json + - key: reports + path: .ci/contract-upgrades/preflight + + - key: go-modules + use: [code, go-tools] + # Allow an intentionally skipped preflight only for compiler-only warming. + after: ${{ rpc-check.succeeded || rpc-check.skipped }} + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-upgrades/modules" + mkdir -p "$GOMODCACHE" + python3 ops/ci/pr-checks.py go-modules + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: contract-upgrades-modules-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/contract-upgrades/modules] + system: [] + artifacts: + - key: reports + path: .ci/pr-checks/go-modules + + - key: helper-tests + use: [code, go-tools, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + RWX_LIVE_FORGE_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_contract_upgrades.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_contract_upgrades.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + # Explicit producer/verdict pairs keep each variant's filesystem isolated. + - key: compile-feature-main + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-feature-main-${{ init.cache-epoch }} + variant: feature-main + + - key: verdict-feature-main + use: [code, go-tools, contract-tools, go-modules, compile-feature-main, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/feature-main/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-feature-main-${{ init.cache-epoch }} + variant: feature-main + + - key: compile-feature-CUSTOM_GAS_TOKEN + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-feature-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + variant: feature-CUSTOM_GAS_TOKEN + + - key: verdict-feature-CUSTOM_GAS_TOKEN + use: [code, go-tools, contract-tools, go-modules, compile-feature-CUSTOM_GAS_TOKEN, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/feature-CUSTOM_GAS_TOKEN/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-feature-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + variant: feature-CUSTOM_GAS_TOKEN + + - key: compile-feature-OPTIMISM_PORTAL_INTEROP + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-feature-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + variant: feature-OPTIMISM_PORTAL_INTEROP + + - key: verdict-feature-OPTIMISM_PORTAL_INTEROP + use: [code, go-tools, contract-tools, go-modules, compile-feature-OPTIMISM_PORTAL_INTEROP, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/feature-OPTIMISM_PORTAL_INTEROP/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-feature-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + variant: feature-OPTIMISM_PORTAL_INTEROP + + - key: compile-feature-ZK_DISPUTE_GAME + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-feature-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + variant: feature-ZK_DISPUTE_GAME + + - key: verdict-feature-ZK_DISPUTE_GAME + use: [code, go-tools, contract-tools, go-modules, compile-feature-ZK_DISPUTE_GAME, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/feature-ZK_DISPUTE_GAME/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-feature-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + variant: feature-ZK_DISPUTE_GAME + + - key: compile-chain-op + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-chain-op-${{ init.cache-epoch }} + variant: chain-op + + - key: verdict-chain-op + use: [code, go-tools, contract-tools, go-modules, compile-chain-op, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/chain-op/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-chain-op-${{ init.cache-epoch }} + variant: chain-op + + - key: compile-chain-ink + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-chain-ink-${{ init.cache-epoch }} + variant: chain-ink + + - key: verdict-chain-ink + use: [code, go-tools, contract-tools, go-modules, compile-chain-ink, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/chain-ink/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-chain-ink-${{ init.cache-epoch }} + variant: chain-ink + + - key: compile-chain-unichain + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-key: contract-upgrades-chain-unichain-${{ init.cache-epoch }} + variant: chain-unichain + + - key: verdict-chain-unichain + use: [code, go-tools, contract-tools, go-modules, compile-chain-unichain, rpc-check] + if: ${{ init.cache-warm != 'true' && tasks.rpc-check.values.ready == 'true' }} + call: ${{ run.dir }}/packages/contract-upgrades-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-upgrades/chain-unichain/prepare + pinned-block: .ci/contract-upgrades/preflight/block.json + cache-key: contract-upgrades-rpc-chain-unichain-${{ init.cache-epoch }} + variant: chain-unichain + + - key: contracts-gate-receipt + use: [code, tools] + after: ${{ (verdict-chain-ink.succeeded || verdict-chain-ink.failed || verdict-chain-ink.skipped) && (verdict-chain-op.succeeded || verdict-chain-op.failed || verdict-chain-op.skipped) && (verdict-chain-unichain.succeeded || verdict-chain-unichain.failed || verdict-chain-unichain.skipped) && (verdict-feature-CUSTOM_GAS_TOKEN.succeeded || verdict-feature-CUSTOM_GAS_TOKEN.failed || verdict-feature-CUSTOM_GAS_TOKEN.skipped) && (verdict-feature-OPTIMISM_PORTAL_INTEROP.succeeded || verdict-feature-OPTIMISM_PORTAL_INTEROP.failed || verdict-feature-OPTIMISM_PORTAL_INTEROP.skipped) && (verdict-feature-ZK_DISPUTE_GAME.succeeded || verdict-feature-ZK_DISPUTE_GAME.failed || verdict-feature-ZK_DISPUTE_GAME.skipped) && (verdict-feature-main.succeeded || verdict-feature-main.failed || verdict-feature-main.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt contracts-upgrades + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-contracts }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_CHAIN_INK_SUCCEEDED: ${{ tasks.verdict-chain-ink.succeeded }} + TASK_VERDICT_CHAIN_INK_FAILED: ${{ tasks.verdict-chain-ink.failed }} + TASK_VERDICT_CHAIN_INK_SKIPPED: ${{ tasks.verdict-chain-ink.skipped }} + TASK_VERDICT_CHAIN_OP_SUCCEEDED: ${{ tasks.verdict-chain-op.succeeded }} + TASK_VERDICT_CHAIN_OP_FAILED: ${{ tasks.verdict-chain-op.failed }} + TASK_VERDICT_CHAIN_OP_SKIPPED: ${{ tasks.verdict-chain-op.skipped }} + TASK_VERDICT_CHAIN_UNICHAIN_SUCCEEDED: ${{ tasks.verdict-chain-unichain.succeeded }} + TASK_VERDICT_CHAIN_UNICHAIN_FAILED: ${{ tasks.verdict-chain-unichain.failed }} + TASK_VERDICT_CHAIN_UNICHAIN_SKIPPED: ${{ tasks.verdict-chain-unichain.skipped }} + TASK_VERDICT_FEATURE_CUSTOM_GAS_TOKEN_SUCCEEDED: ${{ tasks.verdict-feature-CUSTOM_GAS_TOKEN.succeeded }} + TASK_VERDICT_FEATURE_CUSTOM_GAS_TOKEN_FAILED: ${{ tasks.verdict-feature-CUSTOM_GAS_TOKEN.failed }} + TASK_VERDICT_FEATURE_CUSTOM_GAS_TOKEN_SKIPPED: ${{ tasks.verdict-feature-CUSTOM_GAS_TOKEN.skipped }} + TASK_VERDICT_FEATURE_OPTIMISM_PORTAL_INTEROP_SUCCEEDED: ${{ tasks.verdict-feature-OPTIMISM_PORTAL_INTEROP.succeeded }} + TASK_VERDICT_FEATURE_OPTIMISM_PORTAL_INTEROP_FAILED: ${{ tasks.verdict-feature-OPTIMISM_PORTAL_INTEROP.failed }} + TASK_VERDICT_FEATURE_OPTIMISM_PORTAL_INTEROP_SKIPPED: ${{ tasks.verdict-feature-OPTIMISM_PORTAL_INTEROP.skipped }} + TASK_VERDICT_FEATURE_ZK_DISPUTE_GAME_SUCCEEDED: ${{ tasks.verdict-feature-ZK_DISPUTE_GAME.succeeded }} + TASK_VERDICT_FEATURE_ZK_DISPUTE_GAME_FAILED: ${{ tasks.verdict-feature-ZK_DISPUTE_GAME.failed }} + TASK_VERDICT_FEATURE_ZK_DISPUTE_GAME_SKIPPED: ${{ tasks.verdict-feature-ZK_DISPUTE_GAME.skipped }} + TASK_VERDICT_FEATURE_MAIN_SUCCEEDED: ${{ tasks.verdict-feature-main.succeeded }} + TASK_VERDICT_FEATURE_MAIN_FAILED: ${{ tasks.verdict-feature-main.failed }} + TASK_VERDICT_FEATURE_MAIN_SKIPPED: ${{ tasks.verdict-feature-main.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/contracts-upgrades diff --git a/.rwx/contracts.yml b/.rwx/contracts.yml new file mode 100644 index 00000000000..58208bc0ecd --- /dev/null +++ b/.rwx/contracts.yml @@ -0,0 +1,392 @@ +# Complete standard contracts in an optional shadow; CircleCI keeps its gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [compile-standard-main, compile-standard-CUSTOM_GAS_TOKEN, compile-standard-OPTIMISM_PORTAL_INTEROP, compile-standard-ZK_DISPUTE_GAME, compile-modified-main, compile-modified-CUSTOM_GAS_TOKEN, compile-modified-OPTIMISM_PORTAL_INTEROP, compile-modified-ZK_DISPUTE_GAME] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + build-probe: "" + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + +# Cache-only vault; no RPC or publishing credentials. +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: | + bash ops/ci/rwx-metadata.sh + jq -er '."c-run_contracts_feature_tests" | if type == "boolean" then tostring else error("missing contract routing flag") end' \ + .ci/pipeline-parameters.json >"$RWX_VALUES/run-contracts" + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: go-modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-suites/modules" + python3 ops/ci/pr-checks.py go-modules + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: contract-suites-modules-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/contract-suites/modules] + system: [] + artifacts: + - key: reports + path: .ci/pr-checks/go-modules + + # One initialized checkout supplies compiler and runtime submodule sources. + # Consumers validate the retained revisions instead of cloning them again. + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + timeout: 20m + + - key: helper-tests + use: [code, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-suites/modules" + RWX_LIVE_CONTRACT_SUITE_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_contract_suites.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_contract_suites.py' + python3 ops/ci/test_contracts_shadow.py + python3 ops/ci/test_check_changed.py + cache: false + timeout: 15m + outputs: + filesystem: false + + - key: compile-standard-main + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-standard-main-${{ init.cache-epoch }} + suite: standard + feature: main + + - key: verdict-standard-main + use: [source, go-tools, contract-tools, go-modules, compile-standard-main] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-standard-main.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/standard-main/prepare + cache-key: contract-suites-runtime-standard-main-${{ init.cache-epoch }} + suite: standard + feature: main + + - key: compile-standard-CUSTOM_GAS_TOKEN + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-standard-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + suite: standard + feature: CUSTOM_GAS_TOKEN + + - key: verdict-standard-CUSTOM_GAS_TOKEN + use: [source, go-tools, contract-tools, go-modules, compile-standard-CUSTOM_GAS_TOKEN] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-standard-CUSTOM_GAS_TOKEN.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/standard-CUSTOM_GAS_TOKEN/prepare + cache-key: contract-suites-runtime-standard-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + suite: standard + feature: CUSTOM_GAS_TOKEN + + - key: compile-standard-OPTIMISM_PORTAL_INTEROP + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-standard-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + suite: standard + feature: OPTIMISM_PORTAL_INTEROP + + - key: verdict-standard-OPTIMISM_PORTAL_INTEROP + use: [source, go-tools, contract-tools, go-modules, compile-standard-OPTIMISM_PORTAL_INTEROP] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-standard-OPTIMISM_PORTAL_INTEROP.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/standard-OPTIMISM_PORTAL_INTEROP/prepare + cache-key: contract-suites-runtime-standard-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + suite: standard + feature: OPTIMISM_PORTAL_INTEROP + + - key: compile-standard-ZK_DISPUTE_GAME + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-standard-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + suite: standard + feature: ZK_DISPUTE_GAME + + - key: verdict-standard-ZK_DISPUTE_GAME + use: [source, go-tools, contract-tools, go-modules, compile-standard-ZK_DISPUTE_GAME] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-standard-ZK_DISPUTE_GAME.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/standard-ZK_DISPUTE_GAME/prepare + cache-key: contract-suites-runtime-standard-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + suite: standard + feature: ZK_DISPUTE_GAME + + - key: compile-modified-main + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-modified-main-${{ init.cache-epoch }} + suite: modified + feature: main + + - key: verdict-modified-main + use: [source, go-tools, contract-tools, go-modules, compile-modified-main] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-modified-main.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/modified-main/prepare + cache-key: contract-suites-runtime-modified-main-${{ init.cache-epoch }} + suite: modified + feature: main + + - key: compile-modified-CUSTOM_GAS_TOKEN + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-modified-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + suite: modified + feature: CUSTOM_GAS_TOKEN + + - key: verdict-modified-CUSTOM_GAS_TOKEN + use: [source, go-tools, contract-tools, go-modules, compile-modified-CUSTOM_GAS_TOKEN] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-modified-CUSTOM_GAS_TOKEN.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/modified-CUSTOM_GAS_TOKEN/prepare + cache-key: contract-suites-runtime-modified-CUSTOM_GAS_TOKEN-${{ init.cache-epoch }} + suite: modified + feature: CUSTOM_GAS_TOKEN + + - key: compile-modified-OPTIMISM_PORTAL_INTEROP + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-modified-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + suite: modified + feature: OPTIMISM_PORTAL_INTEROP + + - key: verdict-modified-OPTIMISM_PORTAL_INTEROP + use: [source, go-tools, contract-tools, go-modules, compile-modified-OPTIMISM_PORTAL_INTEROP] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-modified-OPTIMISM_PORTAL_INTEROP.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/modified-OPTIMISM_PORTAL_INTEROP/prepare + cache-key: contract-suites-runtime-modified-OPTIMISM_PORTAL_INTEROP-${{ init.cache-epoch }} + suite: modified + feature: OPTIMISM_PORTAL_INTEROP + + - key: compile-modified-ZK_DISPUTE_GAME + use: [source, go-tools, contract-tools, go-modules] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/contracts-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + build-probe: ${{ init.build-probe }} + cache-key: contract-suites-modified-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + suite: modified + feature: ZK_DISPUTE_GAME + + - key: verdict-modified-ZK_DISPUTE_GAME + use: [source, go-tools, contract-tools, go-modules, compile-modified-ZK_DISPUTE_GAME] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' && tasks.compile-modified-ZK_DISPUTE_GAME.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/contracts-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + compiled: .ci/contract-suites/modified-ZK_DISPUTE_GAME/prepare + cache-key: contract-suites-runtime-modified-ZK_DISPUTE_GAME-${{ init.cache-epoch }} + suite: modified + feature: ZK_DISPUTE_GAME + + - key: contracts-gate-receipt + use: [code, tools] + after: ${{ (verdict-modified-CUSTOM_GAS_TOKEN.succeeded || verdict-modified-CUSTOM_GAS_TOKEN.failed || verdict-modified-CUSTOM_GAS_TOKEN.skipped) && (verdict-modified-OPTIMISM_PORTAL_INTEROP.succeeded || verdict-modified-OPTIMISM_PORTAL_INTEROP.failed || verdict-modified-OPTIMISM_PORTAL_INTEROP.skipped) && (verdict-modified-ZK_DISPUTE_GAME.succeeded || verdict-modified-ZK_DISPUTE_GAME.failed || verdict-modified-ZK_DISPUTE_GAME.skipped) && (verdict-modified-main.succeeded || verdict-modified-main.failed || verdict-modified-main.skipped) && (verdict-standard-CUSTOM_GAS_TOKEN.succeeded || verdict-standard-CUSTOM_GAS_TOKEN.failed || verdict-standard-CUSTOM_GAS_TOKEN.skipped) && (verdict-standard-OPTIMISM_PORTAL_INTEROP.succeeded || verdict-standard-OPTIMISM_PORTAL_INTEROP.failed || verdict-standard-OPTIMISM_PORTAL_INTEROP.skipped) && (verdict-standard-ZK_DISPUTE_GAME.succeeded || verdict-standard-ZK_DISPUTE_GAME.failed || verdict-standard-ZK_DISPUTE_GAME.skipped) && (verdict-standard-main.succeeded || verdict-standard-main.failed || verdict-standard-main.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt contracts-suites + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-contracts }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_MODIFIED_CUSTOM_GAS_TOKEN_SUCCEEDED: ${{ tasks.verdict-modified-CUSTOM_GAS_TOKEN.succeeded }} + TASK_VERDICT_MODIFIED_CUSTOM_GAS_TOKEN_FAILED: ${{ tasks.verdict-modified-CUSTOM_GAS_TOKEN.failed }} + TASK_VERDICT_MODIFIED_CUSTOM_GAS_TOKEN_SKIPPED: ${{ tasks.verdict-modified-CUSTOM_GAS_TOKEN.skipped }} + TASK_VERDICT_MODIFIED_OPTIMISM_PORTAL_INTEROP_SUCCEEDED: ${{ tasks.verdict-modified-OPTIMISM_PORTAL_INTEROP.succeeded }} + TASK_VERDICT_MODIFIED_OPTIMISM_PORTAL_INTEROP_FAILED: ${{ tasks.verdict-modified-OPTIMISM_PORTAL_INTEROP.failed }} + TASK_VERDICT_MODIFIED_OPTIMISM_PORTAL_INTEROP_SKIPPED: ${{ tasks.verdict-modified-OPTIMISM_PORTAL_INTEROP.skipped }} + TASK_VERDICT_MODIFIED_ZK_DISPUTE_GAME_SUCCEEDED: ${{ tasks.verdict-modified-ZK_DISPUTE_GAME.succeeded }} + TASK_VERDICT_MODIFIED_ZK_DISPUTE_GAME_FAILED: ${{ tasks.verdict-modified-ZK_DISPUTE_GAME.failed }} + TASK_VERDICT_MODIFIED_ZK_DISPUTE_GAME_SKIPPED: ${{ tasks.verdict-modified-ZK_DISPUTE_GAME.skipped }} + TASK_VERDICT_MODIFIED_MAIN_SUCCEEDED: ${{ tasks.verdict-modified-main.succeeded }} + TASK_VERDICT_MODIFIED_MAIN_FAILED: ${{ tasks.verdict-modified-main.failed }} + TASK_VERDICT_MODIFIED_MAIN_SKIPPED: ${{ tasks.verdict-modified-main.skipped }} + TASK_VERDICT_STANDARD_CUSTOM_GAS_TOKEN_SUCCEEDED: ${{ tasks.verdict-standard-CUSTOM_GAS_TOKEN.succeeded }} + TASK_VERDICT_STANDARD_CUSTOM_GAS_TOKEN_FAILED: ${{ tasks.verdict-standard-CUSTOM_GAS_TOKEN.failed }} + TASK_VERDICT_STANDARD_CUSTOM_GAS_TOKEN_SKIPPED: ${{ tasks.verdict-standard-CUSTOM_GAS_TOKEN.skipped }} + TASK_VERDICT_STANDARD_OPTIMISM_PORTAL_INTEROP_SUCCEEDED: ${{ tasks.verdict-standard-OPTIMISM_PORTAL_INTEROP.succeeded }} + TASK_VERDICT_STANDARD_OPTIMISM_PORTAL_INTEROP_FAILED: ${{ tasks.verdict-standard-OPTIMISM_PORTAL_INTEROP.failed }} + TASK_VERDICT_STANDARD_OPTIMISM_PORTAL_INTEROP_SKIPPED: ${{ tasks.verdict-standard-OPTIMISM_PORTAL_INTEROP.skipped }} + TASK_VERDICT_STANDARD_ZK_DISPUTE_GAME_SUCCEEDED: ${{ tasks.verdict-standard-ZK_DISPUTE_GAME.succeeded }} + TASK_VERDICT_STANDARD_ZK_DISPUTE_GAME_FAILED: ${{ tasks.verdict-standard-ZK_DISPUTE_GAME.failed }} + TASK_VERDICT_STANDARD_ZK_DISPUTE_GAME_SKIPPED: ${{ tasks.verdict-standard-ZK_DISPUTE_GAME.skipped }} + TASK_VERDICT_STANDARD_MAIN_SUCCEEDED: ${{ tasks.verdict-standard-main.succeeded }} + TASK_VERDICT_STANDARD_MAIN_FAILED: ${{ tasks.verdict-standard-main.failed }} + TASK_VERDICT_STANDARD_MAIN_SKIPPED: ${{ tasks.verdict-standard-main.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/contracts-suites diff --git a/.rwx/fetcher-artifacts.yml b/.rwx/fetcher-artifacts.yml new file mode 100644 index 00000000000..5db997e5369 --- /dev/null +++ b/.rwx/fetcher-artifacts.yml @@ -0,0 +1,174 @@ +# Fresh fetcher artifact comparison; Circle still owns its required gate. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [tools, fetcher-solc] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: fetcher-solc + use: contract-tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + if ! svm which 0.8.30 >/dev/null 2>&1; then + for attempt in 1 2 3 4 5; do + if svm install 0.8.30; then break; fi + if [ "$attempt" = 5 ]; then exit 1; fi + sleep "$((2 ** attempt))" + done + fi + timeout: 15m + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: helper-tests + use: [code, fetcher-solc] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: RWX_LIVE_FETCHER_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_fetcher_artifacts.py' + cache: false + timeout: 15m + outputs: + filesystem: false + artifacts: + - key: fixtures + path: .ci/fetcher-artifacts/helper-fixtures + + - key: check + use: [source, fetcher-solc] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/fetcher-artifacts.py --provider rwx + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: false + artifacts: + - key: reports + path: .ci/fetcher-artifacts/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (check.succeeded || check.failed || check.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-fetcher + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CHECK_SUCCEEDED: ${{ tasks.check.succeeded }} + TASK_CHECK_FAILED: ${{ tasks.check.failed }} + TASK_CHECK_SKIPPED: ${{ tasks.check.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-fetcher diff --git a/.rwx/flaky-report.yml b/.rwx/flaky-report.yml new file mode 100644 index 00000000000..c5d540ea1e5 --- /dev/null +++ b/.rwx/flaky-report.yml @@ -0,0 +1,91 @@ +# Execute the original reporting workload against the real public Insights API. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: tools + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + github: + push: + if: ${{ event.git.branch == 'codex/rwx-ci-pilot' || event.git.branch == 'develop' }} + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: ${{ event.git.tag }} + cache-warm: "false" + status-checks: + name: optimism-flaky-report-shadow + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: {cpus: 2, memory: 8gb} + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + - key: mise + call: mise/install 1.1.0 + with: {mise-version: "2026.2.2", install: "false"} + - key: tools + use: [code, mise] + run: bash ops/ci/rwx-prepare.sh + timeout: 30m + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + - key: helper-tests + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 -m unittest discover -s ops/ci -p 'test*flaky_report.py' + cache: false + timeout: 10m + outputs: {filesystem: false} + - key: report + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/flaky-report.py run .ci/flaky-report/run + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + cache: false + timeout: 20m + outputs: + filesystem: false + artifacts: + - key: originals + path: .ci/flaky-report/run diff --git a/.rwx/go-rollup.yml b/.rwx/go-rollup.yml new file mode 100644 index 00000000000..791bea433ee --- /dev/null +++ b/.rwx/go-rollup.yml @@ -0,0 +1,166 @@ +# CLI-only rollup regression mode; the full go-tests shadow owns native routing. +on: + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + +# Cache-only vault, writable by develop and the temporary pilot branch. +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: go-build-tools + use: tools + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: go-runtime + use: tools + run: bash ops/ci/rwx-prepare.sh go-runtime + timeout: 15m + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: shard-tests + use: [code, go-build-tools] + run: | + mise exec -- python ops/ci/test_go_package_shards.py + mise exec -- python ops/ci/test_go_compiled_tests.py + cache: false + timeout: 10m + outputs: + filesystem: false + + # Full source and Git metadata are compiler inputs. Native tool caches restore + # downloaded modules and compiled Go objects only on a content-cache miss. + - key: go-build + use: [code, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: mise exec -- bash ops/ci/go-rollup-tests.sh build + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_SHARD_TOTAL: "4" + BUILD_PROBE: ${{ init.build-probe }} + tool-cache: go-rollup-build-${{ init.cache-epoch }} + runner: + cpus: 16 + memory: 32gb + timeout: 40m + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rollup, .ci/go-rollup/build] + system: [] + artifacts: + - key: compilation + path: .ci/go-rollup/build + + - key: go-rollup + use: go-runtime + if: ${{ tasks.route.values.run-main == 'true' }} + parallel: 4 + run: | + mkdir -p .ci/go-rollup/build + cp -a "$GO_BUILD_ARTIFACT/." .ci/go-rollup/build/ + tar -xzf .ci/go-rollup/build/source.tar.gz + cp .ci/go-rollup/build/manifest.json .ci/go-rollup/manifest.json + export GOMODCACHE="$PWD/.ci/go-runtime/modules" + export GOCACHE="$PWD/.ci/go-runtime/build" + mkdir -p "$GOMODCACHE" "$GOCACHE" + tar -xzf .ci/go-rollup/build/runtime-modules.tar.gz -C "$GOMODCACHE" + mise exec -- bash ops/ci/go-rollup-tests.sh + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + GO_BUILD_ARTIFACT: ${{ tasks.go-build.artifacts.compilation }} + RWX_COMPILED_GO: "true" + CI_SHARD_INDEX: ${{ parallel.index }} + CI_SHARD_TOTAL: ${{ parallel.total }} + TEST_TIMEOUT: 40m + cache: false + timeout: 50m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: false + test-results: + - path: tmp/testlogs/log.json + options: + language: Go + framework: go test + artifacts: + - key: junit + path: tmp/test-results + - key: go-json + path: tmp/testlogs/log.json + - key: test-logs + path: tmp/testlogs diff --git a/.rwx/go-tests.yml b/.rwx/go-tests.yml new file mode 100644 index 00000000000..03859919f14 --- /dev/null +++ b/.rwx/go-tests.yml @@ -0,0 +1,567 @@ +# Full aggregate Go workload. CircleCI retains all required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [go, contracts, kona, op-reth, prestate, compile-0, compile-1, compile-2, compile-3, compile-4, compile-5, compile-6, compile-7, compile-8, compile-9, compile-10, compile-11] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + shard-total: "12" + test-parallel: "8" + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + shard-total: "12" + test-parallel: "8" + +# Cache-only vault, writable by develop and the temporary pilot branch. +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: go-build-tools + use: tools + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: anvil + + - key: rust-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: release + + - key: helper-tests + use: [code, go-build-tools] + if: ${{ init.cache-warm != 'true' }} + run: mise exec -- python3 -m unittest discover -s ops/ci -p 'test_go*.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + # This is a runtime preflight: only it and fresh verdicts receive RPC inputs. + - key: rpc-preflight + use: tools + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 "$CI_SCRIPTS/go-rpc-preflight.py" + filter: + ${{ tasks.bootstrap-inputs.artifacts.ci-scripts }}: [go-rpc-preflight.py] + env: + CI_SCRIPTS: ${{ tasks.bootstrap-inputs.artifacts.ci-scripts }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + OP_CI_SEPOLIA_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_SEPOLIA_L1_ARCHIVE_RPC_URL }} + cache: false + outputs: + filesystem: false + + - key: source + use: [code, tools] + after: ${{ rpc-preflight.succeeded || rpc-preflight.skipped }} + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: go + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-go.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: contracts + use: [source, contract-tools, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: kona + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-kona.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: op-reth + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-op-reth.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: prestate + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-prestate.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: discovery + use: [source, go-build-tools, go.build, contracts.build] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts "$CONTRACT_ARTIFACT" + mise exec -- bash ops/ci/go-full-tests.sh discover + env: + CI_BRANCH: ${{ init.branch }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_SHARD_TOTAL: ${{ init.shard-total }} + PARALLEL: ${{ init.test-parallel }} + GO_ARTIFACT: .ci/go-tests/dependencies/go + CONTRACT_ARTIFACT: .ci/go-tests/dependencies/contracts + outputs: + filesystem: + filter: + workspace: [.ci/go-tests/manifest.json, .ci/go-tests/go-list.json, .ci/go-tests/all-packages.txt, .ci/go-cache/full/modules, cannon/bin, cannon/multicannon/embeds, cannon/testdata/bin, op-core/superchain/superchain-configs.zip, packages/contracts-bedrock/cache, packages/contracts-bedrock/artifacts, packages/contracts-bedrock/forge-artifacts, op-deployer/pkg/deployer/artifacts/forge-artifacts] + system: [] + artifacts: + - key: discovery + path: .ci/go-tests + filter: ['!.ci/go-cache/full/dependencies'] + + - key: compile-0 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '0' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-0-${{ init.cache-epoch }} + + - key: verdict-0 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-0.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '0' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-0-${{ init.cache-epoch }} + + - key: compile-1 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '1' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-1-${{ init.cache-epoch }} + + - key: verdict-1 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-1.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '1' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-1-${{ init.cache-epoch }} + + - key: compile-2 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '2' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-2-${{ init.cache-epoch }} + + - key: verdict-2 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-2.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '2' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-2-${{ init.cache-epoch }} + + - key: compile-3 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '3' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-3-${{ init.cache-epoch }} + + - key: verdict-3 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-3.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '3' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-3-${{ init.cache-epoch }} + + - key: compile-4 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '4' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-4-${{ init.cache-epoch }} + + - key: verdict-4 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-4.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '4' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-4-${{ init.cache-epoch }} + + - key: compile-5 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '5' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-5-${{ init.cache-epoch }} + + - key: verdict-5 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-5.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '5' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-5-${{ init.cache-epoch }} + + - key: compile-6 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '6' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-6-${{ init.cache-epoch }} + + - key: verdict-6 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-6.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '6' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-6-${{ init.cache-epoch }} + + - key: compile-7 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '7' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-7-${{ init.cache-epoch }} + + - key: verdict-7 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-7.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '7' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-7-${{ init.cache-epoch }} + + - key: compile-8 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '8' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-8-${{ init.cache-epoch }} + + - key: verdict-8 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-8.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '8' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-8-${{ init.cache-epoch }} + + - key: compile-9 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '9' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-9-${{ init.cache-epoch }} + + - key: verdict-9 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-9.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '9' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-9-${{ init.cache-epoch }} + + - key: compile-10 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '10' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-10-${{ init.cache-epoch }} + + - key: verdict-10 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-10.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '10' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-10-${{ init.cache-epoch }} + + - key: compile-11 + use: [source, go-build-tools, discovery] + if: ${{ (init.cache-warm == 'true' || tasks.route.values.run-main == 'true') }} + call: ${{ run.dir }}/packages/go-test-compile.yml + with: + commit-sha: ${{ init.commit-sha }} + shard-index: '11' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + build-probe: ${{ init.build-probe }} + cache-key: go-full-compile-11-${{ init.cache-epoch }} + + - key: verdict-11 + use: [source, go-build-tools, contract-tools, go.build, contracts.build, kona.build, op-reth.build, prestate.build, discovery, compile-11.build] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-test-verdict.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + shard-index: '11' + shard-total: ${{ init.shard-total }} + test-parallel: ${{ init.test-parallel }} + cache-key: go-full-runtime-11-${{ init.cache-epoch }} + + - key: main-gate-receipt + use: [code, tools] + after: ${{ (verdict-0.succeeded || verdict-0.failed || verdict-0.skipped) && (verdict-1.succeeded || verdict-1.failed || verdict-1.skipped) && (verdict-10.succeeded || verdict-10.failed || verdict-10.skipped) && (verdict-11.succeeded || verdict-11.failed || verdict-11.skipped) && (verdict-2.succeeded || verdict-2.failed || verdict-2.skipped) && (verdict-3.succeeded || verdict-3.failed || verdict-3.skipped) && (verdict-4.succeeded || verdict-4.failed || verdict-4.skipped) && (verdict-5.succeeded || verdict-5.failed || verdict-5.skipped) && (verdict-6.succeeded || verdict-6.failed || verdict-6.skipped) && (verdict-7.succeeded || verdict-7.failed || verdict-7.skipped) && (verdict-8.succeeded || verdict-8.failed || verdict-8.skipped) && (verdict-9.succeeded || verdict-9.failed || verdict-9.skipped) }} + if: ${{ init.cache-warm != 'true' && init.shard-total == '12' }} + run: python3 ops/ci/pr-gate.py receipt main-go + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERDICT_0_SUCCEEDED: ${{ tasks.verdict-0.succeeded }} + TASK_VERDICT_0_FAILED: ${{ tasks.verdict-0.failed }} + TASK_VERDICT_0_SKIPPED: ${{ tasks.verdict-0.skipped }} + TASK_VERDICT_1_SUCCEEDED: ${{ tasks.verdict-1.succeeded }} + TASK_VERDICT_1_FAILED: ${{ tasks.verdict-1.failed }} + TASK_VERDICT_1_SKIPPED: ${{ tasks.verdict-1.skipped }} + TASK_VERDICT_10_SUCCEEDED: ${{ tasks.verdict-10.succeeded }} + TASK_VERDICT_10_FAILED: ${{ tasks.verdict-10.failed }} + TASK_VERDICT_10_SKIPPED: ${{ tasks.verdict-10.skipped }} + TASK_VERDICT_11_SUCCEEDED: ${{ tasks.verdict-11.succeeded }} + TASK_VERDICT_11_FAILED: ${{ tasks.verdict-11.failed }} + TASK_VERDICT_11_SKIPPED: ${{ tasks.verdict-11.skipped }} + TASK_VERDICT_2_SUCCEEDED: ${{ tasks.verdict-2.succeeded }} + TASK_VERDICT_2_FAILED: ${{ tasks.verdict-2.failed }} + TASK_VERDICT_2_SKIPPED: ${{ tasks.verdict-2.skipped }} + TASK_VERDICT_3_SUCCEEDED: ${{ tasks.verdict-3.succeeded }} + TASK_VERDICT_3_FAILED: ${{ tasks.verdict-3.failed }} + TASK_VERDICT_3_SKIPPED: ${{ tasks.verdict-3.skipped }} + TASK_VERDICT_4_SUCCEEDED: ${{ tasks.verdict-4.succeeded }} + TASK_VERDICT_4_FAILED: ${{ tasks.verdict-4.failed }} + TASK_VERDICT_4_SKIPPED: ${{ tasks.verdict-4.skipped }} + TASK_VERDICT_5_SUCCEEDED: ${{ tasks.verdict-5.succeeded }} + TASK_VERDICT_5_FAILED: ${{ tasks.verdict-5.failed }} + TASK_VERDICT_5_SKIPPED: ${{ tasks.verdict-5.skipped }} + TASK_VERDICT_6_SUCCEEDED: ${{ tasks.verdict-6.succeeded }} + TASK_VERDICT_6_FAILED: ${{ tasks.verdict-6.failed }} + TASK_VERDICT_6_SKIPPED: ${{ tasks.verdict-6.skipped }} + TASK_VERDICT_7_SUCCEEDED: ${{ tasks.verdict-7.succeeded }} + TASK_VERDICT_7_FAILED: ${{ tasks.verdict-7.failed }} + TASK_VERDICT_7_SKIPPED: ${{ tasks.verdict-7.skipped }} + TASK_VERDICT_8_SUCCEEDED: ${{ tasks.verdict-8.succeeded }} + TASK_VERDICT_8_FAILED: ${{ tasks.verdict-8.failed }} + TASK_VERDICT_8_SKIPPED: ${{ tasks.verdict-8.skipped }} + TASK_VERDICT_9_SUCCEEDED: ${{ tasks.verdict-9.succeeded }} + TASK_VERDICT_9_FAILED: ${{ tasks.verdict-9.failed }} + TASK_VERDICT_9_SKIPPED: ${{ tasks.verdict-9.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-go diff --git a/.rwx/kontrol-build.yml b/.rwx/kontrol-build.yml new file mode 100644 index 00000000000..373a344174b --- /dev/null +++ b/.rwx/kontrol-build.yml @@ -0,0 +1,209 @@ +# Full Kontrol summary generation and proof build; Circle retains required ownership. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [contracts, image] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + timeout: 20m + + - key: compiler-set + use: contract-tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + if ! svm which 0.8.30 >/dev/null 2>&1; then + for attempt in 1 2 3 4 5; do + if svm install 0.8.30; then break; fi + if [ "$attempt" = 5 ]; then exit 1; fi + sleep "$((2 ** attempt))" + done + fi + timeout: 15m + + - key: image + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + filter: [mise.toml, ops/ci/kontrol-image.py, ops/ci/kontrol-image.json] + run: python3 ops/ci/kontrol-image.py prepare + docker: preserve-data + timeout: 30m + outputs: + filesystem: + filter: + workspace: [.ci/kontrol-build/image] + system: [] + artifacts: + - key: reports + path: .ci/kontrol-build/image + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: contracts + use: [source, go-tools, compiler-set] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/kontrol-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + + - key: helper-tests + use: [source, go-tools, compiler-set, image] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: RWX_LIVE_KONTROL_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_kontrol_build.py' + docker: true + cache: false + timeout: 20m + outputs: + filesystem: false + artifacts: + - key: fixtures + path: .ci/kontrol-build/helper-fixtures + + - key: check + use: [source, go-tools, compiler-set, image] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/kontrol-build.py --provider rwx --contract-artifact "$CONTRACT_ARTIFACT" + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + docker: true + cache: false + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: false + artifacts: + - key: reports + path: .ci/kontrol-build/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (check.succeeded || check.failed || check.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-kontrol + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CHECK_SUCCEEDED: ${{ tasks.check.succeeded }} + TASK_CHECK_FAILED: ${{ tasks.check.failed }} + TASK_CHECK_SKIPPED: ${{ tasks.check.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-kontrol diff --git a/.rwx/local-package-fixture.yml b/.rwx/local-package-fixture.yml new file mode 100644 index 00000000000..9d9ec298627 --- /dev/null +++ b/.rwx/local-package-fixture.yml @@ -0,0 +1,45 @@ +# CLI-only scoping and failure proof; does not publish a PR status or run suites. +on: + cli: + init: + intentional-failure: "false" + cache-warm: "false" +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 +tasks: + - key: compile + call: ${{ run.dir }}/packages/fixture-compile.yml + + - key: verdict + use: compile.build + if: ${{ init.cache-warm != 'true' && tasks.compile.tasks.build.values.eligible == 'true' }} + call: ${{ run.dir }}/packages/fixture-verdict.yml + with: + binaries: compiled + intentional-failure: ${{ init.intentional-failure }} + + - key: receipt + after: ${{ verdict.succeeded || verdict.failed || verdict.skipped }} + run: | + if [[ "$CACHE_WARM" == true ]]; then + test "$SKIPPED" = true + elif [[ "$INTENTIONAL_FAILURE" == true ]]; then + test "$FAILED" = true + else + test "$SUCCEEDED" = true + fi + env: + CACHE_WARM: ${{ init.cache-warm }} + INTENTIONAL_FAILURE: ${{ init.intentional-failure }} + SUCCEEDED: ${{ tasks.verdict.succeeded }} + FAILED: ${{ tasks.verdict.failed }} + SKIPPED: ${{ tasks.verdict.skipped }} + cache: false + - key: report + if: ${{ tasks.verdict.succeeded }} + run: test "$(cat "$REPORTS/exit-code")" = 0 + env: + REPORTS: ${{ tasks.verdict.tasks.run.artifacts.reports }} + cache: false diff --git a/.rwx/nut-prefork.yml b/.rwx/nut-prefork.yml new file mode 100644 index 00000000000..b91fc7c0f31 --- /dev/null +++ b/.rwx/nut-prefork.yml @@ -0,0 +1,220 @@ +# Complete NUT pre-fork state regeneration; Circle still owns its required gate. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [modules, contracts, superchain] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: compiler + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-modules.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-epoch: ${{ init.cache-epoch }} + + - key: contracts + use: [source, go-tools, contract-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: '' + target-cache-mode: keep + + - key: superchain + use: [source, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/main-checks.py --prepare-superchain + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + timeout: 15m + outputs: + filesystem: + filter: + workspace: [op-core/superchain/superchain-configs.zip] + system: [] + artifacts: + - key: reports + path: .ci/main-checks/prep-superchain + + - key: helper-tests + use: [code, go-tools, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + RWX_LIVE_NUT_PREFORK_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_nut_prefork.py' + cache: false + timeout: 15m + outputs: + filesystem: false + + artifacts: + - key: fixtures + path: .ci/nut-prefork/helper-fixtures + + - key: tests + use: [source, go-tools, contract-tools, superchain] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/nut-prefork/build" + mkdir -p "$GOCACHE" + python3 ops/ci/nut-prefork.py --provider rwx \ + --module-artifact "$MODULE_ARTIFACT" --contract-artifact "$CONTRACT_ARTIFACT" \ + --bundle-artifact "$BUNDLE_ARTIFACT" + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + MODULE_ARTIFACT: ${{ tasks.modules.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + BUNDLE_ARTIFACT: ${{ tasks.superchain.artifacts.reports }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: nut-prefork-compiler-${{ init.cache-epoch }} + timeout: 45m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/nut-prefork/build] + system: [] + test-results: + - path: .ci/nut-prefork/run/native.junit.xml + artifacts: + - key: reports + path: .ci/nut-prefork/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (tests.succeeded || tests.failed || tests.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-nut-prefork + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_TESTS_SUCCEEDED: ${{ tasks.tests.succeeded }} + TASK_TESTS_FAILED: ${{ tasks.tests.failed }} + TASK_TESTS_SKIPPED: ${{ tasks.tests.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-nut-prefork diff --git a/.rwx/nut-provenance.yml b/.rwx/nut-provenance.yml new file mode 100644 index 00000000000..43a70ad2b23 --- /dev/null +++ b/.rwx/nut-provenance.yml @@ -0,0 +1,180 @@ +# Full recorded-source NUT provenance regeneration; Circle still owns its required gate. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [modules, historical-tools] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-modules.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-epoch: ${{ init.cache-epoch }} + + - key: historical-tools + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/nut-tools.yml + with: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + cache-epoch: ${{ init.cache-epoch }} + + - key: helper-tests + use: [code, go-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + python3 -m unittest discover -s ops/ci -p 'test_nut_provenance.py' -v + go test -count=1 ./ops/scripts/nut-provenance-verify + cache: false + timeout: 15m + outputs: + filesystem: false + + - key: verify + use: [code, go-tools, historical-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/nut-provenance/build" + mkdir -p "$GOCACHE" + python3 ops/ci/nut-provenance.py --provider rwx --full \ + --module-artifact "$MODULE_ARTIFACT" --tool-artifact "$TOOL_ARTIFACT" + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + MODULE_ARTIFACT: ${{ tasks.modules.tasks.build.artifacts.dependency }} + TOOL_ARTIFACT: ${{ tasks.historical-tools.tasks.prepare.artifacts.tools }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: nut-provenance-go-compiler-${{ init.cache-epoch }} + timeout: 45m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/nut-provenance/build] + system: [] + artifacts: + - key: reports + path: .ci/nut-provenance/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (verify.succeeded || verify.failed || verify.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-nut-provenance + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_VERIFY_SUCCEEDED: ${{ tasks.verify.succeeded }} + TASK_VERIFY_FAILED: ${{ tasks.verify.failed }} + TASK_VERIFY_SKIPPED: ${{ tasks.verify.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-nut-provenance diff --git a/.rwx/op-reth.yml b/.rwx/op-reth.yml new file mode 100644 index 00000000000..393107dbb77 --- /dev/null +++ b/.rwx/op-reth.yml @@ -0,0 +1,413 @@ +# Four complete op-reth workloads. CircleCI retains the required checks. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [release-build, integration-build, codec-base-build, codec-head-build, snapshot-build] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + codec-base-sha: ${{ event.git.sha }} + cache-warm: "true" + # Automatic pushes are coordinated by pr-gates.yml; CLI and warming remain. + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + codec-base-sha: "" + cache-warm: "false" + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +# Cache-only vault: no publishing credentials. During the pilot, only develop +# and codex/rwx-ci-pilot may write. Remove the temporary pilot grant at promotion. +tool-cache: + vault: optimism-op-reth-shadow + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: helper-tests + use: [code, tools] + run: | + python3 ops/ci/test_op_reth_shadow.py + python3 -m unittest discover -s ops/ci -p 'test_rust_target_cache.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: rust-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' || tasks.route.values.run-rust-ci == 'true' }} + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: release + + - key: rust-dependencies + use: [code, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: | + export CARGO_HOME="$PWD/.ci/rust-cache/cargo" + cd rust + attempt=0 + until cargo fetch --locked; do + attempt=$((attempt + 1)) + if [[ "$attempt" -ge 5 ]]; then exit 1; fi + sleep "$((2 ** attempt))" + done + filter: [rust, mise.toml] + tool-cache: op-reth-dependencies-${{ init.cache-epoch }} + timeout: 30m + outputs: + filesystem: + filter: + workspace: [.ci/rust-cache/cargo] + system: [] + + # Keep full source, Git metadata and the pinned submodule as build inputs. + - key: head-source + use: [code, rust-dependencies] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh source + timeout: 15m + + - key: codec-base-revision + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: | + revision="$CODEC_BASE_SHA" + if [[ -z "$revision" ]]; then + revision="$(git ls-remote --exit-code origin refs/heads/develop | cut -f1)" + fi + [[ "$revision" =~ ^[0-9a-f]{40}$ ]] || { echo 'Expected an immutable develop SHA.' >&2; exit 1; } + mkdir -p .ci + printf '%s\n' "$revision" >"$RWX_VALUES/sha" + printf '%s\n' "$revision" >.ci/op-reth-codec-base-sha.txt + env: + CODEC_BASE_SHA: ${{ init.codec-base-sha }} + cache: false + timeout: 5m + outputs: + filesystem: false + artifacts: + - key: codec-base-revision + path: .ci/op-reth-codec-base-sha.txt + + - key: codec-base-code + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ tasks.codec-base-revision.values.sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: codec-base-source + # Baseline checkout wins over the PR manifest tree in the dependency layer. + use: [rust-dependencies, codec-base-code] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh source + timeout: 15m + + # Each producer has its own mutable cache, avoiding concurrent writers. RWX + # content hits skip compilation; misses restore Cargo targets and local + # sccache entries, then Cargo checks source, flags, profiles and lockfiles. + # BUILD_PROBE forces a content miss for measurements without disabling the + # tool cache. A new CACHE_EPOCH gives genuinely cold build/compile caches. + - key: release-build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/go-op-reth.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: release + # Artifact references create dependencies without inheriting Cargo/sccache + # filesystem layers. Input filters affect cache keys, not layer downloads. + use: [code, rust-tools] + if: ${{ tasks.route.values.run-main == 'true' }} + run: | + mkdir -p .ci/op-reth/release-build + cp -a "$RELEASE_ARTIFACT/." .ci/op-reth/release-build/ + bash /usr/local/lib/optimism-ci/op-reth-shadow.sh release + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + RELEASE_ARTIFACT: ${{ tasks.release-build.tasks.build.artifacts.release-binaries }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: release-verification + path: .ci/op-reth/release + + - key: integration-build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh integration-build + env: &build-env + CI_COMMIT_SHA: ${{ init.commit-sha }} + CACHE_EPOCH: ${{ init.cache-epoch }} + BUILD_PROBE: ${{ init.build-probe }} + TARGET_CACHE_MODE: ${{ init.target-cache-mode }} + tool-cache: op-reth-integration-${{ init.cache-epoch }} + timeout: 60m + runner: &build-runner + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [rust/target, rust/op-reth/crates/chainspec/res/superchain-configs.tar, .ci/rust-cache, .ci/op-reth/integration-build] + system: [] + artifacts: + - key: integration-build-report + path: .ci/op-reth/integration-build + + - key: integration + use: [code, rust-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: | + mkdir -p .ci/op-reth/integration-build + cp -a "$INTEGRATION_ARTIFACT/." .ci/op-reth/integration-build/ + bash /usr/local/lib/optimism-ci/op-reth-shadow.sh integration + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + INTEGRATION_ARTIFACT: ${{ tasks.integration-build.artifacts.integration-build-report }} + cache: false + timeout: 60m + runner: *build-runner + outputs: + filesystem: false + test-results: + # Captain has no nextest-specific parser. Infer its generic JUnit parser. + - path: .ci/op-reth/integration/junit.xml + artifacts: + - key: integration-reports + path: .ci/op-reth/integration + + - key: codec-base-build + use: codec-base-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh codec-base-build + env: + # The PR SHA is deliberately absent: an unchanged pinned develop build + # can content-hit when only the PR changes. + CACHE_EPOCH: ${{ init.cache-epoch }} + BUILD_PROBE: ${{ init.build-probe }} + TARGET_CACHE_MODE: ${{ init.target-cache-mode }} + CODEC_BASE_SHA: ${{ tasks.codec-base-revision.values.sha }} + tool-cache: op-reth-codec-base-${{ init.cache-epoch }} + timeout: 60m + runner: *build-runner + outputs: + filesystem: + filter: + workspace: [rust/target, rust/op-reth/crates/chainspec/res/superchain-configs.tar, .ci/rust-cache, .ci/op-reth/codec-base-build] + system: [] + artifacts: + - key: codec-base-build-report + path: .ci/op-reth/codec-base-build + + - key: codec-head-build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh codec-head-build + env: *build-env + tool-cache: op-reth-codec-head-${{ init.cache-epoch }} + timeout: 60m + runner: *build-runner + outputs: + filesystem: + filter: + workspace: [rust/target, rust/op-reth/crates/chainspec/res/superchain-configs.tar, .ci/rust-cache, .ci/op-reth/codec-head-build] + system: [] + artifacts: + - key: codec-head-build-report + path: .ci/op-reth/codec-head-build + + # Generate random baseline vectors afresh on every run. Only the immutable + # baseline compiler output is reusable, preserving CircleCI's sample cadence. + - key: codec-vectors + use: [code, rust-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: | + mkdir -p .ci/op-reth/codec-base-build + cp -a "$CODEC_BASE_ARTIFACT/." .ci/op-reth/codec-base-build/ + bash /usr/local/lib/optimism-ci/op-reth-shadow.sh codec-vectors + env: + CODEC_BASE_SHA: ${{ tasks.codec-base-revision.values.sha }} + CODEC_BASE_ARTIFACT: ${{ tasks.codec-base-build.artifacts.codec-base-build-report }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: codec-vectors + path: testdata/micro/compact + - key: codec-vector-provenance + path: .ci/op-reth/codec-vectors + + - key: codec + # Keep the PR checkout and mount only the head binary and fresh vectors. + use: [code, rust-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: | + mkdir -p .ci/op-reth/codec-head-build .ci/op-reth/codec-vectors + cp -a "$CODEC_HEAD_ARTIFACT/." .ci/op-reth/codec-head-build/ + cp -a "$CODEC_PROVENANCE/." .ci/op-reth/codec-vectors/ + rm -rf testdata/micro/compact + mkdir -p testdata/micro/compact + cp -a "$CODEC_VECTORS/." testdata/micro/compact/ + bash /usr/local/lib/optimism-ci/op-reth-shadow.sh codec + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CODEC_BASE_SHA: ${{ tasks.codec-base-revision.values.sha }} + CODEC_HEAD_ARTIFACT: ${{ tasks.codec-head-build.artifacts.codec-head-build-report }} + CODEC_PROVENANCE: ${{ tasks.codec-vectors.artifacts.codec-vector-provenance }} + CODEC_VECTORS: ${{ tasks.codec-vectors.artifacts.codec-vectors }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: codec-verification + path: .ci/op-reth/codec + + - key: snapshot-build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh snapshot-build + env: *build-env + tool-cache: op-reth-snapshot-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [rust/target, rust/op-reth/crates/chainspec/res/superchain-configs.tar, .ci/rust-cache, .ci/op-reth/snapshot-build] + system: [] + artifacts: + - key: snapshot-build-report + path: .ci/op-reth/snapshot-build + + - key: snapshot + use: snapshot-build + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh snapshot + cache: false + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: false + artifacts: + - key: snapshot-verification + path: .ci/op-reth/snapshot + + # Terminal task states preserve exact Rust dependencies without Main-only failures. + - key: rust-gate-receipt + use: [code, tools] + if: ${{ init.cache-warm != 'true' }} + after: ${{ (codec.succeeded || codec.failed || codec.skipped) && (integration.succeeded || integration.failed || integration.skipped) && (snapshot.succeeded || snapshot.failed || snapshot.skipped) }} + run: python3 ops/ci/pr-gate.py receipt op-reth-rust + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-rust-ci }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CODEC_SUCCEEDED: ${{ tasks.codec.succeeded }} + TASK_CODEC_FAILED: ${{ tasks.codec.failed }} + TASK_CODEC_SKIPPED: ${{ tasks.codec.skipped }} + TASK_INTEGRATION_SUCCEEDED: ${{ tasks.integration.succeeded }} + TASK_INTEGRATION_FAILED: ${{ tasks.integration.failed }} + TASK_INTEGRATION_SKIPPED: ${{ tasks.integration.skipped }} + TASK_SNAPSHOT_SUCCEEDED: ${{ tasks.snapshot.succeeded }} + TASK_SNAPSHOT_FAILED: ${{ tasks.snapshot.failed }} + TASK_SNAPSHOT_SKIPPED: ${{ tasks.snapshot.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/op-reth-rust diff --git a/.rwx/packages/acceptance-compile.yml b/.rwx/packages/acceptance-compile.yml new file mode 100644 index 00000000000..fc1510e8eb4 --- /dev/null +++ b/.rwx/packages/acceptance-compile.yml @@ -0,0 +1,43 @@ +package: + parameters: + commit-sha: + required: true + shard-total: + required: true + cl-kind: + required: true + go-artifact: + default: .ci/go-tests/dependencies/go + contract-artifact: + default: .ci/go-tests/dependencies/contracts + cache-key: + required: true +tasks: + - key: build + use: package.use + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts "$CONTRACT_ARTIFACT" + mise exec -- bash ops/ci/acceptance-tests.sh discover + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_SHARD_TOTAL: ${{ params.shard-total }} + DEVSTACK_L1_FORK: fusaka + DEVSTACK_L2CL_KIND: ${{ params.cl-kind }} + DEVSTACK_L2EL_KIND: op-reth + GO_ARTIFACT: ${{ params.go-artifact }} + CONTRACT_ARTIFACT: ${{ params.contract-artifact }} + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/acceptance/discover, .ci/acceptance/discovery] + system: [] + artifacts: + - key: discovery + path: .ci/acceptance/discovery + filter: ['!.ci/go-cache/full/dependencies'] diff --git a/.rwx/packages/acceptance-verdict.yml b/.rwx/packages/acceptance-verdict.yml new file mode 100644 index 00000000000..6ecbc5f68a5 --- /dev/null +++ b/.rwx/packages/acceptance-verdict.yml @@ -0,0 +1,84 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + shard-index: + required: true + shard-total: + required: true + cl-kind: + required: true + discovery: + default: .ci/acceptance/discovery + go-artifact: + default: .ci/go-tests/dependencies/go + contract-artifact: + default: .ci/go-tests/dependencies/contracts + kona-artifact: + default: .ci/go-tests/dependencies/kona + reth-artifact: + default: .ci/go-tests/dependencies/op-reth + prestate-artifact: + default: .ci/go-tests/dependencies/prestate + sp1-artifact: + default: .ci/go-tests/dependencies/sp1-executor + cache-key: + required: true + memory: + required: true +tasks: + - key: run + use: package.use + docker: true + run: | + for dependency in go contracts kona op-reth prestate sp1-executor; do + case "$dependency" in + go) artifact="$GO_ARTIFACT" ;; + contracts) artifact="$CONTRACT_ARTIFACT" ;; + kona) artifact="$KONA_ARTIFACT" ;; + op-reth) artifact="$RETH_ARTIFACT" ;; + prestate) artifact="$PRESTATE_ARTIFACT" ;; + sp1-executor) artifact="$SP1_ARTIFACT" ;; + esac + python3 ops/ci/go-artifacts.py restore "$dependency" "$artifact" + done + export CI_ACCEPTANCE_MANIFEST="$PWD/$CI_ACCEPTANCE_MANIFEST" + mise exec -- bash ops/ci/acceptance-tests.sh run + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CIRCLE_BRANCH: ${{ params.branch }} + CI_SHARD_INDEX: ${{ params.shard-index }} + CI_SHARD_TOTAL: ${{ params.shard-total }} + DEVSTACK_L1_FORK: fusaka + DEVSTACK_L2CL_KIND: ${{ params.cl-kind }} + DEVSTACK_L2EL_KIND: op-reth + CI_ACCEPTANCE_MANIFEST: ${{ params.discovery }} + GO_ARTIFACT: ${{ params.go-artifact }} + CONTRACT_ARTIFACT: ${{ params.contract-artifact }} + KONA_ARTIFACT: ${{ params.kona-artifact }} + RETH_ARTIFACT: ${{ params.reth-artifact }} + PRESTATE_ARTIFACT: ${{ params.prestate-artifact }} + SP1_ARTIFACT: ${{ params.sp1-artifact }} + cache: false + tool-cache: ${{ params.cache-key }} + timeout: 120m + runner: + cpus: 16 + memory: ${{ params.memory }} + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/acceptance/run] + system: [] + test-results: + - path: .ci/acceptance/reports/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/acceptance/reports + filter: ['!.ci/go-cache', '!.ci/rust-cache', '!rust/target', '!packages/contracts-bedrock/cache', '!packages/contracts-bedrock/artifacts', '!packages/contracts-bedrock/forge-artifacts', '!op-deployer/pkg/deployer/artifacts/forge-artifacts', '!.ci/go-cache/acceptance/discover'] diff --git a/.rwx/packages/contract-coverage-compile.yml b/.rwx/packages/contract-coverage-compile.yml new file mode 100644 index 00000000000..75a1607c8bd --- /dev/null +++ b/.rwx/packages/contract-coverage-compile.yml @@ -0,0 +1,37 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + cache-key: + required: true + feature: + required: true +tasks: + - key: build + use: package.use + run: | + cd project + export GOMODCACHE="$PWD/.ci/go-cache/contract-coverage/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-coverage/build" + python3 ops/ci/contract-coverage.py prepare --feature "$CI_CONTRACT_FEATURE" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + CI_CONTRACT_COVERAGE_REPLAY: 'true' + CI_CONTRACT_FEATURE: ${{ params.feature }} + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 16 + memory: 64gb + outputs: + filesystem: + filter: + workspace: [project/packages/contracts-bedrock/forge-artifacts, project/packages/contracts-bedrock/cache/solidity-files-cache.json, project/packages/contracts-bedrock/artifacts/build-info, project/packages/contracts-bedrock/scripts/go-ffi/go-ffi, project/.ci/go-cache/contract-coverage/build, 'project/.ci/contract-coverage'] + system: [] + artifacts: + - key: compiled + path: project/.ci/contract-coverage/${{ params.feature }}/prepare diff --git a/.rwx/packages/contract-coverage-verdict.yml b/.rwx/packages/contract-coverage-verdict.yml new file mode 100644 index 00000000000..4b8f90ad6bc --- /dev/null +++ b/.rwx/packages/contract-coverage-verdict.yml @@ -0,0 +1,57 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + compiled: + required: true + archive-preflight: + required: true + cache-key: + required: true + feature: + required: true +tasks: + - key: run + use: package.use + run: | + cd project + export GOMODCACHE="$PWD/.ci/go-cache/contract-coverage/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-coverage/runtime" + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + python3 ops/ci/contract-coverage.py run --feature "$CI_CONTRACT_FEATURE" \ + --prepared "$COMPILED" --block "$ARCHIVE_PREFLIGHT/block.json" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + CI_CONTRACT_COVERAGE_REPLAY: 'true' + COMPILED: ${{ params.compiled }} + ARCHIVE_PREFLIGHT: ${{ params.archive-preflight }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + CI_CONTRACT_FEATURE: ${{ params.feature }} + cache: false + tool-cache: ${{ params.cache-key }} + timeout: 120m + runner: + cpus: 16 + memory: 64gb + outputs: + filesystem: + filter: + workspace: [project/.ci/go-cache/contract-coverage/runtime] + system: [~/.foundry/cache/rpc] + test-results: + - path: project/.ci/contract-coverage/${{ params.feature }}/run/ordinary/derived.junit.xml + options: + language: Solidity + framework: Foundry + - path: project/.ci/contract-coverage/${{ params.feature }}/run/upgrade/derived.junit.xml + options: + language: Solidity + framework: Foundry + artifacts: + - key: reports + path: project/.ci/contract-coverage/${{ params.feature }}/run diff --git a/.rwx/packages/contract-upgrades-compile.yml b/.rwx/packages/contract-upgrades-compile.yml new file mode 100644 index 00000000000..2d3596ed505 --- /dev/null +++ b/.rwx/packages/contract-upgrades-compile.yml @@ -0,0 +1,37 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + cache-key: + required: true + variant: + required: true +tasks: + - key: build + use: package.use + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-upgrades/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-upgrades/build" + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + python3 ops/ci/contract-upgrades.py prepare --variant "$CI_CONTRACT_VARIANT" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + CI_CONTRACT_VARIANT: ${{ params.variant }} + tool-cache: ${{ params.cache-key }} + timeout: 40m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/forge-artifacts, packages/contracts-bedrock/cache/solidity-files-cache.json, packages/contracts-bedrock/artifacts/build-info, packages/contracts-bedrock/scripts/go-ffi/go-ffi, .ci/go-cache/contract-upgrades/build, '.ci/contract-upgrades'] + system: [] + artifacts: + - key: compiled + path: .ci/contract-upgrades/${{ params.variant }}/prepare diff --git a/.rwx/packages/contract-upgrades-verdict.yml b/.rwx/packages/contract-upgrades-verdict.yml new file mode 100644 index 00000000000..dc401bd071f --- /dev/null +++ b/.rwx/packages/contract-upgrades-verdict.yml @@ -0,0 +1,51 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + compiled: + required: true + pinned-block: + required: true + cache-key: + required: true + variant: + required: true +tasks: + - key: run + use: package.use + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-upgrades/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-upgrades/build" + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + python3 ops/ci/contract-upgrades.py run --variant "$CI_CONTRACT_VARIANT" \ + --prepared "$COMPILED" --block "$PINNED_BLOCK" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + COMPILED: ${{ params.compiled }} + PINNED_BLOCK: ${{ params.pinned-block }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + CI_CONTRACT_VARIANT: ${{ params.variant }} + cache: false + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [] + system: [~/.foundry/cache/rpc] + test-results: + - path: .ci/contract-upgrades/${{ params.variant }}/run/original.junit.xml + options: + language: Solidity + framework: Foundry + artifacts: + - key: reports + path: .ci/contract-upgrades/${{ params.variant }}/run diff --git a/.rwx/packages/contracts-compile.yml b/.rwx/packages/contracts-compile.yml new file mode 100644 index 00000000000..b1474a1fefd --- /dev/null +++ b/.rwx/packages/contracts-compile.yml @@ -0,0 +1,41 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + build-probe: + required: true + cache-key: + required: true + suite: + required: true + feature: + required: true +tasks: + - key: build + use: package.use + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-suites/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-suites/build" + python3 ops/ci/contract-suites.py prepare --suite "$CI_CONTRACT_SUITE" --feature "$CI_CONTRACT_FEATURE" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + BUILD_PROBE: ${{ params.build-probe }} + CI_CONTRACT_SUITE: ${{ params.suite }} + CI_CONTRACT_FEATURE: ${{ params.feature }} + tool-cache: ${{ params.cache-key }} + timeout: 40m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/forge-artifacts, packages/contracts-bedrock/cache/solidity-files-cache.json, packages/contracts-bedrock/artifacts/build-info, packages/contracts-bedrock/scripts/go-ffi/go-ffi, .ci/go-cache/contract-suites/build, '.ci/contract-suites'] + system: [] + artifacts: + - key: compiled + path: .ci/contract-suites/${{ params.suite }}-${{ params.feature }}/prepare diff --git a/.rwx/packages/contracts-verdict.yml b/.rwx/packages/contracts-verdict.yml new file mode 100644 index 00000000000..7e957e5bb0d --- /dev/null +++ b/.rwx/packages/contracts-verdict.yml @@ -0,0 +1,51 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + compiled: + required: true + cache-key: + required: true + suite: + required: true + feature: + required: true +tasks: + - key: run + use: package.use + run: | + export GOMODCACHE="$PWD/.ci/go-cache/contract-suites/modules" + export GOCACHE="$PWD/.ci/go-cache/contract-suites-runtime/build" + python3 ops/ci/contract-suites.py run --suite "$CI_CONTRACT_SUITE" --feature "$CI_CONTRACT_FEATURE" --prepared "$COMPILED" + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CONTRACT_PROVIDER: rwx + COMPILED: ${{ params.compiled }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_CONTRACT_SUITE: ${{ params.suite }} + CI_CONTRACT_FEATURE: ${{ params.feature }} + cache: false + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/contract-suites-runtime/build] + system: [] + test-results: + - path: .ci/contract-suites/${{ params.suite }}-${{ params.feature }}/run/original.junit.xml + options: + language: Solidity + framework: Foundry + artifacts: + - key: reports + path: .ci/contract-suites/${{ params.suite }}-${{ params.feature }}/run diff --git a/.rwx/packages/fixture-compile.yml b/.rwx/packages/fixture-compile.yml new file mode 100644 index 00000000000..9caf9bf543a --- /dev/null +++ b/.rwx/packages/fixture-compile.yml @@ -0,0 +1,18 @@ +# Small native proof of the same artifact-only boundary used by Go verdicts. +package: true +tasks: + - key: build + use: package.use + run: | + mkdir -p compiler-cache compiled + echo compiler-only >compiler-cache/marker + echo verified-binary >compiled/binary + echo true >"$RWX_VALUES/eligible" + outputs: + filesystem: + filter: + workspace: [compiler-cache, compiled] + system: [] + artifacts: + - key: binaries + path: compiled diff --git a/.rwx/packages/fixture-verdict.yml b/.rwx/packages/fixture-verdict.yml new file mode 100644 index 00000000000..4bc8e695c83 --- /dev/null +++ b/.rwx/packages/fixture-verdict.yml @@ -0,0 +1,26 @@ +package: + parameters: + binaries: + required: true + intentional-failure: + required: true +tasks: + - key: run + use: package.use + run: | + mkdir -p reports + trap 'echo "$?" >reports/exit-code' EXIT + test ! -e compiler-cache/marker + test "$(cat "$BINARIES/binary")" = verified-binary + date +%s%N >reports/executed-at + if [[ "$INTENTIONAL_FAILURE" == true ]]; then exit 17; fi + env: + BINARIES: ${{ params.binaries }} + INTENTIONAL_FAILURE: ${{ params.intentional-failure }} + cache: false + filter: ['!compiler-cache'] + outputs: + filesystem: false + artifacts: + - key: reports + path: reports diff --git a/.rwx/packages/go-contracts.yml b/.rwx/packages/go-contracts.yml new file mode 100644 index 00000000000..48dedf4b9f1 --- /dev/null +++ b/.rwx/packages/go-contracts.yml @@ -0,0 +1,27 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh contracts + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: go-full-contracts-ci-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/cache, packages/contracts-bedrock/artifacts, packages/contracts-bedrock/forge-artifacts, .ci/go-cache/full, .ci/go-tests/dependencies/contracts] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/contracts diff --git a/.rwx/packages/go-go.yml b/.rwx/packages/go-go.yml new file mode 100644 index 00000000000..93384f7c591 --- /dev/null +++ b/.rwx/packages/go-go.yml @@ -0,0 +1,27 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh go + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: go-full-dependencies-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/full, .ci/go-tests/dependencies/go] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/go diff --git a/.rwx/packages/go-kona.yml b/.rwx/packages/go-kona.yml new file mode 100644 index 00000000000..18fdaa6065e --- /dev/null +++ b/.rwx/packages/go-kona.yml @@ -0,0 +1,27 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh kona + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: kona-release-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [rust/target, .ci/rust-cache, .ci/go-tests/dependencies/kona] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/kona diff --git a/.rwx/packages/go-modules.yml b/.rwx/packages/go-modules.yml new file mode 100644 index 00000000000..6bb7bc094ed --- /dev/null +++ b/.rwx/packages/go-modules.yml @@ -0,0 +1,33 @@ +# Modules only: Cannon's Circle job does not embed generated Cannon binaries. +package: + parameters: + commit-sha: + branch: + cache-epoch: +tasks: + - key: build + use: package.use + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + mkdir -p "$GOMODCACHE" + python3 ops/ci/pr-checks.py go-modules + python3 ops/ci/go-artifacts.py pack go-modules .ci/go-cache/pr-checks/modules + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: pr-checks-go-modules-${{ params.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/pr-checks/modules, .ci/go-tests/dependencies/go-modules] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/go-modules + - key: preparation + path: .ci/pr-checks/go-modules diff --git a/.rwx/packages/go-op-reth.yml b/.rwx/packages/go-op-reth.yml new file mode 100644 index 00000000000..8d8932e0381 --- /dev/null +++ b/.rwx/packages/go-op-reth.yml @@ -0,0 +1,29 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh op-reth + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: op-reth-release-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [rust/target, .ci/rust-cache, .ci/op-reth/release-build, .ci/go-tests/dependencies/op-reth] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/op-reth + - key: release-binaries + path: .ci/op-reth/release-build diff --git a/.rwx/packages/go-prestate.yml b/.rwx/packages/go-prestate.yml new file mode 100644 index 00000000000..493e6092b88 --- /dev/null +++ b/.rwx/packages/go-prestate.yml @@ -0,0 +1,28 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh prestate + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: go-full-prestate-${{ params.cache-epoch }} + docker: preserve-data + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [rust/kona/prestate-artifacts-*, .ci/go-tests/dependencies/prestate] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/prestate diff --git a/.rwx/packages/go-sp1-executor.yml b/.rwx/packages/go-sp1-executor.yml new file mode 100644 index 00000000000..45a90fc815b --- /dev/null +++ b/.rwx/packages/go-sp1-executor.yml @@ -0,0 +1,27 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh sp1-executor + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: sp1-executor-release-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [rust/target, .ci/rust-cache, .ci/go-tests/dependencies/sp1-executor] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/sp1-executor diff --git a/.rwx/packages/go-test-compile.yml b/.rwx/packages/go-test-compile.yml new file mode 100644 index 00000000000..174d39d9a62 --- /dev/null +++ b/.rwx/packages/go-test-compile.yml @@ -0,0 +1,37 @@ +package: + parameters: + commit-sha: + required: true + shard-index: + required: true + shard-total: + required: true + test-parallel: + required: true + build-probe: + required: true + cache-key: + required: true +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-full-tests.sh build + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_SHARD_INDEX: ${{ params.shard-index }} + CI_SHARD_TOTAL: ${{ params.shard-total }} + PARALLEL: ${{ params.test-parallel }} + BUILD_PROBE: ${{ params.build-probe }} + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/full/build, .ci/go-tests/build] + system: [] + artifacts: + - key: binaries + path: .ci/go-tests/build diff --git a/.rwx/packages/go-test-verdict.yml b/.rwx/packages/go-test-verdict.yml new file mode 100644 index 00000000000..09bba16ca26 --- /dev/null +++ b/.rwx/packages/go-test-verdict.yml @@ -0,0 +1,78 @@ +package: + parameters: + commit-sha: + required: true + branch: + required: true + shard-index: + required: true + shard-total: + required: true + test-parallel: + required: true + go-artifact: + default: .ci/go-tests/dependencies/go + contract-artifact: + default: .ci/go-tests/dependencies/contracts + kona-artifact: + default: .ci/go-tests/dependencies/kona + reth-artifact: + default: .ci/go-tests/dependencies/op-reth + prestate-artifact: + default: .ci/go-tests/dependencies/prestate + cache-key: + required: true +tasks: + - key: run + use: package.use + docker: true + run: | + for dependency in go contracts kona op-reth prestate; do + case "$dependency" in + go) artifact="$GO_ARTIFACT" ;; + contracts) artifact="$CONTRACT_ARTIFACT" ;; + kona) artifact="$KONA_ARTIFACT" ;; + op-reth) artifact="$RETH_ARTIFACT" ;; + prestate) artifact="$PRESTATE_ARTIFACT" ;; + esac + python3 ops/ci/go-artifacts.py restore "$dependency" "$artifact" + done + export RUST_BINARY_PATH_OP_RETH="$PWD/rust/target/release/op-reth" + mise exec -- bash ops/ci/go-full-tests.sh run + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + CIRCLE_BRANCH: ${{ params.branch }} + CI_SHARD_INDEX: ${{ params.shard-index }} + CI_SHARD_TOTAL: ${{ params.shard-total }} + PARALLEL: ${{ params.test-parallel }} + GO_ARTIFACT: ${{ params.go-artifact }} + CONTRACT_ARTIFACT: ${{ params.contract-artifact }} + KONA_ARTIFACT: ${{ params.kona-artifact }} + RETH_ARTIFACT: ${{ params.reth-artifact }} + PRESTATE_ARTIFACT: ${{ params.prestate-artifact }} + OP_CI_MAINNET_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_MAINNET_L1_ARCHIVE_RPC_URL }} + OP_CI_SEPOLIA_L1_ARCHIVE_RPC_URL: ${{ vaults.optimism-go-tests-rpc-shadow.secrets.OP_CI_SEPOLIA_L1_ARCHIVE_RPC_URL }} + cache: false + tool-cache: ${{ params.cache-key }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/full/run] + system: [] + test-results: + - path: tmp/testlogs/native.json + options: + language: Go + framework: go test + artifacts: + - key: junit + path: tmp/test-results + - key: go-json + path: tmp/testlogs/log.json + - key: test-logs + path: tmp/testlogs + filter: ['!.ci/go-cache', '!.ci/rust-cache', '!rust/target', '!packages/contracts-bedrock/cache', '!packages/contracts-bedrock/artifacts', '!packages/contracts-bedrock/forge-artifacts', '!op-deployer/pkg/deployer/artifacts/forge-artifacts'] diff --git a/.rwx/packages/kontrol-contracts.yml b/.rwx/packages/kontrol-contracts.yml new file mode 100644 index 00000000000..da573c61686 --- /dev/null +++ b/.rwx/packages/kontrol-contracts.yml @@ -0,0 +1,23 @@ +package: + parameters: + commit-sha: + cache-epoch: +tasks: + - key: build + use: package.use + run: python3 ops/ci/kontrol-contracts.py + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + tool-cache: kontrol-contracts-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/cache, packages/contracts-bedrock/artifacts, packages/contracts-bedrock/forge-artifacts, .ci/go-cache/full, .ci/kontrol-contracts] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/contracts-kontrol diff --git a/.rwx/packages/nut-tools.yml b/.rwx/packages/nut-tools.yml new file mode 100644 index 00000000000..84f827db317 --- /dev/null +++ b/.rwx/packages/nut-tools.yml @@ -0,0 +1,27 @@ +# Reusable historical generators and immutable Solidity compiler binaries. +package: + parameters: + commit-sha: + branch: + cache-epoch: +tasks: + - key: prepare + use: package.use + run: python3 ops/ci/nut-provenance.py --provider rwx --full --prepare-tools + env: + CI: "true" + CI_COMMIT_SHA: ${{ params.commit-sha }} + CI_BRANCH: ${{ params.branch }} + RWX_RUN_ID: + cache-key: excluded + RWX_TASK_ATTEMPT_NUMBER: + cache-key: excluded + tool-cache: nut-provenance-historical-tools-${{ params.cache-epoch }} + timeout: 30m + runner: + cpus: 2 + memory: 8gb + outputs: + artifacts: + - key: tools + path: .ci/nut-provenance/toolchain diff --git a/.rwx/packages/rust-e2e-contracts.yml b/.rwx/packages/rust-e2e-contracts.yml new file mode 100644 index 00000000000..bece64cac93 --- /dev/null +++ b/.rwx/packages/rust-e2e-contracts.yml @@ -0,0 +1,27 @@ +package: + parameters: + commit-sha: + cache-epoch: + build-probe: + target-cache-mode: +tasks: + - key: build + use: package.use + run: mise exec -- bash ops/ci/go-dependencies.sh contracts-e2e + env: + CI_COMMIT_SHA: ${{ params.commit-sha }} + BUILD_PROBE: ${{ params.build-probe }} + TARGET_CACHE_MODE: ${{ params.target-cache-mode }} + tool-cache: go-rust-e2e-contracts-ci-${{ params.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/cache, packages/contracts-bedrock/artifacts, packages/contracts-bedrock/forge-artifacts, .ci/go-cache/full, .ci/go-tests/dependencies/contracts-e2e] + system: [] + artifacts: + - key: dependency + path: .ci/go-tests/dependencies/contracts-e2e diff --git a/.rwx/packages/rust-e2e-release.yml b/.rwx/packages/rust-e2e-release.yml new file mode 100644 index 00000000000..e41053c98af --- /dev/null +++ b/.rwx/packages/rust-e2e-release.yml @@ -0,0 +1,28 @@ +package: + parameters: + commit-sha: + cache-epoch: +tasks: + - key: build + use: package.use + run: bash ops/ci/rust-e2e-release.sh + env: + CI_RUST_PROVIDER: rwx + CI_COMMIT_SHA: ${{ params.commit-sha }} + tool-cache: rust-e2e-workspace-release-${{ params.cache-epoch }} + timeout: 120m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/rust-cache, rust/target] + system: [] + test-results: + - path: .ci/rust-workspace/e2e-release/checks.junit.xml + artifacts: + - key: reports + path: .ci/rust-workspace/e2e-release + - key: dependency + path: .ci/go-tests/dependencies/rust-e2e-release diff --git a/.rwx/packages/toolchain-common.yml b/.rwx/packages/toolchain-common.yml new file mode 100644 index 00000000000..16fa9dc97ea --- /dev/null +++ b/.rwx/packages/toolchain-common.yml @@ -0,0 +1,9 @@ +package: true +tasks: + - key: prepare + use: package.use + run: | + bash ops/ci/rwx-prepare.sh + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh] + timeout: 30m diff --git a/.rwx/packages/toolchain-foundry.yml b/.rwx/packages/toolchain-foundry.yml new file mode 100644 index 00000000000..b661dd368b1 --- /dev/null +++ b/.rwx/packages/toolchain-foundry.yml @@ -0,0 +1,26 @@ +package: + parameters: + mode: + required: true +tasks: + - key: prepare + use: package.use + run: | + bash ops/ci/rwx-contracts-prepare.sh tools + case "$FOUNDRY_TOOL_MODE" in + compiler) ;; + anvil) mise install anvil ;; + analysis) + mise install uv + PATH="$(mise bin-paths | paste -sd: -):$PATH" + export PATH + mise install pipx:semgrep + printf '%s:%s\n' "$(mise bin-paths | paste -sd: -)" "$PATH" >"$RWX_ENV/PATH" + ;; + *) echo "Unknown Foundry tool mode: $FOUNDRY_TOOL_MODE" >&2; exit 1 ;; + esac + env: + FOUNDRY_TOOL_MODE: ${{ params.mode }} + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh] + timeout: 30m diff --git a/.rwx/packages/toolchain-go.yml b/.rwx/packages/toolchain-go.yml new file mode 100644 index 00000000000..2b42ebf8c38 --- /dev/null +++ b/.rwx/packages/toolchain-go.yml @@ -0,0 +1,14 @@ +# Pinned Go tool layers; language installation remains separate from source and verdicts. +package: + parameters: + mode: + required: true + timeout: + default: 30m +tasks: + - key: prepare + use: package.use + run: bash ops/ci/rwx-prepare.sh "$GO_TOOL_MODE" + env: + GO_TOOL_MODE: ${{ params.mode }} + timeout: ${{ params.timeout }} diff --git a/.rwx/packages/toolchain-rust.yml b/.rwx/packages/toolchain-rust.yml new file mode 100644 index 00000000000..65047abf80d --- /dev/null +++ b/.rwx/packages/toolchain-rust.yml @@ -0,0 +1,29 @@ +package: + parameters: + mode: + required: true +tasks: + - key: prepare + use: package.use + run: | + bash ops/ci/rwx-prepare.sh rust + mise exec -- bash ops/ci/rwx-rust-prepare.sh + case "$RUST_TOOL_MODE" in + release) ;; + e2e) bash .circleci/scripts/apt-install.sh m4 file ;; + workspace|sp1) + bash .circleci/scripts/apt-install.sh m4 file + if [[ "$RUST_TOOL_MODE" == workspace ]]; then + mise install github:est31/cargo-udeps github:taiki-e/cargo-hack + else + mise install github:succinctlabs/sp1 + fi + printf '%s:%s\n' "$(mise bin-paths | paste -sd: -)" "$PATH" >"$RWX_ENV/PATH" + ;; + *) echo "Unknown Rust tool mode: $RUST_TOOL_MODE" >&2; exit 1 ;; + esac + env: + RUST_TOOL_MODE: ${{ params.mode }} + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + timeout: 30m diff --git a/.rwx/pilot.yml b/.rwx/pilot.yml new file mode 100644 index 00000000000..13ed64cf9f6 --- /dev/null +++ b/.rwx/pilot.yml @@ -0,0 +1,186 @@ +# Optional shadow checks. CircleCI remains responsible for the required gates. +# Pushes include Bailiff's human-authorized external-fork branches and GitHub's +# gh-readonly-queue branches. Avoid a second pull_request trigger for those SHAs. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [go-lint-tools, rust-fmt-tools] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + # Automatic pushes are coordinated by pr-gates.yml; CLI and warming remain. + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + # This public checkout deliberately has no GitHub token or vault access. + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: go-lint-tools + use: tools + run: bash ops/ci/rwx-prepare.sh go-lint + timeout: 30m + + - key: rust-fmt-tools + use: tools + run: bash ops/ci/rwx-prepare.sh rust + timeout: 30m + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: routing-tests + use: [code, tools] + run: | + mise exec -- bash ops/ci/test-decision-tree.sh + mise exec -- python -m unittest ops/ci/test_rwx_metadata.py + mise exec -- python ops/ci/test_compare_ci.py + cache: false + timeout: 10m + outputs: + filesystem: false + + # Preserve the superchain bundle's existing verification before Go consumes it. + - key: prep-superchain + use: [code, go-lint-tools] + if: ${{ tasks.route.values.run-main == 'true' }} + run: mise exec -- just build-superchain-go + cache: false + timeout: 15m + + - key: go-lint + use: prep-superchain + if: ${{ tasks.route.values.run-main == 'true' }} + run: | + mise exec -- just lint-go + mise exec -- bash ops/prestate-reproducibility/test-build-prestates.sh + cache: false + timeout: 30m + runner: + cpus: 8 + memory: 32gb + outputs: + filesystem: false + + - key: rust-fmt + use: [code, rust-fmt-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: | + mise exec -- just update-superchain-registry-submodule + cd rust + mise exec -- just fmt-check + mise exec -- just check-upstream-mirrors + cache: false + timeout: 20m + outputs: + filesystem: false + + # Terminal task states preserve exact Rust dependencies without Main-only failures. + - key: rust-gate-receipt + use: [code, tools] + after: ${{ (rust-fmt.succeeded || rust-fmt.failed || rust-fmt.skipped) }} + run: python3 ops/ci/pr-gate.py receipt rust-fmt + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-rust-ci }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_RUST_FMT_SUCCEEDED: ${{ tasks.rust-fmt.succeeded }} + TASK_RUST_FMT_FAILED: ${{ tasks.rust-fmt.failed }} + TASK_RUST_FMT_SKIPPED: ${{ tasks.rust-fmt.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/rust-fmt + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (go-lint.succeeded || go-lint.failed || go-lint.skipped) }} + run: python3 ops/ci/pr-gate.py receipt main-pilot + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_GO_LINT_SUCCEEDED: ${{ tasks.go-lint.succeeded }} + TASK_GO_LINT_FAILED: ${{ tasks.go-lint.failed }} + TASK_GO_LINT_SKIPPED: ${{ tasks.go-lint.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-pilot diff --git a/.rwx/pr-checks.yml b/.rwx/pr-checks.yml new file mode 100644 index 00000000000..dd86d0c4f53 --- /dev/null +++ b/.rwx/pr-checks.yml @@ -0,0 +1,588 @@ +# Complete remaining PR checks, with Circle continuing to own required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [go-modules, contract-tools, main-tools, main-superchain, static-tools] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: | + bash ops/ci/rwx-metadata.sh + jq -er '."c-run_contracts_feature_tests" | if type == "boolean" then tostring else error("missing contracts route") end' \ + .ci/pipeline-parameters.json >"$RWX_VALUES/run-contracts" + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: go-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: contract-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-contracts == 'true' }} + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: analysis + + - key: main-tools + use: go-tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + mise install ripgrep mockery + printf '%s:%s\n' "$(mise bin-paths | paste -sd: -)" "$PATH" >"$RWX_ENV/PATH" + timeout: 30m + + - key: static-tools + use: tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + mise install uv shellcheck@0.9.0 + PATH="$(mise bin-paths | paste -sd: -):$PATH" + export PATH + mise install pipx:semgrep@1.178.0 + mise exec shellcheck@0.9.0 pipx:semgrep@1.178.0 -- sh -c 'printf "%s\n" "$PATH"' >"$RWX_ENV/PATH" + timeout: 30m + + - key: static-helper-tests + use: [code, static-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + RWX_LIVE_SHELL_FIXTURE=1 RWX_LIVE_SEMGREP_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_static_checks.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_static_checks.py' + env: + RWX_STATIC_FIXTURE_RETAIN_DIR: .ci/static-fixture-evidence + cache: false + timeout: 15m + outputs: + filesystem: false + artifacts: + - key: original-failure-fixtures + path: .ci/static-fixture-evidence + + - key: shell-check + use: [code, static-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/static-checks.py shell-check + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + cache: false + timeout: 20m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: false + test-results: + - path: .ci/static-checks/shell-check/derived.junit.xml + artifacts: + - key: reports + path: .ci/static-checks/shell-check + + - key: semgrep-test + use: [code, static-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + if [[ "$CI_BRANCH" != develop ]]; then + git fetch --no-tags origin develop + git update-ref refs/heads/develop FETCH_HEAD + fi + python3 ops/ci/static-checks.py semgrep-test + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + cache: false + timeout: 20m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: false + test-results: + - path: .ci/static-checks/semgrep-test/derived.junit.xml + artifacts: + - key: reports + path: .ci/static-checks/semgrep-test + + - key: semgrep-scan-local + use: [code, static-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + if [[ "$CI_BRANCH" != develop ]]; then + git fetch --no-tags origin develop + git update-ref refs/heads/develop FETCH_HEAD + fi + python3 ops/ci/static-checks.py semgrep-scan-local + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + cache: false + timeout: 30m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: false + test-results: + - path: .ci/static-checks/semgrep-scan-local/derived.junit.xml + artifacts: + - key: reports + path: .ci/static-checks/semgrep-scan-local + + - key: helper-tests + use: [code, go-tools] + if: ${{ init.cache-warm != 'true' && (tasks.route.values.run-main == 'true' || tasks.route.values.run-contracts == 'true') }} + run: | + RWX_LIVE_GO_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_pr_checks.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_pr_checks.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: go-modules + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' || tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + mkdir -p "$GOMODCACHE" + python3 ops/ci/pr-checks.py go-modules + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: pr-checks-go-modules-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/pr-checks/modules] + system: [] + artifacts: + - key: reports + path: .ci/pr-checks/go-modules + + - key: contracts-fast + use: [code, go-tools, go-modules, contract-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-contracts == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/pr-checks/fast-build" + git submodule sync --recursive + git -c protocol.file.allow=never submodule update --init --recursive --jobs 8 + python3 ops/ci/pr-checks.py contracts-fast + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + tool-cache: pr-checks-contracts-fast-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/forge-artifacts, packages/contracts-bedrock/cache/solidity-files-cache.json, packages/contracts-bedrock/artifacts/build-info, .ci/go-cache/pr-checks/fast-build] + system: [] + test-results: + - path: .ci/pr-checks/contracts-fast/checks.junit.xml + artifacts: + - key: reports + path: .ci/pr-checks/contracts-fast + + - key: main-helper-tests + use: [code, main-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + RWX_LIVE_MAIN_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_main_checks.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_main_checks.py' + cache: false + timeout: 15m + outputs: + filesystem: false + + - key: main-superchain + use: [code, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/main-checks.py --prepare-superchain + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + timeout: 15m + outputs: + filesystem: + filter: + workspace: [op-core/superchain/superchain-configs.zip] + system: [] + artifacts: + - key: reports + path: .ci/main-checks/prep-superchain + + - key: todo-issues-check + use: [code, main-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + python3 ops/ci/main-checks.py todo-issues-check + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 30m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: false + test-results: + - path: .ci/main-checks/todo-issues-check/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/todo-issues-check + + - key: l2-chains-sync-check + use: [code, main-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + python3 ops/ci/main-checks.py l2-chains-sync-check + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 30m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: false + test-results: + - path: .ci/main-checks/l2-chains-sync-check/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/l2-chains-sync-check + + - key: op-deployer-forge-version + use: [code, main-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + python3 ops/ci/main-checks.py op-deployer-forge-version + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 30m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: false + test-results: + - path: .ci/main-checks/op-deployer-forge-version/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/op-deployer-forge-version + + - key: check-op-geth-version + use: [code, main-tools, go-modules] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/main-checks/check-op-geth-version" + python3 ops/ci/main-checks.py check-op-geth-version + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: main-checks-check-op-geth-version-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/main-checks/check-op-geth-version] + system: [] + test-results: + - path: .ci/main-checks/check-op-geth-version/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/check-op-geth-version + + - key: check-nut-locks + use: [code, main-tools, go-modules] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/main-checks/check-nut-locks" + python3 ops/ci/main-checks.py check-nut-locks + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: main-checks-check-nut-locks-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/main-checks/check-nut-locks] + system: [] + test-results: + - path: .ci/main-checks/check-nut-locks/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/check-nut-locks + + - key: check-generated-mocks-op-node + use: [code, main-tools, go-modules, main-superchain] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/main-checks/check-generated-mocks-op-node" + python3 ops/ci/main-checks.py check-generated-mocks-op-node + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + SUPERCHAIN_PREPARED: ${{ tasks.main-superchain.artifacts.reports }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: main-checks-check-generated-mocks-op-node-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/main-checks/check-generated-mocks-op-node] + system: [] + test-results: + - path: .ci/main-checks/check-generated-mocks-op-node/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/check-generated-mocks-op-node + + - key: check-generated-mocks-op-service + use: [code, main-tools, go-modules, main-superchain] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: | + export GOMODCACHE="$PWD/.ci/go-cache/pr-checks/modules" + export GOCACHE="$PWD/.ci/go-cache/main-checks/check-generated-mocks-op-service" + python3 ops/ci/main-checks.py check-generated-mocks-op-service + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + SUPERCHAIN_PREPARED: ${{ tasks.main-superchain.artifacts.reports }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + tool-cache: main-checks-check-generated-mocks-op-service-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/main-checks/check-generated-mocks-op-service] + system: [] + test-results: + - path: .ci/main-checks/check-generated-mocks-op-service/check.junit.xml + artifacts: + - key: reports + path: .ci/main-checks/check-generated-mocks-op-service + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (check-generated-mocks-op-node.succeeded || check-generated-mocks-op-node.failed || check-generated-mocks-op-node.skipped) && (check-generated-mocks-op-service.succeeded || check-generated-mocks-op-service.failed || check-generated-mocks-op-service.skipped) && (check-nut-locks.succeeded || check-nut-locks.failed || check-nut-locks.skipped) && (check-op-geth-version.succeeded || check-op-geth-version.failed || check-op-geth-version.skipped) && (l2-chains-sync-check.succeeded || l2-chains-sync-check.failed || l2-chains-sync-check.skipped) && (op-deployer-forge-version.succeeded || op-deployer-forge-version.failed || op-deployer-forge-version.skipped) && (semgrep-scan-local.succeeded || semgrep-scan-local.failed || semgrep-scan-local.skipped) && (semgrep-test.succeeded || semgrep-test.failed || semgrep-test.skipped) && (shell-check.succeeded || shell-check.failed || shell-check.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-checks + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CHECK_GENERATED_MOCKS_OP_NODE_SUCCEEDED: ${{ tasks.check-generated-mocks-op-node.succeeded }} + TASK_CHECK_GENERATED_MOCKS_OP_NODE_FAILED: ${{ tasks.check-generated-mocks-op-node.failed }} + TASK_CHECK_GENERATED_MOCKS_OP_NODE_SKIPPED: ${{ tasks.check-generated-mocks-op-node.skipped }} + TASK_CHECK_GENERATED_MOCKS_OP_SERVICE_SUCCEEDED: ${{ tasks.check-generated-mocks-op-service.succeeded }} + TASK_CHECK_GENERATED_MOCKS_OP_SERVICE_FAILED: ${{ tasks.check-generated-mocks-op-service.failed }} + TASK_CHECK_GENERATED_MOCKS_OP_SERVICE_SKIPPED: ${{ tasks.check-generated-mocks-op-service.skipped }} + TASK_CHECK_NUT_LOCKS_SUCCEEDED: ${{ tasks.check-nut-locks.succeeded }} + TASK_CHECK_NUT_LOCKS_FAILED: ${{ tasks.check-nut-locks.failed }} + TASK_CHECK_NUT_LOCKS_SKIPPED: ${{ tasks.check-nut-locks.skipped }} + TASK_CHECK_OP_GETH_VERSION_SUCCEEDED: ${{ tasks.check-op-geth-version.succeeded }} + TASK_CHECK_OP_GETH_VERSION_FAILED: ${{ tasks.check-op-geth-version.failed }} + TASK_CHECK_OP_GETH_VERSION_SKIPPED: ${{ tasks.check-op-geth-version.skipped }} + TASK_L2_CHAINS_SYNC_CHECK_SUCCEEDED: ${{ tasks.l2-chains-sync-check.succeeded }} + TASK_L2_CHAINS_SYNC_CHECK_FAILED: ${{ tasks.l2-chains-sync-check.failed }} + TASK_L2_CHAINS_SYNC_CHECK_SKIPPED: ${{ tasks.l2-chains-sync-check.skipped }} + TASK_OP_DEPLOYER_FORGE_VERSION_SUCCEEDED: ${{ tasks.op-deployer-forge-version.succeeded }} + TASK_OP_DEPLOYER_FORGE_VERSION_FAILED: ${{ tasks.op-deployer-forge-version.failed }} + TASK_OP_DEPLOYER_FORGE_VERSION_SKIPPED: ${{ tasks.op-deployer-forge-version.skipped }} + TASK_SEMGREP_SCAN_LOCAL_SUCCEEDED: ${{ tasks.semgrep-scan-local.succeeded }} + TASK_SEMGREP_SCAN_LOCAL_FAILED: ${{ tasks.semgrep-scan-local.failed }} + TASK_SEMGREP_SCAN_LOCAL_SKIPPED: ${{ tasks.semgrep-scan-local.skipped }} + TASK_SEMGREP_TEST_SUCCEEDED: ${{ tasks.semgrep-test.succeeded }} + TASK_SEMGREP_TEST_FAILED: ${{ tasks.semgrep-test.failed }} + TASK_SEMGREP_TEST_SKIPPED: ${{ tasks.semgrep-test.skipped }} + TASK_SHELL_CHECK_SUCCEEDED: ${{ tasks.shell-check.succeeded }} + TASK_SHELL_CHECK_FAILED: ${{ tasks.shell-check.failed }} + TASK_SHELL_CHECK_SKIPPED: ${{ tasks.shell-check.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-checks + + # Bind every complete Contracts prerequisite to fresh engine terminal states. + - key: contracts-gate-receipt + use: [code, tools] + after: ${{ (contracts-fast.succeeded || contracts-fast.failed || contracts-fast.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt contracts-checks + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-contracts }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CONTRACTS_FAST_SUCCEEDED: ${{ tasks.contracts-fast.succeeded }} + TASK_CONTRACTS_FAST_FAILED: ${{ tasks.contracts-fast.failed }} + TASK_CONTRACTS_FAST_SKIPPED: ${{ tasks.contracts-fast.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/contracts-checks diff --git a/.rwx/pr-gates.yml b/.rwx/pr-gates.yml new file mode 100644 index 00000000000..f8b2a0ef4a8 --- /dev/null +++ b/.rwx/pr-gates.yml @@ -0,0 +1,616 @@ +# Genuine optional PR gates; Circle continues to own every required gate. +on: + github: + push: + if: ${{ event.git.branch == 'codex/rwx-ci-pilot' || event.git.branch == 'develop' }} + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: ${{ event.git.tag }} + status-checks: + default: + name: optimism-rwx-pr-coordinator + custom: + - name: optimism-pilot + tasks: native-fmt + - name: optimism-rust-shadow + tasks: native-workspace + - name: optimism-op-reth-shadow + tasks: native-op-reth + - name: optimism-rust-gate-shadow + tasks: gate-status + - name: optimism-pr-checks-shadow + tasks: native-pr-checks + - name: optimism-go-tests-shadow + tasks: native-go + - name: optimism-acceptance-shadow + tasks: native-acceptance + - name: optimism-cannon-go-shadow + tasks: native-cannon + - name: optimism-nut-prefork-shadow + tasks: native-nut-prefork + - name: optimism-nut-provenance-shadow + tasks: native-nut-provenance + - name: optimism-fetcher-artifacts-shadow + tasks: native-fetcher + - name: optimism-sp1-guest-shadow + tasks: native-sp1 + - name: optimism-kontrol-build-shadow + tasks: native-kontrol + - name: optimism-main-gate-shadow + tasks: main-gate-status + - name: optimism-contracts-shadow + tasks: native-contracts + - name: optimism-contract-upgrades-shadow + tasks: native-contract-upgrades + - name: optimism-contract-coverage-shadow + tasks: native-contract-coverage + - name: optimism-contract-l2-fork-shadow + tasks: native-contract-l2-fork + - name: optimism-contracts-gate-shadow + tasks: contracts-gate-status + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: helper-tests + use: [code, tools] + run: | + python3 -m unittest discover -s ops/ci -p 'test_pr_gate*.py' -v + python3 -m unittest discover -s ops/ci -p 'test_rwx_local_packages.py' -v + python3 -m unittest discover -s ops/ci -p 'test_ci_report.py' -v + python3 -m unittest discover -s ops/ci -p 'test_compare_ci.py' -v + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: native-fmt + call: ${{ run.dir }}/pilot.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + + - key: native-workspace + call: ${{ run.dir }}/rust.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: v1 + + - key: native-op-reth + call: ${{ run.dir }}/op-reth.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + codec-base-sha: "" + + - key: aggregate + use: [code, tools] + after: ${{ (native-op-reth.succeeded || native-op-reth.failed || native-op-reth.skipped) && (native-fmt.succeeded || native-fmt.failed || native-fmt.skipped) && (native-workspace.succeeded || native-workspace.failed || native-workspace.skipped) }} + if: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.succeeded && tasks.native-fmt.tasks.rust-gate-receipt.succeeded && tasks.native-workspace.tasks.rust-gate-receipt.succeeded }} + run: python3 ops/ci/pr-gate.py aggregate required-rust-ci + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_RUST_FMT_REPORT: ${{ tasks.native-fmt.tasks.rust-gate-receipt.artifacts.receipt }} + GROUP_RUST_FMT_SUCCEEDED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.succeeded }} + GROUP_RUST_FMT_FAILED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.failed }} + GROUP_RUST_FMT_SKIPPED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.skipped }} + GROUP_RUST_WORKSPACE_REPORT: ${{ tasks.native-workspace.tasks.rust-gate-receipt.artifacts.receipt }} + GROUP_RUST_WORKSPACE_SUCCEEDED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.succeeded }} + GROUP_RUST_WORKSPACE_FAILED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.failed }} + GROUP_RUST_WORKSPACE_SKIPPED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.skipped }} + GROUP_OP_RETH_RUST_REPORT: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.artifacts.receipt }} + GROUP_OP_RETH_RUST_SUCCEEDED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.succeeded }} + GROUP_OP_RETH_RUST_FAILED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.failed }} + GROUP_OP_RETH_RUST_SKIPPED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate + + # RWX custom checks fail on skipped tasks. Observe both mutually exclusive + # collectors, then publish one executed verdict with their actual outcomes. + - key: gate-status + use: [code, tools] + after: ${{ (aggregate.succeeded || aggregate.failed || aggregate.skipped) && (gate-failure.succeeded || gate-failure.failed || gate-failure.skipped) }} + run: python3 ops/ci/pr-gate.py status required-rust-ci + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + OBSERVER_AGGREGATE_SUCCEEDED: ${{ tasks.aggregate.succeeded }} + OBSERVER_AGGREGATE_FAILED: ${{ tasks.aggregate.failed }} + OBSERVER_AGGREGATE_SKIPPED: ${{ tasks.aggregate.skipped }} + OBSERVER_GATE_FAILURE_SUCCEEDED: ${{ tasks.gate-failure.succeeded }} + OBSERVER_GATE_FAILURE_FAILED: ${{ tasks.gate-failure.failed }} + OBSERVER_GATE_FAILURE_SKIPPED: ${{ tasks.gate-failure.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: status + path: .ci/pr-gates/status/required-rust-ci + + - key: gate-failure + use: [code, tools] + after: ${{ (native-op-reth.succeeded || native-op-reth.failed || native-op-reth.skipped) && (native-fmt.succeeded || native-fmt.failed || native-fmt.skipped) && (native-workspace.succeeded || native-workspace.failed || native-workspace.skipped) }} + if: ${{ (tasks.native-op-reth.tasks.rust-gate-receipt.failed || tasks.native-op-reth.tasks.rust-gate-receipt.skipped) || (tasks.native-fmt.tasks.rust-gate-receipt.failed || tasks.native-fmt.tasks.rust-gate-receipt.skipped) || (tasks.native-workspace.tasks.rust-gate-receipt.failed || tasks.native-workspace.tasks.rust-gate-receipt.skipped) }} + run: python3 ops/ci/pr-gate.py aggregate required-rust-ci --failed + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_RUST_FMT_SUCCEEDED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.succeeded }} + GROUP_RUST_FMT_FAILED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.failed }} + GROUP_RUST_FMT_SKIPPED: ${{ tasks.native-fmt.tasks.rust-gate-receipt.skipped }} + GROUP_RUST_WORKSPACE_SUCCEEDED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.succeeded }} + GROUP_RUST_WORKSPACE_FAILED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.failed }} + GROUP_RUST_WORKSPACE_SKIPPED: ${{ tasks.native-workspace.tasks.rust-gate-receipt.skipped }} + GROUP_OP_RETH_RUST_SUCCEEDED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.succeeded }} + GROUP_OP_RETH_RUST_FAILED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.failed }} + GROUP_OP_RETH_RUST_SKIPPED: ${{ tasks.native-op-reth.tasks.rust-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate + + - key: native-pr-checks + call: ${{ run.dir }}/pr-checks.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-go + call: ${{ run.dir }}/go-tests.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + build-probe: "" + target-cache-mode: "keep" + shard-total: "12" + test-parallel: "8" + + - key: native-acceptance + call: ${{ run.dir }}/acceptance.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + build-probe: "" + target-cache-mode: "keep" + shard-total: "8" + + - key: native-cannon + call: ${{ run.dir }}/cannon-go.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-nut-prefork + call: ${{ run.dir }}/nut-prefork.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-nut-provenance + call: ${{ run.dir }}/nut-provenance.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-fetcher + call: ${{ run.dir }}/fetcher-artifacts.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-sp1 + call: ${{ run.dir }}/sp1-guest.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + target-cache-mode: "keep" + + - key: native-kontrol + call: ${{ run.dir }}/kontrol-build.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: main-aggregate + use: [code, tools] + after: ${{ (native-acceptance.succeeded || native-acceptance.failed || native-acceptance.skipped) && (native-cannon.succeeded || native-cannon.failed || native-cannon.skipped) && (native-pr-checks.succeeded || native-pr-checks.failed || native-pr-checks.skipped) && (native-fetcher.succeeded || native-fetcher.failed || native-fetcher.skipped) && (native-go.succeeded || native-go.failed || native-go.skipped) && (native-kontrol.succeeded || native-kontrol.failed || native-kontrol.skipped) && (native-nut-prefork.succeeded || native-nut-prefork.failed || native-nut-prefork.skipped) && (native-nut-provenance.succeeded || native-nut-provenance.failed || native-nut-provenance.skipped) && (native-fmt.succeeded || native-fmt.failed || native-fmt.skipped) && (native-sp1.succeeded || native-sp1.failed || native-sp1.skipped) }} + if: ${{ tasks.native-acceptance.tasks.main-gate-receipt.succeeded && tasks.native-cannon.tasks.main-gate-receipt.succeeded && tasks.native-pr-checks.tasks.main-gate-receipt.succeeded && tasks.native-fetcher.tasks.main-gate-receipt.succeeded && tasks.native-go.tasks.main-gate-receipt.succeeded && tasks.native-kontrol.tasks.main-gate-receipt.succeeded && tasks.native-nut-prefork.tasks.main-gate-receipt.succeeded && tasks.native-nut-provenance.tasks.main-gate-receipt.succeeded && tasks.native-fmt.tasks.main-gate-receipt.succeeded && tasks.native-sp1.tasks.main-gate-receipt.succeeded }} + run: python3 ops/ci/pr-gate.py aggregate ci-gate + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_MAIN_ACCEPTANCE_REPORT: ${{ tasks.native-acceptance.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_ACCEPTANCE_SUCCEEDED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_ACCEPTANCE_FAILED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.failed }} + GROUP_MAIN_ACCEPTANCE_SKIPPED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_CANNON_REPORT: ${{ tasks.native-cannon.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_CANNON_SUCCEEDED: ${{ tasks.native-cannon.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_CANNON_FAILED: ${{ tasks.native-cannon.tasks.main-gate-receipt.failed }} + GROUP_MAIN_CANNON_SKIPPED: ${{ tasks.native-cannon.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_CHECKS_REPORT: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_CHECKS_SUCCEEDED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_CHECKS_FAILED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.failed }} + GROUP_MAIN_CHECKS_SKIPPED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_FETCHER_REPORT: ${{ tasks.native-fetcher.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_FETCHER_SUCCEEDED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_FETCHER_FAILED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.failed }} + GROUP_MAIN_FETCHER_SKIPPED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_GO_REPORT: ${{ tasks.native-go.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_GO_SUCCEEDED: ${{ tasks.native-go.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_GO_FAILED: ${{ tasks.native-go.tasks.main-gate-receipt.failed }} + GROUP_MAIN_GO_SKIPPED: ${{ tasks.native-go.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_KONTROL_REPORT: ${{ tasks.native-kontrol.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_KONTROL_SUCCEEDED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_KONTROL_FAILED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.failed }} + GROUP_MAIN_KONTROL_SKIPPED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_NUT_PREFORK_REPORT: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_NUT_PREFORK_SUCCEEDED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_NUT_PREFORK_FAILED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.failed }} + GROUP_MAIN_NUT_PREFORK_SKIPPED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_NUT_PROVENANCE_REPORT: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_NUT_PROVENANCE_SUCCEEDED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_NUT_PROVENANCE_FAILED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.failed }} + GROUP_MAIN_NUT_PROVENANCE_SKIPPED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_PILOT_REPORT: ${{ tasks.native-fmt.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_PILOT_SUCCEEDED: ${{ tasks.native-fmt.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_PILOT_FAILED: ${{ tasks.native-fmt.tasks.main-gate-receipt.failed }} + GROUP_MAIN_PILOT_SKIPPED: ${{ tasks.native-fmt.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_SP1_REPORT: ${{ tasks.native-sp1.tasks.main-gate-receipt.artifacts.receipt }} + GROUP_MAIN_SP1_SUCCEEDED: ${{ tasks.native-sp1.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_SP1_FAILED: ${{ tasks.native-sp1.tasks.main-gate-receipt.failed }} + GROUP_MAIN_SP1_SKIPPED: ${{ tasks.native-sp1.tasks.main-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate + + - key: main-gate-failure + use: [code, tools] + after: ${{ (native-acceptance.succeeded || native-acceptance.failed || native-acceptance.skipped) && (native-cannon.succeeded || native-cannon.failed || native-cannon.skipped) && (native-pr-checks.succeeded || native-pr-checks.failed || native-pr-checks.skipped) && (native-fetcher.succeeded || native-fetcher.failed || native-fetcher.skipped) && (native-go.succeeded || native-go.failed || native-go.skipped) && (native-kontrol.succeeded || native-kontrol.failed || native-kontrol.skipped) && (native-nut-prefork.succeeded || native-nut-prefork.failed || native-nut-prefork.skipped) && (native-nut-provenance.succeeded || native-nut-provenance.failed || native-nut-provenance.skipped) && (native-fmt.succeeded || native-fmt.failed || native-fmt.skipped) && (native-sp1.succeeded || native-sp1.failed || native-sp1.skipped) }} + if: ${{ (tasks.native-acceptance.tasks.main-gate-receipt.failed || tasks.native-acceptance.tasks.main-gate-receipt.skipped) || (tasks.native-cannon.tasks.main-gate-receipt.failed || tasks.native-cannon.tasks.main-gate-receipt.skipped) || (tasks.native-pr-checks.tasks.main-gate-receipt.failed || tasks.native-pr-checks.tasks.main-gate-receipt.skipped) || (tasks.native-fetcher.tasks.main-gate-receipt.failed || tasks.native-fetcher.tasks.main-gate-receipt.skipped) || (tasks.native-go.tasks.main-gate-receipt.failed || tasks.native-go.tasks.main-gate-receipt.skipped) || (tasks.native-kontrol.tasks.main-gate-receipt.failed || tasks.native-kontrol.tasks.main-gate-receipt.skipped) || (tasks.native-nut-prefork.tasks.main-gate-receipt.failed || tasks.native-nut-prefork.tasks.main-gate-receipt.skipped) || (tasks.native-nut-provenance.tasks.main-gate-receipt.failed || tasks.native-nut-provenance.tasks.main-gate-receipt.skipped) || (tasks.native-fmt.tasks.main-gate-receipt.failed || tasks.native-fmt.tasks.main-gate-receipt.skipped) || (tasks.native-sp1.tasks.main-gate-receipt.failed || tasks.native-sp1.tasks.main-gate-receipt.skipped) }} + run: python3 ops/ci/pr-gate.py aggregate ci-gate --failed + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_MAIN_ACCEPTANCE_SUCCEEDED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_ACCEPTANCE_FAILED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.failed }} + GROUP_MAIN_ACCEPTANCE_SKIPPED: ${{ tasks.native-acceptance.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_CANNON_SUCCEEDED: ${{ tasks.native-cannon.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_CANNON_FAILED: ${{ tasks.native-cannon.tasks.main-gate-receipt.failed }} + GROUP_MAIN_CANNON_SKIPPED: ${{ tasks.native-cannon.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_CHECKS_SUCCEEDED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_CHECKS_FAILED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.failed }} + GROUP_MAIN_CHECKS_SKIPPED: ${{ tasks.native-pr-checks.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_FETCHER_SUCCEEDED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_FETCHER_FAILED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.failed }} + GROUP_MAIN_FETCHER_SKIPPED: ${{ tasks.native-fetcher.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_GO_SUCCEEDED: ${{ tasks.native-go.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_GO_FAILED: ${{ tasks.native-go.tasks.main-gate-receipt.failed }} + GROUP_MAIN_GO_SKIPPED: ${{ tasks.native-go.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_KONTROL_SUCCEEDED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_KONTROL_FAILED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.failed }} + GROUP_MAIN_KONTROL_SKIPPED: ${{ tasks.native-kontrol.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_NUT_PREFORK_SUCCEEDED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_NUT_PREFORK_FAILED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.failed }} + GROUP_MAIN_NUT_PREFORK_SKIPPED: ${{ tasks.native-nut-prefork.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_NUT_PROVENANCE_SUCCEEDED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_NUT_PROVENANCE_FAILED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.failed }} + GROUP_MAIN_NUT_PROVENANCE_SKIPPED: ${{ tasks.native-nut-provenance.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_PILOT_SUCCEEDED: ${{ tasks.native-fmt.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_PILOT_FAILED: ${{ tasks.native-fmt.tasks.main-gate-receipt.failed }} + GROUP_MAIN_PILOT_SKIPPED: ${{ tasks.native-fmt.tasks.main-gate-receipt.skipped }} + GROUP_MAIN_SP1_SUCCEEDED: ${{ tasks.native-sp1.tasks.main-gate-receipt.succeeded }} + GROUP_MAIN_SP1_FAILED: ${{ tasks.native-sp1.tasks.main-gate-receipt.failed }} + GROUP_MAIN_SP1_SKIPPED: ${{ tasks.native-sp1.tasks.main-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate + + - key: main-gate-status + use: [code, tools] + after: ${{ (main-aggregate.succeeded || main-aggregate.failed || main-aggregate.skipped) && (main-gate-failure.succeeded || main-gate-failure.failed || main-gate-failure.skipped) }} + run: python3 ops/ci/pr-gate.py status ci-gate + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + OBSERVER_AGGREGATE_SUCCEEDED: ${{ tasks.main-aggregate.succeeded }} + OBSERVER_AGGREGATE_FAILED: ${{ tasks.main-aggregate.failed }} + OBSERVER_AGGREGATE_SKIPPED: ${{ tasks.main-aggregate.skipped }} + OBSERVER_GATE_FAILURE_SUCCEEDED: ${{ tasks.main-gate-failure.succeeded }} + OBSERVER_GATE_FAILURE_FAILED: ${{ tasks.main-gate-failure.failed }} + OBSERVER_GATE_FAILURE_SKIPPED: ${{ tasks.main-gate-failure.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: status + path: .ci/pr-gates/status/ci-gate + + # Contracts shares the existing fast-check child and executes all matrices once. + - key: native-contracts + call: ${{ run.dir }}/contracts.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + build-probe: "" + + - key: native-contract-upgrades + call: ${{ run.dir }}/contract-upgrades.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-contract-coverage + call: ${{ run.dir }}/contract-coverage.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + + - key: native-contract-l2-fork + call: ${{ run.dir }}/contract-l2-fork.yml + init: + commit-sha: ${{ init.commit-sha }} + branch: ${{ init.branch }} + tag: ${{ init.tag }} + cache-warm: "false" + cache-epoch: "v1" + fork-block: "latest" + + - key: contracts-aggregate + use: [code, tools] + after: ${{ (native-pr-checks.succeeded || native-pr-checks.failed || native-pr-checks.skipped) && (native-contract-coverage.succeeded || native-contract-coverage.failed || native-contract-coverage.skipped) && (native-contract-l2-fork.succeeded || native-contract-l2-fork.failed || native-contract-l2-fork.skipped) && (native-contracts.succeeded || native-contracts.failed || native-contracts.skipped) && (native-contract-upgrades.succeeded || native-contract-upgrades.failed || native-contract-upgrades.skipped) }} + if: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.succeeded && tasks.native-contract-coverage.tasks.contracts-gate-receipt.succeeded && tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.succeeded && tasks.native-contracts.tasks.contracts-gate-receipt.succeeded && tasks.native-contract-upgrades.tasks.contracts-gate-receipt.succeeded }} + run: python3 ops/ci/pr-gate.py aggregate required-contracts-ci + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_CONTRACTS_CHECKS_REPORT: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.artifacts.receipt }} + GROUP_CONTRACTS_CHECKS_SUCCEEDED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_CHECKS_FAILED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_CHECKS_SKIPPED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_COVERAGE_REPORT: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.artifacts.receipt }} + GROUP_CONTRACTS_COVERAGE_SUCCEEDED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_COVERAGE_FAILED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_COVERAGE_SKIPPED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_L2_FORK_REPORT: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.artifacts.receipt }} + GROUP_CONTRACTS_L2_FORK_SUCCEEDED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_L2_FORK_FAILED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_L2_FORK_SKIPPED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_SUITES_REPORT: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.artifacts.receipt }} + GROUP_CONTRACTS_SUITES_SUCCEEDED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_SUITES_FAILED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_SUITES_SKIPPED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_UPGRADES_REPORT: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.artifacts.receipt }} + GROUP_CONTRACTS_UPGRADES_SUCCEEDED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_UPGRADES_FAILED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_UPGRADES_SKIPPED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate/required-contracts-ci + + - key: contracts-gate-failure + use: [code, tools] + after: ${{ (native-pr-checks.succeeded || native-pr-checks.failed || native-pr-checks.skipped) && (native-contract-coverage.succeeded || native-contract-coverage.failed || native-contract-coverage.skipped) && (native-contract-l2-fork.succeeded || native-contract-l2-fork.failed || native-contract-l2-fork.skipped) && (native-contracts.succeeded || native-contracts.failed || native-contracts.skipped) && (native-contract-upgrades.succeeded || native-contract-upgrades.failed || native-contract-upgrades.skipped) }} + if: ${{ (tasks.native-pr-checks.tasks.contracts-gate-receipt.failed || tasks.native-pr-checks.tasks.contracts-gate-receipt.skipped) || (tasks.native-contract-coverage.tasks.contracts-gate-receipt.failed || tasks.native-contract-coverage.tasks.contracts-gate-receipt.skipped) || (tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.failed || tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.skipped) || (tasks.native-contracts.tasks.contracts-gate-receipt.failed || tasks.native-contracts.tasks.contracts-gate-receipt.skipped) || (tasks.native-contract-upgrades.tasks.contracts-gate-receipt.failed || tasks.native-contract-upgrades.tasks.contracts-gate-receipt.skipped) }} + run: python3 ops/ci/pr-gate.py aggregate required-contracts-ci --failed + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GATE_ROUTING: ${{ tasks.route.artifacts.routing }} + GROUP_CONTRACTS_CHECKS_SUCCEEDED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_CHECKS_FAILED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_CHECKS_SKIPPED: ${{ tasks.native-pr-checks.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_COVERAGE_SUCCEEDED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_COVERAGE_FAILED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_COVERAGE_SKIPPED: ${{ tasks.native-contract-coverage.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_L2_FORK_SUCCEEDED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_L2_FORK_FAILED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_L2_FORK_SKIPPED: ${{ tasks.native-contract-l2-fork.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_SUITES_SUCCEEDED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_SUITES_FAILED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_SUITES_SKIPPED: ${{ tasks.native-contracts.tasks.contracts-gate-receipt.skipped }} + GROUP_CONTRACTS_UPGRADES_SUCCEEDED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.succeeded }} + GROUP_CONTRACTS_UPGRADES_FAILED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.failed }} + GROUP_CONTRACTS_UPGRADES_SKIPPED: ${{ tasks.native-contract-upgrades.tasks.contracts-gate-receipt.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/pr-gates/aggregate/required-contracts-ci + + - key: contracts-gate-status + use: [code, tools] + after: ${{ (contracts-aggregate.succeeded || contracts-aggregate.failed || contracts-aggregate.skipped) && (contracts-gate-failure.succeeded || contracts-gate-failure.failed || contracts-gate-failure.skipped) }} + run: python3 ops/ci/pr-gate.py status required-contracts-ci + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + OBSERVER_AGGREGATE_SUCCEEDED: ${{ tasks.contracts-aggregate.succeeded }} + OBSERVER_AGGREGATE_FAILED: ${{ tasks.contracts-aggregate.failed }} + OBSERVER_AGGREGATE_SKIPPED: ${{ tasks.contracts-aggregate.skipped }} + OBSERVER_GATE_FAILURE_SUCCEEDED: ${{ tasks.contracts-gate-failure.succeeded }} + OBSERVER_GATE_FAILURE_FAILED: ${{ tasks.contracts-gate-failure.failed }} + OBSERVER_GATE_FAILURE_SKIPPED: ${{ tasks.contracts-gate-failure.skipped }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: status + path: .ci/pr-gates/status/required-contracts-ci diff --git a/.rwx/rust-e2e.yml b/.rwx/rust-e2e.yml new file mode 100644 index 00000000000..6aa4bb56fa8 --- /dev/null +++ b/.rwx/rust-e2e.yml @@ -0,0 +1,1001 @@ +# Full Rust E2E shadows. CircleCI retains all required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [go, contracts, release, prestate, compile-proof, compile-restart, compile-simple-kona, compile-simple-kona-sequencer, compile-op-reth] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + github: + push: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: ${{ event.git.tag }} + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + status-checks: + name: optimism-rust-e2e-shadow + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + build-probe: "" + target-cache-mode: keep + +# Cache-only vault, writable by develop and the temporary pilot branch. +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: go-build-tools + use: tools + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-foundry.yml + with: + mode: anvil + + - key: rust-tools + use: [bootstrap-inputs, tools] + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: e2e + + - key: helper-tests + use: [code, go-build-tools, contract-tools] + run: | + bash .circleci/scripts/apt-install.sh zstd + RWX_LIVE_GO_FIXTURE=1 RWX_LIVE_FORGE_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_rust_e2e.py' + python3 -m unittest discover -s ops/ci -p 'test_rust_e2e_release.py' + python3 -m unittest discover -s ops/ci -p 'test_rust_e2e_gate.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_rust_e2e.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_contract_artifacts.py' + python3 -m unittest discover -s ops/ci -p 'test_go_artifacts.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: source + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + git submodule sync --recursive + git submodule update --init --recursive --jobs 8 + git rev-parse HEAD >packages/contracts-bedrock/.gitcommit + timeout: 20m + + - key: rust-dependencies + use: [source, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + export CARGO_HOME="$PWD/.ci/rust-cache/cargo" + cd rust + attempt=0 + until cargo fetch --locked; do + attempt=$((attempt + 1)) + if [[ "$attempt" -ge 5 ]]; then exit 1; fi + sleep "$((2 ** attempt))" + done + filter: [rust, mise.toml] + tool-cache: op-reth-dependencies-${{ init.cache-epoch }} + timeout: 30m + outputs: + filesystem: + filter: + workspace: [.ci/rust-cache/cargo] + system: [] + + - key: head-source + use: [source, rust-dependencies] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: 'true' + + - key: go + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + call: ${{ run.dir }}/packages/go-go.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: contracts + use: [source, contract-tools, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + call: ${{ run.dir }}/packages/rust-e2e-contracts.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: release + use: [rust-tools, head-source] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + call: ${{ run.dir }}/packages/rust-e2e-release.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + + - key: prestate + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + call: ${{ run.dir }}/packages/go-prestate.yml + with: + commit-sha: ${{ init.commit-sha }} + cache-epoch: ${{ init.cache-epoch }} + build-probe: ${{ init.build-probe }} + target-cache-mode: ${{ init.target-cache-mode }} + + - key: runtime-tools + use: [go-build-tools, contract-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + mise install go:github.com/ethereum/go-ethereum/cmd/geth + printf '%s\n' "$(mise bin-paths | paste -sd: -):$PATH" >"$RWX_ENV/PATH" + timeout: 30m + + - key: compile-proof + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + bash ops/ci/rust-e2e.sh compile proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_E2E_PROVIDER: rwx + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-go-compile-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/compile] + system: [] + artifacts: + - key: compiled + path: .ci/rust-e2e/compiled/proof + + - key: compile-restart + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + bash ops/ci/rust-e2e.sh compile restart + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_E2E_PROVIDER: rwx + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-go-compile-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/compile] + system: [] + artifacts: + - key: compiled + path: .ci/rust-e2e/compiled/restart + + - key: compile-simple-kona + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + bash ops/ci/rust-e2e.sh compile simple-kona + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_E2E_PROVIDER: rwx + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-go-compile-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/compile] + system: [] + artifacts: + - key: compiled + path: .ci/rust-e2e/compiled/simple-kona + + - key: compile-simple-kona-sequencer + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + bash ops/ci/rust-e2e.sh compile simple-kona-sequencer + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_E2E_PROVIDER: rwx + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-go-compile-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/compile] + system: [] + artifacts: + - key: compiled + path: .ci/rust-e2e/compiled/simple-kona-sequencer + + - key: compile-op-reth + use: [source, go-build-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-e2e-ci == 'true' }} + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + bash ops/ci/rust-e2e.sh compile op-reth + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_E2E_PROVIDER: rwx + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-go-compile-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/compile] + system: [] + artifacts: + - key: compiled + path: .ci/rust-e2e/compiled/op-reth + + # Run/attempt identity forces fresh verdicts while preserving runtime Go caches. + - key: verdict-proof-0 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "0" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-1 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "1" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-2 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "2" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-3 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "3" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-4 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "4" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-5 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "5" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-6 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "6" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-proof-7 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run proof + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "7" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-proof.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-proof-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/proof/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/proof + + - key: verdict-restart-0 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + python3 ops/ci/go-artifacts.py restore prestate "$PRESTATE_ARTIFACT" + bash ops/ci/rust-e2e.sh run restart + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "0" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-restart.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + PRESTATE_ARTIFACT: ${{ tasks.prestate.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-restart-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/restart/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/restart + + - key: verdict-restart-1 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + python3 ops/ci/go-artifacts.py restore prestate "$PRESTATE_ARTIFACT" + bash ops/ci/rust-e2e.sh run restart + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "1" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-restart.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + PRESTATE_ARTIFACT: ${{ tasks.prestate.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-restart-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/restart/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/restart + + - key: verdict-restart-2 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + python3 ops/ci/go-artifacts.py restore prestate "$PRESTATE_ARTIFACT" + bash ops/ci/rust-e2e.sh run restart + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "2" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-restart.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + PRESTATE_ARTIFACT: ${{ tasks.prestate.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-restart-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/restart/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/restart + + - key: verdict-simple-kona-0 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + python3 ops/ci/go-artifacts.py restore prestate "$PRESTATE_ARTIFACT" + bash ops/ci/rust-e2e.sh run simple-kona + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "0" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-simple-kona.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + PRESTATE_ARTIFACT: ${{ tasks.prestate.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-simple-kona-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/simple-kona/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/simple-kona + + - key: verdict-simple-kona-sequencer-0 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + python3 ops/ci/go-artifacts.py restore prestate "$PRESTATE_ARTIFACT" + bash ops/ci/rust-e2e.sh run simple-kona-sequencer + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "0" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-simple-kona-sequencer.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + PRESTATE_ARTIFACT: ${{ tasks.prestate.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-simple-kona-sequencer-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/simple-kona-sequencer/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/simple-kona-sequencer + + - key: verdict-op-reth-0 + use: [source, runtime-tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + docker: true + run: | + python3 ops/ci/go-artifacts.py restore go "$GO_ARTIFACT" + python3 ops/ci/go-artifacts.py restore contracts-e2e "$CONTRACT_ARTIFACT" + python3 ops/ci/go-artifacts.py restore rust-e2e-release "$RELEASE_ARTIFACT" + bash ops/ci/rust-e2e.sh run op-reth + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_E2E_PROVIDER: rwx + CI_SHARD_INDEX: "0" + PARALLEL: "8" + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + CI_E2E_COMPILED: ${{ tasks.compile-op-reth.artifacts.compiled }} + GO_ARTIFACT: ${{ tasks.go.tasks.build.artifacts.dependency }} + CONTRACT_ARTIFACT: ${{ tasks.contracts.tasks.build.artifacts.dependency }} + RELEASE_ARTIFACT: ${{ tasks.release.tasks.build.artifacts.dependency }} + tool-cache: rust-e2e-runtime-op-reth-${{ init.cache-epoch }} + timeout: 60m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/go-cache/rust-e2e/run] + system: [] + test-results: + - path: .ci/rust-e2e/reports/op-reth/native.json + options: + language: Go + framework: go test + artifacts: + - key: reports + path: .ci/rust-e2e/reports/op-reth + + - key: required-rust-e2e + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + after: [release, verdict-proof-0, verdict-proof-1, verdict-proof-2, verdict-proof-3, verdict-proof-4, verdict-proof-5, verdict-proof-6, verdict-proof-7, verdict-restart-0, verdict-restart-1, verdict-restart-2, verdict-simple-kona-0, verdict-simple-kona-sequencer-0, verdict-op-reth-0] + run: | + reports=() + for index in 0 1 2 3 4 5 6 7 8 9 10 11 12 13; do + name="REPORT_$index" + reports+=("${!name}") + done + python3 ops/ci/rust-e2e-gate.py --release "$RELEASE_REPORT" --sha "$CI_COMMIT_SHA" \ + --output .ci/rust-e2e/gate.json "${reports[@]}" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + RELEASE_REPORT: ${{ tasks.release.tasks.build.artifacts.reports }} + REPORT_0: ${{ tasks.verdict-proof-0.artifacts.reports }} + REPORT_1: ${{ tasks.verdict-proof-1.artifacts.reports }} + REPORT_2: ${{ tasks.verdict-proof-2.artifacts.reports }} + REPORT_3: ${{ tasks.verdict-proof-3.artifacts.reports }} + REPORT_4: ${{ tasks.verdict-proof-4.artifacts.reports }} + REPORT_5: ${{ tasks.verdict-proof-5.artifacts.reports }} + REPORT_6: ${{ tasks.verdict-proof-6.artifacts.reports }} + REPORT_7: ${{ tasks.verdict-proof-7.artifacts.reports }} + REPORT_8: ${{ tasks.verdict-restart-0.artifacts.reports }} + REPORT_9: ${{ tasks.verdict-restart-1.artifacts.reports }} + REPORT_10: ${{ tasks.verdict-restart-2.artifacts.reports }} + REPORT_11: ${{ tasks.verdict-simple-kona-0.artifacts.reports }} + REPORT_12: ${{ tasks.verdict-simple-kona-sequencer-0.artifacts.reports }} + REPORT_13: ${{ tasks.verdict-op-reth-0.artifacts.reports }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: gate + path: .ci/rust-e2e/gate.json + + # The overall optional check fails on a build failure even if no consumer ran. + # RWX cancels all tasks when the run is canceled; this task cannot turn it green. + - key: required-rust-e2e-failure + use: [code, tools] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-rust-e2e-ci == 'true' }} + after: ${{ release.failed || verdict-proof-0.failed || verdict-proof-1.failed || verdict-proof-2.failed || verdict-proof-3.failed || verdict-proof-4.failed || verdict-proof-5.failed || verdict-proof-6.failed || verdict-proof-7.failed || verdict-restart-0.failed || verdict-restart-1.failed || verdict-restart-2.failed || verdict-simple-kona-0.failed || verdict-simple-kona-sequencer-0.failed || verdict-op-reth-0.failed }} + run: | + mkdir -p .ci/rust-e2e + printf '%s\n' '{"passed":false,"reason":"E2E dependency or verdict failed or was aborted"}' >.ci/rust-e2e/gate-failure.json + exit 1 + cache: false + outputs: + filesystem: false + artifacts: + - key: gate + path: .ci/rust-e2e/gate-failure.json diff --git a/.rwx/rust.yml b/.rwx/rust.yml new file mode 100644 index 00000000000..f4fb05345a5 --- /dev/null +++ b/.rwx/rust.yml @@ -0,0 +1,838 @@ +# Rust workspace builds, tests, cross-compilation and source checks. Circle retains required gates. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [tests-build, build, clippy, docs, no-std, udeps, feature-plan, features-0, features-1, features-2, features-3, features-4, features-5, features-6, features-7, features-8, features-9, wasm-unknown, wasm-wasi, registry, cannon-env, cannon-go, cannon-witness, cannon-build] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-epoch: v1 + cache-warm: "true" + # Automatic pushes are coordinated by pr-gates.yml; CLI and warming remain. + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-epoch: v1 + cache-warm: "false" + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +# Cache-only vault: no publishing credentials. During the pilot, only develop +# and codex/rwx-ci-pilot may write. Remove the temporary pilot grant at promotion. +tool-cache: + vault: optimism-op-reth-shadow + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + # Artifact dependencies avoid inheriting the checkout's Git history into + # reusable tools. Language toolchains are added only by their own workload. + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: helper-tests + use: [code, tools] + run: | + python3 -m unittest discover -s ops/ci -p 'test_rust_workspace.py' + python3 -m unittest discover -s ops/ci -p 'test_rust_target_cache.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_rust_extra.py' + python3 -m unittest discover -s ops/ci -p 'test_rust_cannon.py' + python3 -m unittest discover -s ops/ci -p 'test_compare_rust_cannon.py' + cache: false + timeout: 10m + outputs: + filesystem: false + + - key: rust-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: workspace + + - key: wasm-tools + use: rust-tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: rustup target add wasm32-unknown-unknown wasm32-wasip1 + timeout: 30m + + - key: lint-tools + use: rust-tools + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: | + mise install github:ggwpez/zepter github:crate-ci/typos + printf '%s:%s\n' "$(mise bin-paths | paste -sd: -)" "$PATH" >"$RWX_ENV/PATH" + timeout: 30m + + - key: go-tools + use: rust-tools + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + call: ${{ run.dir }}/packages/toolchain-go.yml + with: + mode: go + + - key: rust-dependencies + use: [code, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: | + export CARGO_HOME="$PWD/.ci/rust-cache/cargo" + cd rust + attempt=0 + until cargo fetch --locked; do + attempt=$((attempt + 1)) + if [[ "$attempt" -ge 5 ]]; then exit 1; fi + sleep "$((2 ** attempt))" + done + filter: [rust, mise.toml] + tool-cache: op-reth-dependencies-${{ init.cache-epoch }} + timeout: 30m + outputs: + filesystem: + filter: + workspace: [.ci/rust-cache/cargo] + system: [] + + # Keep full source, Git metadata and the pinned submodule as build inputs. + - key: head-source + use: [code, rust-dependencies] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash /usr/local/lib/optimism-ci/op-reth-shadow.sh source + timeout: 15m + + - key: feature-plan + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh feature-plan + env: &rust-env + CI_RUST_PROVIDER: rwx + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 15m + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/rust-workspace/feature-plan + + - key: tests-build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh tests-build + env: *rust-env + tool-cache: rust-workspace-tests-build-${{ init.cache-epoch }} + timeout: 90m + runner: &build-runner + cpus: 16 + memory: 32gb + outputs: + filesystem: &compiler-output + filter: + workspace: [rust/target, .ci/rust-cache, rust/op-reth/crates/chainspec/res/superchain-configs.tar] + system: [] + artifacts: + - key: report + path: .ci/rust-workspace/tests-build + + # cache:false also disables tool caches. Include run/attempt identity instead + # so every verdict executes while compiler-only layers remain reusable. + - key: tests + use: [tests-build, head-source] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh tests + env: + CI_RUST_PROVIDER: rwx + CI_COMMIT_SHA: ${{ init.commit-sha }} + TEST_ARCHIVE: ${{ tasks.tests-build.artifacts.report }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + tool-cache: rust-workspace-tests-runtime-${{ init.cache-epoch }} + timeout: 90m + runner: *build-runner + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/tests/junit.xml + - path: .ci/rust-workspace/tests/beacon.junit.xml + - path: .ci/rust-workspace/tests/doctests.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/tests + + - key: doctest + use: head-source + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh doctest + env: &fresh-rust-env + <<: *rust-env + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + tool-cache: rust-workspace-doctest-${{ init.cache-epoch }} + timeout: 90m + runner: *build-runner + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/doctest/doctests.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/doctest + + - key: build + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh build + env: *fresh-rust-env + tool-cache: rust-workspace-build-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/build + + - key: docs + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh docs + env: *fresh-rust-env + tool-cache: rust-workspace-docs-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/docs + + - key: clippy + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh clippy + env: *fresh-rust-env + tool-cache: rust-workspace-clippy-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/clippy + + - key: no-std + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh no-std + env: *fresh-rust-env + tool-cache: rust-workspace-no-std-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/no-std + + - key: udeps + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh udeps + env: *fresh-rust-env + tool-cache: rust-workspace-udeps-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/udeps + + - key: features-0 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "0" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-0-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-0 + + - key: features-1 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "1" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-1-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-1 + + - key: features-2 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "2" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-2-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-2 + + - key: features-3 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "3" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-3-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-3 + + - key: features-4 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "4" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-4-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-4 + + - key: features-5 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "5" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-5-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-5 + + - key: features-6 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "6" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-6-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-6 + + - key: features-7 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "7" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-7-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-7 + + - key: features-8 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "8" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-8-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-8 + + - key: features-9 + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh features + env: + <<: *fresh-rust-env + CI_RUST_PARTITION_INDEX: "9" + CI_RUST_PARTITION_TOTAL: "10" + tool-cache: rust-workspace-features-9-${{ init.cache-epoch }} + timeout: 120m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/features-9 + + - key: wasm-unknown + use: [head-source, wasm-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh wasm-unknown + env: *fresh-rust-env + tool-cache: rust-workspace-wasm-unknown-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/wasm-unknown/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/wasm-unknown + + - key: wasm-wasi + use: [head-source, wasm-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh wasm-wasi + env: *fresh-rust-env + tool-cache: rust-workspace-wasm-wasi-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/wasm-wasi/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/wasm-wasi + + - key: zepter + use: [head-source, lint-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh zepter + env: *fresh-rust-env + tool-cache: rust-workspace-zepter-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/zepter/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/zepter + + - key: typos + use: [head-source, lint-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh typos + env: *fresh-rust-env + tool-cache: rust-workspace-typos-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 2 + memory: 8gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/typos/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/typos + + - key: registry + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh registry + env: *fresh-rust-env + tool-cache: rust-workspace-registry-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/registry/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/registry + + - key: interop + use: [head-source, go-tools] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-workspace.sh interop + env: *fresh-rust-env + tool-cache: rust-workspace-interop-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 4 + memory: 8gb + outputs: + filesystem: + filter: + workspace: [rust/target, .ci/rust-cache, .ci/interop-go, rust/op-reth/crates/chainspec/res/superchain-configs.tar] + system: [] + test-results: + - path: .ci/rust-workspace/interop/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/interop + + # Preserve Docker/BuildKit data through the normal use dependency, without + # exporting image tarballs. The original recipe builds all configured ELFs. + - key: cannon-env + use: [head-source, cannon-witness] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh env + env: *rust-env + docker: preserve-data + tool-cache: rust-cannon-env-${{ init.cache-epoch }} + timeout: 90m + runner: *build-runner + outputs: + filesystem: *compiler-output + artifacts: + - key: report + path: .ci/rust-workspace/cannon-env + + - key: cannon-go + use: [head-source, go-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh go + env: *rust-env + tool-cache: rust-cannon-go-${{ init.cache-epoch }} + timeout: 30m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [cannon/bin, cannon/multicannon/embeds, .ci/cannon-cache/go] + system: [] + artifacts: + - key: report + path: .ci/rust-workspace/cannon-go + + # Fail a missing/corrupt public fixture before the expensive environment build. + - key: cannon-witness + use: head-source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh witness + env: *rust-env + timeout: 15m + outputs: + filesystem: + filter: + workspace: [rust/kona/bin/client/testdata/*.tar.zst, rust/kona/bin/client/testdata/*.etag] + system: [] + artifacts: + - key: report + path: .ci/rust-workspace/cannon-witness + + - key: cannon-lint + use: [head-source, cannon-env] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh lint + env: + <<: *fresh-rust-env + CANNON_ENV_ARTIFACT: ${{ tasks.cannon-env.artifacts.report }} + docker: preserve-data + tool-cache: rust-cannon-lint-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/cannon-lint/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/cannon-lint + + - key: cannon-build + use: [head-source, cannon-env] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh build + env: + <<: *fresh-rust-env + CANNON_ENV_ARTIFACT: ${{ tasks.cannon-env.artifacts.report }} + docker: preserve-data + tool-cache: rust-cannon-build-${{ init.cache-epoch }} + timeout: 90m + runner: *build-runner + outputs: + filesystem: *compiler-output + test-results: + - path: .ci/rust-workspace/cannon-build/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/cannon-build + + - key: cannon-offline + use: [head-source, go-tools, cannon-witness, cannon-go, cannon-build] + if: ${{ tasks.route.values.run-rust-ci == 'true' }} + run: bash ops/ci/rust-cannon.sh offline + env: + <<: *fresh-rust-env + CANNON_GO_ARTIFACT: ${{ tasks.cannon-go.artifacts.report }} + CANNON_WITNESS_ARTIFACT: ${{ tasks.cannon-witness.artifacts.report }} + CANNON_BUILD_ARTIFACT: ${{ tasks.cannon-build.artifacts.report }} + docker: preserve-data + tool-cache: rust-cannon-offline-${{ init.cache-epoch }} + timeout: 90m + runner: *build-runner + outputs: + filesystem: + filter: + workspace: [rust/target, .ci/rust-cache, .ci/cannon-cache] + system: [] + test-results: + - path: .ci/rust-workspace/cannon-offline/checks.junit.xml + artifacts: + - key: report + path: .ci/rust-workspace/cannon-offline + + # Terminal task states preserve exact Rust dependencies without Main-only failures. + - key: rust-gate-receipt + use: [code, tools] + if: ${{ init.cache-warm != 'true' }} + after: ${{ (build.succeeded || build.failed || build.skipped) && (cannon-build.succeeded || cannon-build.failed || cannon-build.skipped) && (cannon-lint.succeeded || cannon-lint.failed || cannon-lint.skipped) && (cannon-offline.succeeded || cannon-offline.failed || cannon-offline.skipped) && (clippy.succeeded || clippy.failed || clippy.skipped) && (docs.succeeded || docs.failed || docs.skipped) && (doctest.succeeded || doctest.failed || doctest.skipped) && (features-0.succeeded || features-0.failed || features-0.skipped) && (features-1.succeeded || features-1.failed || features-1.skipped) && (features-2.succeeded || features-2.failed || features-2.skipped) && (features-3.succeeded || features-3.failed || features-3.skipped) && (features-4.succeeded || features-4.failed || features-4.skipped) && (features-5.succeeded || features-5.failed || features-5.skipped) && (features-6.succeeded || features-6.failed || features-6.skipped) && (features-7.succeeded || features-7.failed || features-7.skipped) && (features-8.succeeded || features-8.failed || features-8.skipped) && (features-9.succeeded || features-9.failed || features-9.skipped) && (interop.succeeded || interop.failed || interop.skipped) && (no-std.succeeded || no-std.failed || no-std.skipped) && (registry.succeeded || registry.failed || registry.skipped) && (tests.succeeded || tests.failed || tests.skipped) && (typos.succeeded || typos.failed || typos.skipped) && (udeps.succeeded || udeps.failed || udeps.skipped) && (wasm-unknown.succeeded || wasm-unknown.failed || wasm-unknown.skipped) && (wasm-wasi.succeeded || wasm-wasi.failed || wasm-wasi.skipped) && (zepter.succeeded || zepter.failed || zepter.skipped) }} + run: python3 ops/ci/pr-gate.py receipt rust-workspace + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-rust-ci }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_BUILD_SUCCEEDED: ${{ tasks.build.succeeded }} + TASK_BUILD_FAILED: ${{ tasks.build.failed }} + TASK_BUILD_SKIPPED: ${{ tasks.build.skipped }} + TASK_CANNON_BUILD_SUCCEEDED: ${{ tasks.cannon-build.succeeded }} + TASK_CANNON_BUILD_FAILED: ${{ tasks.cannon-build.failed }} + TASK_CANNON_BUILD_SKIPPED: ${{ tasks.cannon-build.skipped }} + TASK_CANNON_LINT_SUCCEEDED: ${{ tasks.cannon-lint.succeeded }} + TASK_CANNON_LINT_FAILED: ${{ tasks.cannon-lint.failed }} + TASK_CANNON_LINT_SKIPPED: ${{ tasks.cannon-lint.skipped }} + TASK_CANNON_OFFLINE_SUCCEEDED: ${{ tasks.cannon-offline.succeeded }} + TASK_CANNON_OFFLINE_FAILED: ${{ tasks.cannon-offline.failed }} + TASK_CANNON_OFFLINE_SKIPPED: ${{ tasks.cannon-offline.skipped }} + TASK_CLIPPY_SUCCEEDED: ${{ tasks.clippy.succeeded }} + TASK_CLIPPY_FAILED: ${{ tasks.clippy.failed }} + TASK_CLIPPY_SKIPPED: ${{ tasks.clippy.skipped }} + TASK_DOCS_SUCCEEDED: ${{ tasks.docs.succeeded }} + TASK_DOCS_FAILED: ${{ tasks.docs.failed }} + TASK_DOCS_SKIPPED: ${{ tasks.docs.skipped }} + TASK_DOCTEST_SUCCEEDED: ${{ tasks.doctest.succeeded }} + TASK_DOCTEST_FAILED: ${{ tasks.doctest.failed }} + TASK_DOCTEST_SKIPPED: ${{ tasks.doctest.skipped }} + TASK_FEATURES_0_SUCCEEDED: ${{ tasks.features-0.succeeded }} + TASK_FEATURES_0_FAILED: ${{ tasks.features-0.failed }} + TASK_FEATURES_0_SKIPPED: ${{ tasks.features-0.skipped }} + TASK_FEATURES_1_SUCCEEDED: ${{ tasks.features-1.succeeded }} + TASK_FEATURES_1_FAILED: ${{ tasks.features-1.failed }} + TASK_FEATURES_1_SKIPPED: ${{ tasks.features-1.skipped }} + TASK_FEATURES_2_SUCCEEDED: ${{ tasks.features-2.succeeded }} + TASK_FEATURES_2_FAILED: ${{ tasks.features-2.failed }} + TASK_FEATURES_2_SKIPPED: ${{ tasks.features-2.skipped }} + TASK_FEATURES_3_SUCCEEDED: ${{ tasks.features-3.succeeded }} + TASK_FEATURES_3_FAILED: ${{ tasks.features-3.failed }} + TASK_FEATURES_3_SKIPPED: ${{ tasks.features-3.skipped }} + TASK_FEATURES_4_SUCCEEDED: ${{ tasks.features-4.succeeded }} + TASK_FEATURES_4_FAILED: ${{ tasks.features-4.failed }} + TASK_FEATURES_4_SKIPPED: ${{ tasks.features-4.skipped }} + TASK_FEATURES_5_SUCCEEDED: ${{ tasks.features-5.succeeded }} + TASK_FEATURES_5_FAILED: ${{ tasks.features-5.failed }} + TASK_FEATURES_5_SKIPPED: ${{ tasks.features-5.skipped }} + TASK_FEATURES_6_SUCCEEDED: ${{ tasks.features-6.succeeded }} + TASK_FEATURES_6_FAILED: ${{ tasks.features-6.failed }} + TASK_FEATURES_6_SKIPPED: ${{ tasks.features-6.skipped }} + TASK_FEATURES_7_SUCCEEDED: ${{ tasks.features-7.succeeded }} + TASK_FEATURES_7_FAILED: ${{ tasks.features-7.failed }} + TASK_FEATURES_7_SKIPPED: ${{ tasks.features-7.skipped }} + TASK_FEATURES_8_SUCCEEDED: ${{ tasks.features-8.succeeded }} + TASK_FEATURES_8_FAILED: ${{ tasks.features-8.failed }} + TASK_FEATURES_8_SKIPPED: ${{ tasks.features-8.skipped }} + TASK_FEATURES_9_SUCCEEDED: ${{ tasks.features-9.succeeded }} + TASK_FEATURES_9_FAILED: ${{ tasks.features-9.failed }} + TASK_FEATURES_9_SKIPPED: ${{ tasks.features-9.skipped }} + TASK_INTEROP_SUCCEEDED: ${{ tasks.interop.succeeded }} + TASK_INTEROP_FAILED: ${{ tasks.interop.failed }} + TASK_INTEROP_SKIPPED: ${{ tasks.interop.skipped }} + TASK_NO_STD_SUCCEEDED: ${{ tasks.no-std.succeeded }} + TASK_NO_STD_FAILED: ${{ tasks.no-std.failed }} + TASK_NO_STD_SKIPPED: ${{ tasks.no-std.skipped }} + TASK_REGISTRY_SUCCEEDED: ${{ tasks.registry.succeeded }} + TASK_REGISTRY_FAILED: ${{ tasks.registry.failed }} + TASK_REGISTRY_SKIPPED: ${{ tasks.registry.skipped }} + TASK_TESTS_SUCCEEDED: ${{ tasks.tests.succeeded }} + TASK_TESTS_FAILED: ${{ tasks.tests.failed }} + TASK_TESTS_SKIPPED: ${{ tasks.tests.skipped }} + TASK_TYPOS_SUCCEEDED: ${{ tasks.typos.succeeded }} + TASK_TYPOS_FAILED: ${{ tasks.typos.failed }} + TASK_TYPOS_SKIPPED: ${{ tasks.typos.skipped }} + TASK_UDEPS_SUCCEEDED: ${{ tasks.udeps.succeeded }} + TASK_UDEPS_FAILED: ${{ tasks.udeps.failed }} + TASK_UDEPS_SKIPPED: ${{ tasks.udeps.skipped }} + TASK_WASM_UNKNOWN_SUCCEEDED: ${{ tasks.wasm-unknown.succeeded }} + TASK_WASM_UNKNOWN_FAILED: ${{ tasks.wasm-unknown.failed }} + TASK_WASM_UNKNOWN_SKIPPED: ${{ tasks.wasm-unknown.skipped }} + TASK_WASM_WASI_SUCCEEDED: ${{ tasks.wasm-wasi.succeeded }} + TASK_WASM_WASI_FAILED: ${{ tasks.wasm-wasi.failed }} + TASK_WASM_WASI_SKIPPED: ${{ tasks.wasm-wasi.skipped }} + TASK_ZEPTER_SUCCEEDED: ${{ tasks.zepter.succeeded }} + TASK_ZEPTER_FAILED: ${{ tasks.zepter.failed }} + TASK_ZEPTER_SKIPPED: ${{ tasks.zepter.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/rust-workspace diff --git a/.rwx/selector-upload.yml b/.rwx/selector-upload.yml new file mode 100644 index 00000000000..bb27798f233 --- /dev/null +++ b/.rwx/selector-upload.yml @@ -0,0 +1,168 @@ +# Execute the original selector publisher against a private official registry. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [registry-images, prepare] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + github: + push: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: ${{ event.git.tag }} + cache-warm: "false" + cache-epoch: v1 + status-checks: + name: optimism-selector-upload-shadow + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: {cpus: 2, memory: 8gb} + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [code, mise] + run: bash ops/ci/rwx-prepare.sh + timeout: 30m + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: contract-tools + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + bash ops/ci/rwx-contracts-prepare.sh tools + bash .circleci/scripts/apt-install.sh iproute2 openssl + timeout: 30m + + - key: registry-images + use: [code, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + docker: preserve-data + run: python3 ops/ci/selector-registry.py images .ci/selector-registry + timeout: 45m + runner: {cpus: 8, memory: 16gb} + outputs: + artifacts: + - key: images + path: .ci/selector-registry + + - key: source + use: [code, contract-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: | + git submodule sync --recursive + for attempt in 1 2 3 4 5; do + if git -c protocol.file.allow=never submodule update --init --recursive --jobs 8; then break; fi + if [ "$attempt" = 5 ]; then exit 1; fi + sleep "$((2 ** attempt))" + done + timeout: 20m + + - key: prepare + use: source + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/selector-upload.py prepare .ci/selector-upload/prepare + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + tool-cache: selector-upload-abi-v2-${{ init.cache-epoch }} + timeout: 40m + runner: {cpus: 16, memory: 32gb} + outputs: + filesystem: + filter: + workspace: [packages/contracts-bedrock/cache, packages/contracts-bedrock/forge-artifacts, packages/contracts-bedrock/artifacts, packages/contracts-bedrock/foundry-pp] + system: [/root/.svm, /home/*/.svm] + artifacts: + - key: compiled + path: .ci/selector-upload/prepare + + - key: helper-tests + use: [code, contract-tools, registry-images] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + docker: true + run: | + python3 -m unittest discover -s ops/ci -p 'test*selector_upload.py' + python3 ops/ci/selector-registry.py fixture "$REGISTRY_IMAGES/sourcify" .ci/selector-upload/fixtures + env: + REGISTRY_IMAGES: ${{ tasks.registry-images.artifacts.images }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: original-failure-fixtures + path: .ci/selector-upload/fixtures + + - key: upload + use: [source, prepare, registry-images] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + docker: true + run: | + python3 ops/ci/selector-upload.py run .ci/selector-upload/run "$COMPILED" \ + "$REGISTRY_IMAGES/sourcify" "$REGISTRY_IMAGES/images.json" + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CI_CHECK_PROVIDER: rwx + COMPILED: ${{ tasks.prepare.artifacts.compiled }} + REGISTRY_IMAGES: ${{ tasks.registry-images.artifacts.images }} + cache: false + timeout: 60m + runner: {cpus: 8, memory: 16gb} + outputs: + filesystem: false + artifacts: + - key: report + path: .ci/selector-upload/run diff --git a/.rwx/sp1-guest.yml b/.rwx/sp1-guest.yml new file mode 100644 index 00000000000..fa2dbec326b --- /dev/null +++ b/.rwx/sp1-guest.yml @@ -0,0 +1,215 @@ +# Complete SP1 guest ELF build and fresh original checks; Circle retains required ownership. +on: + cache-rebuild: + if: ${{ event.git.branch == 'develop' }} + target: [build] + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "true" + cache-epoch: v1 + target-cache-mode: keep + cli: + init: + commit-sha: ${{ event.git.sha }} + branch: ${{ event.git.branch }} + tag: "" + cache-warm: "false" + cache-epoch: v1 + target-cache-mode: keep + +tool-cache: + vault: optimism-op-reth-shadow + +base: + image: ubuntu:24.04@sha256:008173c23f95b170204355c12626cb5a965d779a7e1283b09e9cffbb1bf33ca3 + config: rwx/base 1.2.0 + arch: x86_64 + +defaults: + runner: + cpus: 2 + memory: 8gb + +tasks: + - key: code + call: git/clone 2.2.0 + with: + repository: https://github.com/ethereum-optimism/optimism.git + ref: ${{ init.commit-sha }} + preserve-git-dir: true + fetch-full-depth: true + submodules: false + + - key: bootstrap-inputs + use: code + run: 'true' + filter: [mise.toml, ops/ci, .circleci/scripts/apt-install.sh] + outputs: + filesystem: + filter: + workspace: [mise.toml, .circleci/scripts/apt-install.sh, ops/ci/rwx-prepare.sh, ops/ci/rwx-contracts-prepare.sh, ops/ci/rwx-rust-prepare.sh, ops/ci/op-reth-shadow.sh, ops/ci/op-reth-report.py, ops/ci/rust-target-cache.py] + system: [] + artifacts: + - key: mise-config + path: mise.toml + - key: ci-scripts + path: ops/ci + - key: apt-script + path: .circleci/scripts/apt-install.sh + + - key: mise + call: mise/install 1.1.0 + with: + mise-version: "2026.2.2" + install: "false" + + - key: tools + use: [mise, bootstrap-inputs] + call: ${{ run.dir }}/packages/toolchain-common.yml + + - key: route + use: [code, tools] + run: bash ops/ci/rwx-metadata.sh + env: + CI_EVENT: push + CI_CACHE_WARM: ${{ init.cache-warm }} + CI_BRANCH: ${{ init.branch }} + CI_TAG: ${{ init.tag }} + CI_COMMIT_SHA: ${{ init.commit-sha }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: routing + path: .ci/pipeline-parameters.json + + - key: rust-tools + use: [bootstrap-inputs, tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + call: ${{ run.dir }}/packages/toolchain-rust.yml + with: + mode: sp1 + + - key: toolchain + use: [code, rust-tools] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/sp1-guest.py --provider rwx --phase toolchain + filter: + - mise.toml + - rust/Cargo.toml + - rust/Cargo.lock + - rust/kona/sp1/programs/Cargo.toml + - rust/kona/sp1/programs/Cargo.lock + - rust/kona/sp1/justfile + - rust/rust-toolchain.toml + - rust/.cargo/config.toml + - ops/ci/sp1-guest.py + - ops/ci/sp1-guest-native-build.py + - ops/ci/sp1-guest-toolchain.sh + - ops/ci/rust-workspace-report.py + - ops/ci/ci-report.py + - ops/ci/rust-target-cache.py + timeout: 40m + outputs: + artifacts: + - key: reports + path: .ci/sp1-guest/toolchain + + - key: build + use: [code, toolchain] + if: ${{ init.cache-warm == 'true' || tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/sp1-guest.py --provider rwx --phase build + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CARGO_INCREMENTAL: "0" + SP1_GUEST_TARGET_MODE: ${{ init.target-cache-mode }} + tool-cache: sp1-guest-canonical-elf-${{ init.cache-epoch }} + timeout: 90m + runner: + cpus: 16 + memory: 32gb + outputs: + filesystem: + filter: + workspace: [.ci/sp1-cache/cargo, .ci/sp1-cache/elf-target, .ci/sp1-cache/elf-sccache] + system: [] + artifacts: + - key: dependency + path: .ci/sp1-guest/dependency + + - key: helper-tests + use: [code, toolchain] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: RWX_LIVE_SP1_FIXTURE=1 python3 -m unittest discover -s ops/ci -p 'test_sp1_guest.py' + env: + SP1_GUEST_ARTIFACT: ${{ tasks.build.artifacts.dependency }} + SP1_GUEST_REPORT: ${{ tasks.check.artifacts.reports }} + cache: false + timeout: 15m + outputs: + filesystem: false + artifacts: + - key: fixtures + path: .ci/sp1-guest/helper-fixtures + + - key: check + use: [code, toolchain] + if: ${{ init.cache-warm != 'true' && tasks.route.values.run-main == 'true' }} + run: python3 ops/ci/sp1-guest.py --provider rwx --phase checks --artifact "$SP1_GUEST_ARTIFACT" + env: + CI: "true" + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + CARGO_INCREMENTAL: "0" + SP1_GUEST_TARGET_MODE: ${{ init.target-cache-mode }} + SP1_GUEST_ARTIFACT: ${{ tasks.build.artifacts.dependency }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + tool-cache: sp1-guest-checks-${{ init.cache-epoch }} + cache: false + timeout: 90m + runner: + cpus: 8 + memory: 16gb + outputs: + filesystem: + filter: + workspace: [.ci/sp1-cache/cargo, .ci/sp1-cache/checks-target, .ci/sp1-cache/checks-sccache] + system: [] + test-results: + - path: .ci/sp1-guest/run/*.junit.xml + artifacts: + - key: reports + path: .ci/sp1-guest/run + + # Exact Main dependency states; unrelated workload failures stay outside this gate. + - key: main-gate-receipt + use: [code, tools] + after: ${{ (check.succeeded || check.failed || check.skipped) }} + if: ${{ init.cache-warm != 'true' }} + run: python3 ops/ci/pr-gate.py receipt main-sp1 + env: + CI_COMMIT_SHA: ${{ init.commit-sha }} + CI_BRANCH: ${{ init.branch }} + GROUP_SELECTED: ${{ tasks.route.values.run-main }} + RWX_RUN_ID: + cache-key: included + RWX_TASK_ATTEMPT_NUMBER: + cache-key: included + TASK_CHECK_SUCCEEDED: ${{ tasks.check.succeeded }} + TASK_CHECK_FAILED: ${{ tasks.check.failed }} + TASK_CHECK_SKIPPED: ${{ tasks.check.skipped }} + cache: false + timeout: 10m + outputs: + filesystem: false + artifacts: + - key: receipt + path: .ci/pr-gates/groups/main-sp1 diff --git a/docs/ai/ci-comparison.md b/docs/ai/ci-comparison.md new file mode 100644 index 00000000000..c089b1c6868 --- /dev/null +++ b/docs/ai/ci-comparison.md @@ -0,0 +1,150 @@ +# Compare CircleCI and RWX shadow reports + +`ops/ci/compare-ci.py` compares retained evidence for one workload on one commit. +It runs locally without network access or credentials. Keep raw downloads outside +Git (for example, under `.ci/comparison/`); retain provider run/job URLs alongside +the collection so another operator can audit it. + +The completed pilot's originals and generated summaries are retained outside the +checkout in two private copies. See the [archive index](rwx-evidence-index.md) +for checksums, restoration and the source bundle. A locally ignored `.ci/` +directory is collection workspace, not durable evidence storage. Keep generated +reports out of subsequent commits. + +## Collect one revision + +Open a PR for the comparison branch: CircleCI currently has `build-prs-only` +enabled. Wait for both providers to finish, and verify their full commit SHA and +branch before downloading reports. A previous commit with the same source tree +is a historical baseline, not a same-revision comparison. + +For Go rollup, download every aggregate CircleCI Go JSON shard and all RWX Go +JSON shards. Select `github.com/ethereum-optimism/optimism/op-node/rollup` with +`metadata.package_prefix`. Retain RWX's package manifest and the CircleCI package +selection evidence, including packages without test files. Go JSON preserves +attempt history; CircleCI's final test API or JUnit cannot establish zero retries. + +For standard contracts, collect CircleCI's complete test API response and RWX's +original JUnit for each of `main`, `CUSTOM_GAS_TOKEN`, `OPTIMISM_PORTAL_INTEROP` +and `ZK_DISPUTE_GAME`. Mark trace/rerun reports as `diagnostic`: a successful +rerun must not replace the first verdict. Retain the test-file inventory and +configuration evidence. Distinguish runtime configuration dumps from settings +that are only declared in source. The standard branch profile is `liteci`; +`develop` uses `ci`. Neither profile covers the other contract jobs. + +For L2 fork tests, `ops/ci/compare-contract-l2-fork.py` checks the complete +original selection, compiler bindings, commands, initial JUnit, pinned-block +frames and sealed runtime relay report. The relay records every request that +reaches the upstream RPC. When Foundry serves every fork read from its state +cache, the relay records zero requests. The reader accepts that sealed report +alongside the complete fresh verdict and independent pinned-block checks. +Different request counts alone do not establish a workload mismatch or a speed +comparison. Keep those cache states explicit; compare common stable archive +results when both reports contain them. + +Do not collect secret values. Fetch every page of test APIs and every expected +shard. Record the original job/task terminal outcome even if its test cases all +passed: later build or convention-check failures still matter. + +## Describe the evidence + +A version-1 collection lists local report sources. Paths are relative to the +collection JSON. This abbreviated Go example is illustrative; replace all +metadata and paths with verified evidence: + +```json +{ + "version": 1, + "metadata": { + "provider": "rwx", + "sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "branch": "codex/comparison-example", + "workload": "go-rollup", + "profile": "ci", + "features": ["main"], + "package_prefix": "github.com/ethereum-optimism/optimism/op-node/rollup", + "routing_context": { + "kind": "branch", + "base_branch": "develop", + "run_main": true, + "run_contracts_feature_tests": true + }, + "trigger": {"type": "github.push", "evidence": "provider-run.json"}, + "test_config": {"tags": ["ci"], "short": false, "timeout": "40m"} + }, + "sources": [ + { + "id": "rollup-0", + "format": "go-json", + "path": "shard-0/log.json", + "metadata_path": "shard-0/job-metadata.json", + "feature": "main", + "role": "verdict", + "shard_index": 0, + "shard_total": 1 + } + ], + "discovery": { + "packages": ["github.com/ethereum-optimism/optimism/op-node/rollup"], + "complete": true, + "provenance": "package-discovery.json" + }, + "measurements": {"wall_seconds": null, "billed_seconds": null, "cost": null} +} +``` + +Use `circleci-tests`, `go-json`, or `junit` for each source's `format`. CircleCI +case files accept `tests`, `items`, or `cases` lists; a nonempty pagination token +is rejected. A source's `metadata_path` points to a retained JSON object with +`sha`, `branch`, and `status` (`success`, `fail`, or `canceled`). Extract those +values from the provider response and retain the original response for audit. +CircleCI case envelopes containing `sha` can bind that revision directly, with +an explicit source `status`. The tool checks consistency with these files; it +does not authenticate the collector or attest a provider response. + +Populate `routing_context` from verified shared routing output, using the same +field names and values for both providers. Preserve raw trigger provenance in +`trigger`; an RWX push and a CircleCI PR webhook may select the same context. +Do not infer a context from a trigger name. Include effective test settings in +`test_config`, with configuration provenance alongside the collection. Metadata +mismatches make the comparison incomparable. + +For Go discovery, use `packages` and optionally `total`/`shards` from the package +manifest. For contracts, use `test_files`. Set `complete: true` only when retained +selection evidence proves the full workload was discovered; it requires a +nonempty `provenance` reference. Matching case reports alone do not prove this. + +## Normalize and compare + +```bash +python3 ops/ci/compare-ci.py normalize --input .ci/comparison/circleci.json --output .ci/comparison/circleci.normalized.json +python3 ops/ci/compare-ci.py normalize --input .ci/comparison/rwx.json --output .ci/comparison/rwx.normalized.json +python3 ops/ci/compare-ci.py compare --baseline .ci/comparison/circleci.normalized.json --candidate .ci/comparison/rwx.normalized.json --output .ci/comparison/report.json --markdown .ci/comparison/report.md +python3 ops/ci/test_compare_ci.py +``` + +The JSON report includes missing/extra identities, outcome and skip-reason +changes, observed retries, evidence gaps and retained measurements. `equivalent` +means the supplied workload evidence meets the comparison checks; it does not +establish aggregate gate coverage or authorize changing required checks. +`different` identifies a mismatch or unhealthy source. `incomplete` retains +case comparisons but lacks required discovery, revision, outcome, routing or +skip-reason evidence. `incomparable` identifies incompatible metadata. Exit +codes are 0 for equivalent, 1 for different/incomplete, and 2 for incomparable +or rejected input. + +Equivalence compares final case verdicts; it can coexist with observed retries. +Review retry history separately before judging reliability. Unavailable attempt +history is reported as unknown rather than as zero retries. + +For the pilot, the primary metric is push-to-final-verdict wall time for the same +selected workload and coverage. Include queueing, setup and transfers; label +run-start timings when earlier timestamps are unavailable. Report setup, longest +shard, summed task time, CPU time, cache state and actual resources separately with +explicit units and scope. Runner sizes may differ when optimizing wall time. +Cost and billed-usage analysis are deferred; leave those values null until needed. +RWX cached tasks can retain historical execution fields; exclude them from current +compute totals. An aggregate +CircleCI Go job's duration includes other packages and dependency builds, so it +cannot measure only the rollup slice. Separate warm-cache observations from cold +runs, and compare multiple samples before drawing performance conclusions. diff --git a/docs/ai/ci-config-review.md b/docs/ai/ci-config-review.md index 261de87b46e..2ae3c82f33e 100644 --- a/docs/ai/ci-config-review.md +++ b/docs/ai/ci-config-review.md @@ -1,8 +1,9 @@ # CI Config Review -Checklist for reviewing changes to `.circleci/` and `.github/workflows/`. The -repo-specific items are the high-priority ones — they're where the real bugs -hide. For each changed file, walk the relevant items and look for the bad pattern. +Checklist for reviewing changes to `.circleci/`, `.rwx/`, `ops/ci/`, and +`.github/workflows/`. The repo-specific items are the high-priority ones — they're +where the real bugs hide. For each changed file, walk the relevant items and look +for the bad pattern. ## How CI is wired here @@ -13,10 +14,12 @@ hide. For each changed file, walk the relevant items and look for the bad patter toolset (`utils/install-mise`): it always finishes before any continuation job starts, so on a cold cache it is the only job that installs over the network — continuation jobs restore the mise cache it saved. -- **Routing is data + logic split**: `routing.yml` holds the declarative data +- **Routing is data + logic split**: `ops/ci/routing.yml` holds the declarative data (schedule→workflows, API dispatch flag→workflows, change-detection patterns, - passthrough params); `compute-workflow-conditions.sh` holds the conditions that - decide which entries fire. Add a schedule/dispatch/pattern by editing `routing.yml`. + passthrough params); `ops/ci/compute-workflow-conditions.sh` holds the conditions + that decide which entries fire. CircleCI scripts adapt its pipeline metadata; + `.circleci/routing.yml` points to the shared data. Add a schedule/dispatch/pattern + by editing the shared `routing.yml`, and validate the CircleCI adapter too. - **The real config is merged from fragments** under `.circleci/continue/` (`helpers.yml` → `main.yml` → `rust-ci.yml` → `rust-e2e.yml` → `rust-nightly-bump.yml`) by `merge-configs.sh`. **Merge is later-wins**: a key @@ -26,7 +29,9 @@ hide. For each changed file, walk the relevant items and look for the bad patter params; `detect`/`detect_all` match the `routing.yml` change patterns against the changed files (`detect` true if *any* file matches, `detect_all` only if *every* file matches). `workflow-helpers.sh` sets the `c-run_*` flags; - `test-decision-tree.sh` asserts the routing policy. + `ops/ci/test-decision-tree.sh` asserts the routing policy, real changed-file + fixtures, and CircleCI adapter parity. `.rwx/` and `ops/ci/` changes select the + same CI, contract and Rust validation as `.circleci/` changes. - **The gate**: the GitHub `enforce-ci-checks-develop` ruleset requires exactly four checks — `ci-gate`, `required-contracts-ci`, `required-rust-ci`, `required-rust-e2e`. These are fan-in jobs (no work, just `requires:`). A merge @@ -38,6 +43,11 @@ hide. For each changed file, walk the relevant items and look for the bad patter ## Validating a change locally +For the RWX pilot, use [rwx-migration.md](rwx-migration.md). The pilot must keep +distinct check names and must not silently remove a CircleCI gate dependency or +enable an additional publisher. Validate shared routing changes through both the +shared tests and the existing CircleCI entrypoints. + Because the real config is merged from fragments, validate the **merged** file, not a single fragment: @@ -45,16 +55,20 @@ single fragment: # 1. Merge the fragments into /tmp/merged-config.yml (uses mise's yq; resolves anchors). mise exec -- bash .circleci/scripts/merge-configs.sh -# 2. Validate it. --org-slug is REQUIRED: the private org orb +# 2. Validate it. --org is REQUIRED: the private org orb # ethereum-optimism/circleci-utils won't resolve without it (and the CLI -# needs CIRCLECI_CLI_TOKEN set to resolve --org-slug). -export CIRCLECI_CLI_TOKEN="" -circleci config validate --org-slug gh/ethereum-optimism /tmp/merged-config.yml +# needs CIRCLE_TOKEN set to resolve the organization). +export CIRCLE_TOKEN="" +circleci config validate --org gh/ethereum-optimism /tmp/merged-config.yml # 3. The setup config imports the private orb too, so it needs the same flag. -circleci config validate --org-slug gh/ethereum-optimism .circleci/config.yml +circleci config validate --org gh/ethereum-optimism .circleci/config.yml ``` +These commands use CircleCI CLI 1.x. With the older 0.x CLI, use `--org-slug` +and `CIRCLECI_CLI_TOKEN` instead. Do not confuse a shell-profile variable name +with the environment variable the selected CLI actually reads. + Install the CLI without sudo: `curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | DESTDIR="$HOME/.local/bin" bash`. @@ -62,6 +76,10 @@ This checks orb resolution and config structure, but **not** continuation-time p wiring — a param leak across fragment anchors still only surfaces when the pipeline actually continues. +Run shared routing and Circle adapter fixtures with +`mise exec yq jq -- bash ops/ci/test-decision-tree.sh` using Bash 4+ or Linux. +macOS `/bin/bash` 3 is unsupported; the fixture entrypoint rejects it explicitly. + ## Choosing where a new job runs When a diff adds a job, the first question is cadence, not correctness. Options diff --git a/docs/ai/ci-ops.md b/docs/ai/ci-ops.md index b2459f883dd..7922ba39dc8 100644 --- a/docs/ai/ci-ops.md +++ b/docs/ai/ci-ops.md @@ -4,6 +4,10 @@ This document provides guidance for AI agents working with CI/CD operational tas For Docker image build failures — especially flaky `apt`/`apk`/`curl` downloads from package registries and CDNs — see [docker.md](docker.md). +The optional RWX pilot and migration checklist are documented in +[rwx-migration.md](rwx-migration.md). CircleCI remains the required CI provider; +an RWX pilot result does not replace watching the existing merge gates. + ## Watching CI after a push Watch every push to a terminal state — `AGENTS.md` requires it. Most jobs run on @@ -38,9 +42,10 @@ Notes that matter in practice: run on fast paths; for every other suite, look for an open flake issue instead. A rerun that hides a real regression costs more than the minutes it saved, and a confirmed flake needs an issue, not a silent retry. Reruns through the CircleCI v2 API need a - personal API token in `CIRCLE_TOKEN` — not the `CIRCLECI_CLI_TOKEN` the CLI reads for - [ci-config-review.md](ci-config-review.md)'s `circleci config validate --org-slug`, and - not the `CIRCLE_API_TOKEN` in `.circleci/`, which is the in-job context token. For + personal API token in `CIRCLE_TOKEN`, also read by CircleCI CLI 1.x for + [ci-config-review.md](ci-config-review.md)'s `circleci config validate --org`. + Older CLI 0.x uses `CIRCLECI_CLI_TOKEN` and `--org-slug`. The `CIRCLE_API_TOKEN` + in `.circleci/` is the in-job context token. For flakes in `op-acceptance-tests/`/`op-devstack/`, [flake-prevention.md](flake-prevention.md) catalogues the recurring causes. diff --git a/docs/ai/rwx-acceptance-parity.md b/docs/ai/rwx-acceptance-parity.md new file mode 100644 index 00000000000..2b7dad78511 --- /dev/null +++ b/docs/ai/rwx-acceptance-parity.md @@ -0,0 +1,132 @@ +# Acceptance RWX shadow + +The next port stays in draft PR #23151. `.rwx/acceptance.yml` introduces optional +`optimism-acceptance-shadow` using shared `run-main` routing. Circle retains all +required gates. The five new job/dependency edges advance +implementation coverage to 21/86 (24%); main-workflow coverage is 13/32 (41%). + +## Workload and dependencies + +Both complete `./op-acceptance-tests/tests/...` variants run at the `fusaka` L1 fork: +`op-node + op-reth` and `kona-node + op-reth`. Each has eight test-name shards, +`-count=1`, `-p=8`, `-parallel=1`, a 30-minute package timeout and no test retries, +matching the current Circle acceptance command. Shared Just execution preserves +client-specific skips and all subtests. Assignment groups identical test names +across packages, so a global run regexp cannot duplicate their execution. + +The Go/Cannon/superchain, ci-profile contracts, Kona host/client/node plus op-zk-proposer, op-reth +and reproducible prestates reuse the full Go port's verified producer packages. +A new isolated SP1 producer builds `kona-sp1-super-range-executor` with Circle's +release profile and all features, staging the binary at the same dedicated path. +Cargo target and sccache caches are retained; source SHA, pins, settings and file +hashes bind every restored dependency. There are no publisher or notification +side effects. + +Runtime exports all six Rust binary paths and checks each executable before tests. +`KONA_SP1_ELF_DIR` stays unset, preserving Circle's stub artifacts and mock verifier. +Real SP1 guest ELFs remain a separate Circle gate. Runtime Go builds use an +isolated native compiler cache. Go, Forge, Cast, Anvil and Docker are available; +The pinned Glamsterdam geth tool is installed, checked before execution and retained +with its file hash and version. `eatmydata` preserves Circle's fsync behavior. Archive RPC credentials are not +needed by these suites and are not added to the acceptance workflow. + +Initial workers are 16 CPU / 64 GiB for op-node and 16 CPU / 32 GiB for Kona. +Circle documented peaks of 38.5 GiB and 21.3 GiB respectively; RWX's supported +sizes and reserved memory make 64 GiB the safe initial op-node allocation. +This is a reliability baseline, not a performance comparison. No benchmark +matrix or concurrency optimization is part of this stage. + +## Discovery and reports + +`acceptance-manifest.py` retains untagged `go list -e -json` and original +`go test -list '^Test' -json` output. Package/dependency errors, failed/incomplete +listing, duplicate identities and invalid assignments fail validation. Complete +package selection includes packages without tests. Every discovered test identity +receives exactly one initial assignment, and unknown/new names remain included. + +The shared Just runner retains complete discovery, effective settings and actual +assigned identities on both providers. Circle continues using its timing-based +splitter; RWX consumes a source/settings/toolchain-bound manifest and deterministic +partitions. Valid empty shards still emit fresh package reports. Native verdict +caching is disabled, while compiler objects remain reusable. + +RWX collects original events, JUnit, per-test logs, dependency provenance, +complete selection, execution coverage and a compact native reporting projection. +Collection runs after test failures and rejects missing, duplicate or unassigned +top-level verdicts without replacing original failure evidence. Protected develop +warming targets producers and discovery only, running no acceptance verdicts. + +## Validation tracking + +- [x] Discovery/assignment tests: missing data, errors, duplicate test names, + unknown/new cases, empty shards, stale SHA/settings/toolchain and corruption. +- [x] Execute the Just entrypoint through Circle and RWX fixture paths, including + failed discovery and a failing verdict with retained original reports. +- [x] Original failure retention and execution-coverage validation tests. +- [x] ShellCheck and RWX definition/package lint. +- [x] Hosted SP1 producer and all sixteen acceptance verdicts reach terminal states. +- [x] Compare both variants' complete original reports on the same SHA with Circle; + investigate every missing, extra, changed or unexplained skipped identity. +- [x] Retain hosted failures and attribute the observed setup failures using originals and source evidence. +- [x] Comparison revision's required Circle gates and optional RWX checks reach terminal states. + +Native/protected warming after merge and broader gate/fork/merge-group rehearsals +remain separate operational follow-ups. Protected warming includes the pinned +geth tool layer and executes no acceptance verdicts. + +The first clean hosted attempt at `fb8f75b685` retained complete failure reports +and exposed two port-specific runtime gaps: Kona host/client lookup fell back to +unavailable Cargo, and the selective tool layer omitted pinned Glamsterdam geth. +All sixteen original report bundles are retained; their coverage summaries show +zero missing, extra or duplicated assigned identities. See the compact +[first-failure index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-acceptance-evidence/first-failure.json). +Explicit verified binary paths and geth installation address those gaps; no test +expectations or skip rules were changed. The corrected hosted run and same-revision comparison passed before advancing +the inventory. + +The corrected clean native run at `236df44cad` passed all six producer packages, +both discoveries and all sixteen fresh verdict workers: +[RWX run 92831490](https://cloud.rwx.com/optimism/runs/9283149078084d80a1cbaba1eb917b69). +Same-revision [Circle job 5632121](https://circleci.com/gh/ethereum-optimism/optimism/5632121) +(Kona) and [5632123](https://circleci.com/gh/ethereum-optimism/optimism/5632123) +(op-node) passed too. Complete original-report comparison verifies the same 80 +packages, 243 top-level identities and 780 reported case identities per variant. +All initial assignments occur exactly once. Both providers report 770 pass / 10 +skip for op-node and 742 pass / 38 skip for Kona, with zero failures or retries. +There are no missing, extra or unknown skipped cases, unhealthy sources or +incomplete evidence. All sixteen native verdict tasks executed; none reused a +verdict result. + +Strict skip-text comparison remains `different`: nine op-node and 37 Kona +messages contain different structured logger timestamps. Each was checked after +replacing only that recognized timestamp field: severity, message, scope and test +identity match exactly. The unchanged originals, both reasons and each resolution +are retained in [the compact parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-acceptance-evidence/parity.json). +Other skips preserve existing client-support limits, the disabled batcher test +and full SP1 ELF opt-in behavior; real guest ELFs remain outside this stage. +All four required Circle gates and the five optional RWX shadows passed on the +comparison revision. Full JSON, JUnit, selection, provenance and per-test logs +remain in provider artifacts and downloaded evidence archives. + +The final closeout synchronizes `develop`, including its newly added ZK +acceptance case. Exhaustive discovery includes that case automatically. Final +combined-revision execution, original-report comparison and required checks are +tracked in PR #23151; the immutable comparison above remains tied to its stated +source revision. No performance matrix or speed claim is part of this stage. + +The final develop synchronization follows current Circle dependencies: the +proposer package/binary is `op-zk-proposer`, and the removed standalone +`op-reth-sdm-fixture` is no longer built, exported or restored. Producer settings, +artifact validation, the op-reth shadow and runtime fixtures follow those changes. +The immutable earlier comparison above predates that upstream rename/removal; +final same-revision evidence is recorded in the PR. + +The current-source validation exposed Cargo reusing older workspace dependency +metadata from restored targets when checkout timestamps did not advance. Circle +compiled op-reth successfully on the same SHA. The live two-crate Cargo fixture +reproduces the missing-trait failure, then passes after the timestamp correction +and proves unchanged targets remain reusable. All Rust producers now fingerprint +source content, restore a stable timestamp for each content version and commit +that fingerprint only after successful compilation. Registry targets and sccache +remain intact. See the [failure attribution](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-acceptance-evidence/cargo-freshness-failure.json). +The expanded pinned Linux helper suite passes all 153 tests. diff --git a/docs/ai/rwx-cannon-go.md b/docs/ai/rwx-cannon-go.md new file mode 100644 index 00000000000..0be50fbcca5 --- /dev/null +++ b/docs/ai/rwx-cannon-go.md @@ -0,0 +1,137 @@ +# Cannon Go shadow + +The optional `optimism-cannon-go-shadow` implements Main's +`cannon-go-lint-and-test` through the shared `run-main` routing. This occurrence +passes complete original-report parity at +`5ee3311d889fd889a70d112b46ed00d8a398508c`. Verified occurrence coverage is +now 75/86 (87%): Main 24/32, Contracts 21/23, Rust 21/22 and Rust E2E 9/9. +Circle retains the original job name, dependency, notifications, +cache namespace and required-gate membership. + +The shared adapter runs the original `just lint` compatibility recipe, then +`gotestsum-split.sh --format=testname -- -timeout=10m -parallel=$(nproc) ./...` +from `cannon`. PR runs preserve `SKIP_SLOW_TESTS=true`; scheduled Circle runs +preserve `false` and the 45-minute timeout. No `ci` tag, `-short`, explicit +package concurrency, test retries or package exclusions are added. The root +Go lint job remains the actual Cannon linter; its separate occurrence was +already ported. + +RWX always supplies `-count=1`, disables verdict task caching, and retains a +dedicated native Go compiler cache. Circle's existing `c-go_fresh_tests` +benchmark parameter now also controls this adapter, defaulting to false. +Both benchmark providers must run freshly. `go list -e -json ./...` errors and +dependency errors fail discovery even if Go exits zero. Complete test listing +includes every initial Test, Example and Fuzz identity and retains packages +without tests. Every selected package and initial test must appear exactly +once in the original verdict events. + +The modules-only producer downloads, verifies and discovers the full module +set before creating a source-bound archive. It keeps the checked-in Cannon +embed placeholders intact: this Circle workload does not receive the full +Go producer's generated VM binaries or hello ELF. The existing reusable `ci` +contract producer runs independently. Both producers bind their archive and +file hashes to the source revision, tool pins, settings and actual tools. +Runtime restores reject missing, corrupt or mismatched inputs. Full source, +submodules, relative fixture paths and Git metadata remain available. + +The initial verdict worker has 8 CPUs and 16 GiB, matching the original +`xlarge.gen2` Docker allocation in the +[Circle resource reference](https://circleci.com/docs/reference/configuration-reference/). +Its CPU count, default +package concurrency and effective `-parallel` are recorded. This stage adds +no shard or resource tuning matrix. Warm-only events on protected `develop` +target the two producers and select zero tests or helper fixtures. Native +test reporting uses the bounded projection; complete original JSON, JUnit, +per-test logs, discovery, commands, effective environment and source hashes +remain in the `reports` artifact. Failed and cancelled commands retain their +real exit/signal and available original reports without inventing verdicts. + +`compare-cannon-go.py` validates every original file seal, rederives discovery +and coverage, verifies native report projection and runtime dependency +provenance, then compares both providers' complete selection, settings, +initial assignments, outcomes, skip reasons and retry histories on one SHA. +Only execution durations and bound absolute workspace prefixes differ in the +comparison view, apart from explicitly retained CPU visibility differences +under the original `nproc` rule; original bytes remain unchanged. A failed package or +TestMain, a missing terminal verdict, a foreign package, unexpected retry or +altered command prevents parity. + +The live helper fixtures use actual Go, gotestsum and Forge. They cover new +packages and packages without tests, repeated fresh execution, source-relative +contract fixtures, runtime builds, original failure evidence, cancellation, +and stale source/settings or corrupt dependency archives. Hosted fixture +artifacts are retained separately from the complete real-workload reports. +Run both helper test modules, shared routing/adapter fixtures, ShellCheck, +RWX lint and merged Circle validation before pushing. Final hosted evidence, +cache observations and check states belong here after execution; scaffolding +and fixture results do not establish full workload parity. + +The implementation passed eight Linux helper fixtures (including four actual +Go/Forge execution fixtures), ten complete-report comparison fixtures and +35 shared routing/adapter fixtures. The existing wrapper and routing scripts +passed ShellCheck. RWX lint checked the new run and both dependency packages +with zero problems. Circle's merged 5,289-line config and setup config validated, +and processing with all PR workflows and fresh Go tests enabled succeeded. +Hosted execution and cache observations follow below. + +The initial Circle benchmark request (135575) was rejected during setup because +it supplied the continuation's `c-main_dispatch` instead of setup's +`main_dispatch`. No test workload ran in that request. Benchmark dispatch uses +the setup parameter. The shared adapter explicitly accepts Circle's `0`/`1` +boolean environment values as well as `true`/`false`; an actual Circle-style +fixture exercises the fresh setting through the environment without a CLI +override. + +Circle's corrected fresh benchmark 135577 at `6df00e26ca5e1ed99162ef0714b22b11f0d985ef` +executed all 16 packages, 112 initial tests and 2,881 case identities, all passing +without retries. Its original `nproc` returned 32 inside the 8-vCPU Docker +allocation. RWX returned 8 on its requested 8-CPU worker. The adapter now retains +the original CPU command/output and the comparer records this difference while +checking every other setting and each provider's exact effective arguments. +The module's toolchain directive selects Go 1.26.6 on both providers despite +the Mise bootstrap's 1.26.5 pin; the actual toolchains and Go environment agree. + +The initial native Cannon run at `c833ae75459faf15fbf0fea3c6fbdbcc9562618c` +completed both producers and helpers, then failed before tests because archive +restoration tried overwriting a materialized read-only Go toolchain cache file. +The next head reproduced that preflight failure; neither run counts as test +coverage. Complete original failure reports and task logs remain retained; +[first-failure evidence](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-cannon-go-evidence/first-failures.json) records their +original hashes, settings and rejected setup request. +The shared artifact restorer now verifies matching destination bytes and the +corresponding archived payload before reusing an existing regular file. It +preserves the read-only directory/file modes and rejects corrupt archive bytes +even when the existing destination is correct. Six artifact helper fixtures +pass, including repeated read-only restoration, and the live Go/Forge fixtures +exercise this exact cache shape. The corrected full hosted comparison passes. + +[RWX run f536dccd](https://cloud.rwx.com/optimism/runs/f536dccd0d9c419bac8bea355153c09c) +and fresh [Circle pipeline 135579, job 5635468](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135579) +executed the same source revision. Both select all **16 packages, 112 initial +tests and 2,881 case identities**, all passing without skips or retries. The +[complete parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-cannon-go-evidence/parity.json) verifies every selected +source file, initial assignment, original command, effective setting, package +verdict, JUnit identity and split-log bytes. It seals **2,989 Circle and 2,991 RWX +original files**. Circle's hash-manifest-declared empty files are explicitly +recorded; missing nonempty reports fail validation. + +The sole effective concurrency difference is retained explicitly: Circle's +original CPU discovery returns 32 and RWX's returns 8. Both retain the original +`nproc` rule, their exact arguments and outputs, Go 1.26.6, Forge, gotestsum and +Just versions. Every other workload setting, complete selection, result and +retry history agrees. Complete reports remain under +`.ci/rwx-cannon-go-evidence/5ee3/{circle,rwx}`; the index records every original +hash and native dependency binding. The hosted helper task also passed all +eight execution fixtures and ten comparison fixtures; its original fixture +reports are retained separately. These fixtures do not add occurrence coverage. + +Two exact-source CLI dependency-only rehearsals completed with zero verdicts or +helper tests. The first [warm run 358c75a0](https://cloud.rwx.com/optimism/runs/358c75a0894f4179bf4aa10baf73a63d) +executed the producers with reusable tool caches. The unchanged +[replay b1ec0c4a](https://cloud.rwx.com/optimism/runs/b1ec0c4a5cc54e2ebd204d883fd0a84a) +reused both complete module and contract producer task outputs. The +[cache and fixture index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-cannon-go-evidence/cache-and-fixtures.json) retains +their original task/cache identities, tool-cache declarations, source parameters, +zero-test selections and all eight original fixture report seals. Protected +`develop` cache-rebuild events remain unobserved. These are cache correctness +observations; no warm median or speed win is claimed. diff --git a/docs/ai/rwx-cannon-parity.md b/docs/ai/rwx-cannon-parity.md new file mode 100644 index 00000000000..097cfcf46a7 --- /dev/null +++ b/docs/ai/rwx-cannon-parity.md @@ -0,0 +1,97 @@ +# Cannon Rust shadow + +The three remaining Cannon workloads use the shared +`ops/ci/rust-cannon.sh` adapter on both Circle and RWX: +`kona-lint-cannon`, `kona-build-fpvm-cannon-client`, and +`kona-host-client-offline-cannon`. All three pass complete same-SHA hosted parity +and are checked in the [inventory](rwx-parity-todos.md), bringing implementation +coverage to **38/86 = 44%**, including **21/22 Rust workflow jobs**. + +The original Just recipes remain authoritative. Variant discovery retains all +configured clients; lint retains its MIPS target, nightly toolchain and feature +flags; release compilation retains the `release-client-lto` profile. Offline +execution retains the complete pinned OP Sepolia witness and all six original +block/chain/output-root inputs in `ops/ci/cannon-witness.json`. + +RWX prepares the reproducible Docker environment, witness archive and native +Cannon binaries as separate dependencies. It preserves Docker/BuildKit data +through `docker: preserve-data` and keeps isolated host Cargo, Go and sccache +compiler caches. Verdict tasks include run and attempt identities; runtime +states, witness extraction directories, logs and reports are excluded from +reusable filesystem outputs. Protected cache warming builds dependencies and +ELFs without executing the offline guest. + +Each dependency records its source revision, complete input hashes, toolchain, +variant inventory, image/base digests and binary hashes. Consumers reject stale, +corrupt, incomplete or mismatched dependencies. The shared adapter removes old +runtime states before replay and checks the original final `cannon witness` +output: the VM must have exited with code zero, with the expected state version, +a nonempty execution and the original output-root validation log. A successful +Cannon CLI exit alone is insufficient to establish a successful guest. + +Original stage commands, working directories, exits, logs, MIPS ELFs, compressed +final state and JUnit are retained after success, failure and cancellation. +Offline JUnit reports one executed guest; compiling the Interop client does not +claim an Interop replay. + +The Cargo source fingerprint now covers `op-core/nuts/bundles`, which Kona +embeds from outside the Rust workspace. Changed contents receive fresh mtimes +before native builds and Docker COPY; unchanged inputs restore stable mtimes. +Real Cargo fixtures reproduce stale embedded data, verify rebuilding changed +inputs and verify subsequent unchanged-target reuse. Helper fixtures also cover +missing witness data, failed discovery, missing/corrupt binaries, mismatched +producer provenance, a failing VM behind a successful CLI exit, original failure +collection and process cancellation. + +At benchmark revision `2c68e86760cbf76fa9cfb78f58aee5e130ef45a7`, all three pass on +[native RWX](https://cloud.rwx.com/optimism/runs/2e7238e8499d43eda927b1924b579415) +and [Circle pipeline 135550](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135550). +Circle jobs are lint 5633371, build 5633369 and offline replay 5633368. Complete +original reports agree on source/input hashes, settings, toolchains, variant +discovery, effective commands, working directories, outcomes and JUnit. Both +image and runtime MIPS binaries match byte for byte for every selected variant. +The offline guest exits successfully after **1,402,053,869 steps**. Its complete +original witness, witness hash and full final state match between providers; +the compressed final-state hashes also agree. Native Go binaries retain both +host hash sets; host debug paths may differ. Docker image IDs differ, while base +digests, toolchains, inputs and ELF outputs agree. + +The [immutable parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-rust-evidence/cannon-parity.json) retains complete +original-file checksums, commands, producer bindings and guest-state evidence. +Original archives are retained under `.ci/rwx-cannon-stage-evidence/2c68/` and in +the provider artifacts. Circle omits only its zero-byte `guest.log`; its original +manifest declares SHA256(empty). Missing nonempty originals fail comparison. +All verdicts execute on attempt one, with zero observed retries. + +Thirteen Cannon helper tests, eight target-cache fixtures and ten damaged/stale +provider-comparison tests pass locally and on pinned Linux runtimes. Tests cover +the actual shell adapter, fresh state replacement, original parameters, failing +guest detection behind successful CLI exits, report collection and cancellation. +The cache fixtures execute real Cargo rebuilds and unchanged-target reuse. +Routing/Circle fixtures, ShellCheck, RWX lint and merged/activated Circle +validation pass. No speed advantage is claimed. + +An [isolated same-source CLI run](https://cloud.rwx.com/optimism/runs/0e52b2257bb545e992ea227728c12864) +repeated all three workloads freshly, then replayed an intentionally wrong L2 +claim. The unchanged-input repeat passes complete parity against the same Circle +originals, including both ELF inventories and the full final guest state. Every +source fingerprint reports unchanged inputs. Original logs show reusable Docker +layers and retained Cargo targets; the host build finished in 3.08 seconds. The +repeat recorded 5 seconds for environment preparation, 11 for lint, 9 for client +build and 72 for offline execution. Initial executions were 159, 18, 103 and 212 +seconds respectively. These are task execution observations, excluding queueing, +setup and transfers. No full-pipeline or provider speed advantage is claimed. +Sccache reported three uncacheable requests on the repeat; this proves Cargo and +Docker reuse rather than attributing the improvement to sccache hits. + +The wrong-claim guest exited with code one after **1,402,060,522 steps**, while +both the original Cannon run command and witness command returned zero. The +shared final-state guard rejected it, emitted failing JUnit, retained original +commands/logs/state and made the diagnostic task and CLI run fail as expected. +This uses a separate diagnostic definition; it does not replace a passing push +verdict or modify tests. The [cache and failure index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-rust-evidence/cannon-cache-and-failure.json) +retains both successful fresh repeats and the original failure, including the +explicit wrong-claim override. Runtime dependency-order advisories from the +benchmark are retained; the push definition now declares ancestor inputs before +their consumers, as RWX advised. Rust E2E is the next workload stage; final +aggregate checks and optional gate equivalence remain separate requirements. diff --git a/docs/ai/rwx-ci-helpers.md b/docs/ai/rwx-ci-helpers.md new file mode 100644 index 00000000000..3804f3daf01 --- /dev/null +++ b/docs/ai/rwx-ci-helpers.md @@ -0,0 +1,63 @@ +# RWX report helper ownership + +Runtime runners do not import offline comparison scripts. Shared mechanics live +in two modules under `ops/ci`: + +- `ci-report.py` reads and writes report JSON, hashes files, verifies manifest + bytes, and captures one subprocess invocation. It owns signal forwarding and + preservation of the original process exit code. +- `ci-test-results.py` decodes original Go JSON and JUnit. It retains case and + package failures, skips and retry histories. It does not compare providers or + decide whether a suite has complete coverage. + +The modules use only the Python standard library. Existing file-based loaders +work both when a script runs directly and when a test loads it from another +working directory. + +## Caller contracts + +The suite chooses its package or case selection, command, working directory, +required report files, source/settings binding and verdict rules. Contract +upgrades and Rust E2E each check their own final status before passing the +original file manifest to the shared byte verifier. Freshness, cache provenance, +diagnostic reruns and retry ceilings remain in their existing suite owners. + +The byte verifier rejects missing required entries, unsafe paths, invalid hashes +and corrupt bytes. Runtime callers do not authorize reconstruction of missing +files. Offline Circle collectors may explicitly supply an audit list to recover +only a manifest-declared empty file. Recovery records the report, path and hash. + +The subprocess helper executes once and forwards cancellation to the process +group. The stage records the original negative signal status and returns its +shell exit code. Rust keeps its combined log and optional separate stdout. +Contracts keep separate stdout/stderr and their existing redactor. Contract +output is redacted before it is saved, echoed or hashed. The helper does not +retry or turn diagnostic output into a passing verdict. + +Input seals and isolated runtime fixtures include the shared modules wherever +they supply execution behavior. Bootstrap tool inputs remain unchanged because +these modules are consumed from the source checkout, not tool installation. +SP1's filtered toolchain snapshot includes `ci-report.py`. Its regression loads +the runner from exactly the declared filter files and checks agreement with +the producer's sealed toolchain inputs. + +## Regression coverage + +`ci_test_fixtures.py` consolidates report writing, sealing and mutation for the +three contract comparison fixtures. A shared working-directory context also +restores the test process after the NUT CLI runner changes directories. This +prevents a deleted temporary checkout from affecting later fixture groups. +Each fixture still specifies its own +selection, profiles, commands, compiler signatures and original verdicts. + +The coordinator runs the shared helper and existing report decoder regressions. +Tests import runtime runners from a directory with no comparer files, reject +corrupt and missing originals, require explicit empty-file recovery, and drain +stderr larger than a pipe buffer while preserving separate JSON stdout. Existing +suite tests retain real subprocess cancellation, stale compiler/cache rejection, +original failures, diagnostic reruns and Go retry-limit coverage. + +Local verification on October 5, 2026 passed all 554 helper tests (512 passed, +42 opt-in/tool tests skipped). The final comparer/helper batch passed 57 tests; +RWX lint passed for the changed coordinator. Hosted status belongs to the PR's +exact source revision and is recorded separately from these local results. diff --git a/docs/ai/rwx-contract-coverage.md b/docs/ai/rwx-contract-coverage.md new file mode 100644 index 00000000000..96d865d66d2 --- /dev/null +++ b/docs/ai/rwx-contract-coverage.md @@ -0,0 +1,180 @@ +# Contract coverage shadow + +The optional `optimism-contract-coverage-shadow` implements all four configured +coverage feature variants through the existing shared contracts routing. All +four pass complete hosted ordinary/upgrade original-report comparison. Verified +job coverage is 74/86 (86%), with twelve occurrences remaining. +Circle retains every required gate and production publisher. + +The shared adapter expands the original `just coverage-lcov-all` into its exact +ordered, short-circuiting recipes: `just coverage-lcov`, followed by +`just coverage-lcov-upgrade --match-contract "OPContractsManager.*_Upgrade_Test"`. +The upgrade recipe still runs the production `prepare-upgrade-env` and its +`test/{L1,dispute,cannon}/**` filter, ten fork retries and 1,000 ms backoff. The +original default-profile `just build-source` prerequisite stays separate from +the `cicoverage` verdict profile: optimizer disabled, no compilation restrictions, +16 threads, one fuzz run, one invariant run and depth one. Go FFI, recursive +submodules, source paths, fixtures, commit/branch metadata and pinned tools remain +available. Inherited filters and feature switches are cleared and the selected +feature is applied explicitly. + +Each pass adds Foundry's `--report attribution` alongside LCOV. The pinned Forge +1.8.3 emits original per-execution JSON containing identities, outcomes and every +covered source item/hit. Compatible invariant predicates share one campaign and +one attribution row. For multiple file reports, it ignores `--report-file` +and writes the default `lcov.info`; the adapter retains each pass independently +before restoring the original public `lcov.info` and `lcov-upgrade.info` paths. +Reports are retained even after an initial failure. A diagnostic `just test-rerun` +cannot replace the original outcome or overwrite its coverage evidence. Empty +failure caches cannot accidentally run the whole suite as a diagnostic. + +Coverage's human-readable original verdict log is validated against its complete +machine attribution. Native test reporting uses a clearly named **derived** +JUnit view of those originals. No result is fabricated, and comparison rechecks +every derived case, status and skip reason against both original sources. +Ordinary/upgrade prefixes keep reporting identities distinct even when both +passes execute the same Solidity case. The full original identities stay intact. +LCOV entries and complete attribution items are compared without discarding hit counts +or branch details. The comparison index fingerprints every complete attribution +row, including all items and fields, while full originals remain retained. +Compiler signatures, abstract empty-bytecode declarations and +whole-contract setup skips account for every selected case. + +The first hosted run at `463983e15a8817d6894f3c17a67e5de7ab6683fa` failed in +the shared report parser on both providers. All four native producers passed; +the ordinary coverage command exited zero. Main's original engine summary and +attribution contain 2,477 execution records. Three of those records are merged +invariant campaigns with twelve separately reported predicates. The parser now +retains all twelve canonical predicate verdicts, binds each complete campaign to +its actual attribution anchor, and independently validates the original engine +totals. Main therefore has 2,486 predicate/unit verdict identities at this point; +whole-contract setup skips receive their usual complete selection accounting. +Missing members, duplicate names, absent anchors, wrong kinds, unclosed campaigns +and contradictory engine totals fail validation. Campaign coverage stays shared; +the report does not invent individual covered-item lists for merged predicates. +This behavior follows the pinned +[campaign selection](https://github.com/foundry-rs/foundry/blob/cae51ad458f6abb64852b7709eb784352429825d/crates/forge/src/runner.rs#L399) +and [attribution serializer](https://github.com/foundry-rs/foundry/blob/cae51ad458f6abb64852b7709eb784352429825d/crates/forge/src/coverage.rs#L329). +Real pinned Forge fixtures now cover multiple predicates and a failed campaign. +The upgrade pass had not started at the first failure; +this observation does not establish full coverage parity. Original first-failure +reports remain under `.ci/rwx-contract-coverage-evidence/4639/` and immutable +provider artifacts, including both complete ordinary attribution reports. +The [first-failure index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-coverage-evidence/first-failure.json) retains +both complete original seals, canonical predicate events, full changed attribution +items and the source-bound diagnosis. All 150 original files verify against their +seals. Complete selection accounting agrees: 2,308 passes and 551 skips, including +whole-contract setup skips. This remains ordinary-pass evidence, with upgrade absent. + +The opt-in Circle parameter `c-contract_coverage_replay` defaults to false and +preserves its usual unseeded coverage execution. RWX uses a seed derived from the +full tested SHA and feature. A same-SHA comparison must opt Circle into this same +replay seed; comparison rejects unseeded or different-input benchmark reports. +The first complete comparison also found two independent sources of hit-count +differences: `ForgeArtifacts.ensurePath` loops over an absolute output path, and +eight trie tests use Go FFI OS randomness outside Foundry's seeded generator. +Native coverage now clones into `project/`, matching Circle's checkout depth using +[git/clone's supported path parameter](https://www.rwx.com/docs/rwx/packages/git/clone). +All artifact, compiler-cache and report paths follow that checkout; working +directories and fixtures remain relative to the real repository root. +Opt-in replay also binds `OP_CI_FFI_REPLAY_SEED` to the same benchmark seed. Only the +FFI trie helper accepts it, requiring `CI=true` and the `cicoverage` profile. A +ChaCha8 stream is derived from the seed and complete command arguments; the original +trie variants and unbiased range sampling stay intact. Ordinary calls retain +`crypto/rand`. All nine real CLI variants reproduce their complete outputs with +the same input, change with a different seed, and reject invalid replay inputs. +The comparison still rejects every changed LCOV hit or attribution field; these +corrections now pass complete hosted ordinary/upgrade comparison for every variant. +All fuzz/invariant counts, filters and test assertions remain unchanged. Changed +revisions and features receive different samples. This controls the input of the +benchmark; it does not prove arbitrary unseeded runs have identical hit counts. + +The test-only mainnet L1 archive RPC is exposed solely to preflight and runtime +tasks through the existing restricted pilot vault. Preflight executes the +production daily 00:00 UTC block discovery, verifies chain 1 and its complete +block identity, and seals its originals. Circle's existing fork-cache key uses +that same block number. Both consumers revalidate the block and retain the sealed +preflight before testing; comparison rejects different blocks or hashes. RPC +authentication is redacted before streaming or retaining logs, attribution, +generated counterexamples and fixture reports. + +Four isolated producers build source and Go FFI, then compile complete test +signatures with `forge build` under the coverage profile. `forge test --list` +alone creates minimal artifacts without the needed compiler metadata; the real +build is required for authoritative signature bindings. Both ordinary and +upgrade discovery are retained. Consumers reject stale source/settings/tools, +missing or corrupt compiled files, uninitialized submodules and unexpected +tracked fixture mutations before running tests. The selected NUT bundle writer +retains its exact before/after snapshot payloads as in the standard shadow. + +Producers and verdicts start at 16 CPU / 64 GiB, matching the existing coverage +profile's documented memory needs and 16-thread cap. These are supported +[RWX runner specifications](https://www.rwx.com/docs/runner). +Go module/build/runtime caches remain isolated. Runtime Foundry RPC data is +reusable; verdicts, logs, test-failure caches and generated fixtures are excluded +from reusable outputs. Foundry's coverage instrumented compilation executes +inside the fresh verdict; this stage does not claim a compile-only coverage API. +Protected `develop` warming targets only the four producers and executes zero +verdicts or RPC tasks. A CLI warm-only rehearsal must be labeled separately from +an actual protected event. + +Local pinned Linux fixtures execute the production Go FFI build recipe and both +coverage Just recipes, verify upgrade environment/selection, setup skips and an +abstract declaration, prove fresh execution, compare full original LCOV and +attribution, and preserve an intentional initial failure plus diagnostics. +Missing RPC inputs, stale preparation and a corrupt FFI binary fail before +testing. Comparison fixtures reject missing passes/new files, duplicate +assignments, reduced settings, altered commands/archives, changed LCOV hits, +invented derived skip reasons, source mutation, corruption and unexplained retries. +The complete hosted comparison and unchanged-input cache rehearsal below close +these four inventory occurrences. + +## Hosted closeout + +Complete parity passes at `ffefdb34638470dd1126cbf2aaebb4644400b6fb` on +[native run aba3398e](https://cloud.rwx.com/optimism/runs/aba3398e8ef549bb9d4de10f20e6d40d) +and [Circle pipeline 135572](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135572). +All eight original coverage commands pass, with no retries. Both providers retain +all 164 files, 2,882 ordinary signatures and 50 upgrade signatures per feature. +Every ordinary selection accounts for 23 abstract empty-bytecode declarations; +all 2,859 executable ordinary cases and all 50 upgrade cases are accounted for. + +| Feature | Ordinary pass / skip | Upgrade pass / skip | +| --- | ---: | ---: | +| main | 2,308 / 551 | 45 / 5 | +| CUSTOM_GAS_TOKEN | 2,340 / 519 | 45 / 5 | +| OPTIMISM_PORTAL_INTEROP | 2,414 / 445 | 45 / 5 | +| ZK_DISPUTE_GAME | 2,433 / 426 | 50 / 0 | + +All selected cases, setup skips, predicate verdicts, shared invariant campaigns, +skip reasons, compiler signatures, source hashes, effective settings, runtime +fixtures, LCOV entries and complete per-test attribution fingerprints agree. +Every raw LCOV hit and attribution item remains included. The full original +attribution files are retained; comparison does not replace them with sampled +items or aggregate totals. All 762 original files verify against their seals. +The [parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-coverage-evidence/parity.json) retains complete +comparison views, original hashes, provider identities and immutable report links. +Original reports remain under `.ci/rwx-contract-coverage-evidence/ffef/` and the +provider artifacts. This controlled replay establishes parity for these exact +inputs; it does not establish a distributional claim for arbitrary unseeded runs. + +Both providers use L1 archive block 26,122,439, hash +`0x3401750e7c5cb2b34c5290ff470a0058c3c72ccc4317d2c822c2e271c0ff0362`, +selected by the production daily 00:00 UTC policy. Its timestamp is 1,791,158,399. +The preflight and originals retain the actual block verification. Credentials +remain restricted to the preflight/runtime tasks and redacted from reports. + +[Warm-only rehearsal c0c94e11](https://cloud.rwx.com/optimism/runs/c0c94e11276549a9addddf9c87096cee) +reused every compile task directly from `aba3398e`, at the same source and settings. +All four producers passed with zero tests, and the run selected no verdict, +helper or RPC task. The +[cache and failure index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-coverage-evidence/cache-and-fixtures.json) +retains the task provenance, original prepared manifests and hosted intentional +failure reports. The changed source/settings at `ffefdb3` invalidated the earlier +producer outputs and all four compiled again. This is a CLI warm-only observation; +an actual protected `develop` cache-rebuild event remains unobserved. + +All four required Circle gates and dependency review passed at this exact SHA. +The first pushed static-check shadow failed on its diagnosed ShellCheck PATH; +its corrected definition passed the separate rehearsal. The final updated PR +must still reach successful terminal states on its own head. diff --git a/docs/ai/rwx-contract-l2-fork.md b/docs/ai/rwx-contract-l2-fork.md new file mode 100644 index 00000000000..c8b7c0e941f --- /dev/null +++ b/docs/ai/rwx-contract-l2-fork.md @@ -0,0 +1,182 @@ +# OP Mainnet L2 fork shadow + +The optional `optimism-contract-l2-fork-shadow` implements the complete original +`contracts-bedrock-tests-l2-fork op-mainnet` workload. Complete hosted same-SHA +original parity passes at `f821983dd56cbd7e488ab903d1ac386a330de7f6`. Both +providers execute all seven initial cases and retain all 407 runtime relay +requests, with zero diagnostic reruns, transport retries or HTTP 429s. +The [complete comparison](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-l2-fork-evidence/parity.json) retains +source, commands, compiler, selection, settings, block, RPC and original results. +This closes the L2 fork occurrence in the 86-job baseline. + +The definition follows shared Contracts routing. It retains profile `ci`, main +features, every `test/L2/fork/**` signature and compiler artifact, original +working directories and recursive source submodules. The authoritative NUT +bundle check runs immediately before the original `just test-l2-fork-upgrade` +verdict. Failure retains its first JUnit and console evidence, with the original +`--rerun -vvvv` command captured separately. Diagnostic success cannot replace an +initial failure. + +This shadow uses the official public `https://mainnet.optimism.io` endpoint. +Preflight runs before expensive preparation and verifies chain 10, the chosen +block hash, predeploy code, implementation storage and a version call. Complete +original RPC requests, response bodies and attempt metadata remain retained. +Circle's pilot branch and optional isolated replay use that same endpoint and +the exact native block. Circle jobs on other refs use their existing private RPC +selection. + +The live read-only preflight verified block 157803345, +`0xdfa269ad424586197ac2059c1cba15cf08c07f13e4fd3c00406f65626c0031ed`, +including nonempty code/state. The public endpoint is documented in this source +tree and the pinned superchain registry. It supplies no new credential and makes +no transaction submissions. Public archive availability and rate limits remain +runtime constraints. The completed hosted comparison below retains the actual +requests and transport outcomes. + +Go modules, Go compiler outputs, Foundry outputs and pinned solc installations +use isolated reusable producers. Compilation runs on 16 CPUs / 32 GiB and fresh +verdicts initially use the same resources. Runtime Go compilation has its own +cache; reusable runtime outputs contain only that compiler cache and Foundry RPC +state. Test results, failure caches and logs are excluded. Protected `develop` +warming targets compilation and executes zero tests or NUT checks. + +`c-l2_fork_parity_replay` defaults to false. Pilot-only API replay requires +`main_dispatch=false` and a numeric `c-l2_fork_parity_block`. It runs the original +module preparation and original L2 job through the shared evidence adapter. +Routing fixtures pin the exact enabled workflow set and preserve normal pushes, +main dispatches and other branches. Neither provider's parity checklist nor the +Contracts aggregate receives credit until complete original reports agree. + + +The first automatic native run `b2bc1ecdff844e2bbe7367cf321f3002` at `63844aed` +completed compilation in 657 seconds, then rejected an incomplete preflight mount +before executing tests. The consumer received only `block.json`; validation +correctly required its complete sealed RPC originals. The corrected consumer +mounts the entire preflight report, and a regression rejects an unsealed block-only +mount before any NUT or test command. The first failure remains retained. + +For the final pilot batch, `ops/ci/pilot-l2-fork-block.txt` pins block 157804208, +`0xdef17f84f90e7d1bbf71a5ff53e9cb92201f9a31f8b7e63b3d84b55c82008673`. +This is a reproducible comparison input, bound to the complete source SHA, rather +than two independently selected heads. Both providers verify the entire public +block and state again before execution. Explicit numeric CLI blocks remain +available; `develop` continues to discover the latest head. A normal pilot push +executes the same shared original L2 runner in Circle's existing Contracts +workflow and the native coordinator, so the L2 workload and its genuine gate can +be verified alongside the selector replay in one batch. Other Circle refs retain +their original RPC selection and test path. Complete preparation/runtime consoles +are archived as files to avoid provider console truncation; success never hides +an earlier failed command or diagnostic rerun. + +The corrected native coordinator at `936ef20e` ran all seven selected cases +successfully in 77 seconds of fresh verdict execution. Its compilation reused +the compiler cache and executed in 23 seconds. These are task observations, +not a provider speed comparison. Circle pipeline 135628/job 5638523 was killed +after exactly ten minutes without console output during full preparation. +The complete partial preparation stream and all untruncated API logs remain +retained in the [first combined batch](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-pr-gates-evidence/contracts-first-batch.json). +The preparation step now allows 30 minutes without output while retaining its +full original console archive. The failed Circle run adds no parity credit. + +The next combined batch at `a3a09108` completed preparation successfully. +Circle job 5638688's initial verdict reported five failed `setUp()` cases, each +with the same original public RPC HTTP 429 storage-fetch failure. No failing +assertion was replaced: the separate diagnostic rerun passed all seven selected +cases and the shared runner still exited 1. Native fresh execution passed all +seven cases in 59 seconds. Complete initial and diagnostic JUnit, request frames, +console streams and all untruncated Circle API logs are retained in the +[second combined batch](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-pr-gates-evidence/contracts-second-batch.json). + +Both providers now run the complete verdict with `--threads 1` and +`--compute-units-per-second 100`. The pinned Forge 1.8.3 binary and source support +these native controls. Only L2 suite concurrency and RPC throughput change; +the complete selection, profile, fuzz/invariant settings, fork height, original +NUT check and ten transport retries remain intact. The shared runtime settings +are recorded and compared, and changed/omitted limits fail verification. +The diagnostic rerun uses the same controls and cannot make an initial failure +pass. Twenty-one helper/comparison fixtures cover these boundaries and the +existing failure, source, compiler, block and report checks. They add no coverage. + +The backoff-only combined batch at `cb341ad0` still hit public RPC HTTP 429 +on both providers. Native's initial verdict retained four passing cases and +two failed suite setups; its separate diagnostic rerun passed all seven and +correctly kept the job and Contracts gate failed. Both complete first verdicts +remain retained. Forge's assumed compute-unit budget adjusts retry backoff; +it does not proactively pace each request. + +The shared runner now sends fork traffic through a loopback transport with one +upstream request in flight and a global budget of two requests per second. +Request bytes, IDs, response bodies, timestamps and every transport attempt are +retained. Only HTTP 429/500/502/503/504 and connection failures receive bounded +backoff; permanent denials and RPC execution errors pass through immediately. +The relay caches no state and uses the same public endpoint. Test assertion +failures still retain the original failing verdict and separate diagnostics. +Complete transport validation checks its source-bound policy, every frame/hash, +response IDs, pacing and retry bounds. Common stable archive results must agree +across providers; different request inventories from Foundry's existing RPC +cache remain retained. Live head metadata is identified separately from pinned +archive state. + +An isolated Linux probe ran the pinned Forge 1.8.3 binary and Solc 0.8.15 through +the actual relay against the real pinned OP Mainnet block. Both tests verified +chain, block and nonempty implementation storage and passed. All 19 requests +and attempts are retained, with zero 429s. Five unsupported `anvil_nodeInfo` or +`eth_getAccountInfo` probes returned original HTTP 403 method-denial responses; +Forge's normal fallback succeeded and the relay did not retry those denials. +This integration probe and 30 helper/comparison fixtures add zero coverage. +The [transport preflight](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-pr-gates-evidence/contracts-pacing-preflight.json) +binds those complete originals and the prior full-workload failures. + +The combined automatic batch at `3be585cb` passes the original result comparison; +its effective Circle runtime transport was subsequently found unverified. +Circle pipeline 135642, original [job 5639658](https://circleci.com/gh/ethereum-optimism/optimism/5639658), +and native [run d287cd4e](https://cloud.rwx.com/optimism/runs/d287cd4ef59b4c02b11cfbbf03d0e7f5) +both pass all seven initial cases, with zero skips or diagnostic reruns. All +selection, compiler methods and bytecode, effective settings, submodules, +commands, original outcomes and pinned-block frames agree. Circle retains all +58 report files and complete untruncated API logs; native retains all 1,686 +report files, including 407 upstream requests and their complete response frames. +Native observed zero HTTP 429s and zero transport retries. Thirteen original +HTTP 403 capability denials passed through once; Forge's normal fallback succeeded. + +Circle's relay report contains zero requests. The next batch at `30a54d78` +failed its initial verdict on HTTP 429, again with zero relay requests. A real +shell reproduction establishes that Circle's `BASH_ENV` reinitializes nested +Bash shells and overwrites the runner's loopback URL with the public endpoint. +The earlier attribution to warm Foundry state was incorrect. The Python runner +now removes `BASH_ENV` after the caller initializes the job environment and +before applying its runtime overrides. Exported job values remain inherited. + +All 32 L2 helper/comparison tests pass, including a real nested Bash request +through the relay. A pinned Forge 1.8.3 / Solc 0.8.15 probe runs through two +nested Bash shells against the original pinned archive block: two tests pass, +all 19 requests use the relay, and no request receives HTTP 429. Five original +capability-denial HTTP 403 responses pass through once. Complete frames and +implementation hashes remain in the [corrective preflight](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-pr-gates-evidence/contracts-shell-preflight.json). +These local checks add zero hosted workload coverage. + +The reader still accepts sealed zero-request reports: a fully warm RPC cache +can validly need no upstream calls. That condition must not be inferred from a +zero count alone. The complete fresh verdict and pinned-block checks still apply; +all 15 comparison fixtures pass without changing any original report. + +Circle's full contract build took 649.3 seconds and its fresh verdict took 58.9 +seconds. Native compilation executed in 19 seconds and its fresh verdict task +executed in 240 seconds on 16 CPUs / 32 GiB. These observations have different +runtime transport paths and do not establish a provider speed comparison. The +prior Contracts aggregate comparison also retains all 21 exact prerequisites +and its fresh final status. The corrected combined verification below supersedes +that runtime-path evidence. + +The corrected combined batch at `f821983d` proves the actual nested-shell fix. +Circle pipeline 135650, original [job 5639942](https://circleci.com/gh/ethereum-optimism/optimism/5639942), +and native [run 7a0f17fd](https://cloud.rwx.com/optimism/runs/7a0f17fd80244703bc3b2a25c941c2da) +both pass all seven initial cases and preserve the same complete settings, +compiler artifacts, commands and pinned block. Each report contains all 407 +relay requests and attempts: 394 HTTP 200 responses and thirteen original HTTP +403 capability denials. Forge's existing fallback succeeds; the relay retries +neither denial. Both providers record zero HTTP 429s and zero transport retries. +Complete original collections and sealed reports pass the strict reader. Both +genuine Contracts gates also pass their 21 prerequisites. The historical +[3be5 comparison](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-l2-fork-evidence/3be5-batch-parity.json) and complete +first failures remain retained; no original failed verdict was replaced. diff --git a/docs/ai/rwx-contract-suites.md b/docs/ai/rwx-contract-suites.md new file mode 100644 index 00000000000..7c045a7e66e --- /dev/null +++ b/docs/ai/rwx-contract-suites.md @@ -0,0 +1,173 @@ +# Standard and changed-file contract suites + +The existing optional `optimism-contracts-shadow` now uses one shared adapter for +the four standard feature variants and four changed-file heavy-fuzz variants. +All eight now pass complete same-revision original-report comparison. +All eight occurrences have verified parity. The current +[inventory](rwx-parity-todos.md) is 74/86 (86%), with twelve remaining after the +coverage and static-check closeouts. Circle retains its required gate. + +`contract-suites.py` preserves Circle's original `find test -name "*.t.sol"` +selection for standard tests, and the exact `git diff origin/develop...HEAD` +added/modified `.t.sol` selection for heavy fuzzing. Circle still invokes its +actual `circleci tests split --split-by=timings`; the current job has one +partition. Native records an equivalent complete assignment. Both retain the +original file-selection and split logs, configured parameter, target revisions, +full Forge discovery and compiler signatures. Empty changed-file selection +prepares zero tests and disables native verdicts; it cannot establish coverage. + +Standard profiles remain `liteci` on PRs and `ci` on `develop`: 128 fuzz runs, +64 invariant runs and depth 32. Modified tests retain `ciheavy`: 20,000 fuzz +runs, 128 invariant runs, depth 512, and 300-second fuzz and invariant timeouts. +The original profile, feature and filter parameters are checked against actual +settings. No inherited environment may silently reduce the workload. + +Each suite/feature compiles independently on 16 CPU / 32 GiB with isolated +Foundry and Go compiler caches. Fresh verdicts start from verified compiled +artifacts on 16 CPU / 32 GiB and preserve full source paths, Git history, +submodules, fixtures, toolchain and revision metadata. Runtime Go compilation +has a separate cache. The original `forge test --match-path ... --junit`, +nonempty-JUnit guard and `just lint-forge-tests-check-no-build` execute. Initial +failure evidence remains intact when `just test-rerun` emits diagnostic traces. +Reports, outcomes and generated fixtures are excluded from reusable outputs. +Compiler-only protected warming runs zero verdicts. + +The adapter seals complete tracked inputs, effective configuration, producer +settings, compiler outputs, selected signatures, complete original JUnit and +each command/log/exit/signal. Consumers reject stale settings/revisions, +missing/corrupt binaries, changed target history, fixture selection or tools. +Comparison revalidates each file assignment and signature against original +discovery and accounts for deployable cases, abstract bytecode declarations and +whole-contract setup skips. Original skip reasons remain untouched. The first +benchmark retains two reasonless Interop L1Block skips with their exact source +guard; the follow-up below verifies the explicit messages on both providers. + +Submodules are explicitly initialized recursively before provenance validation; +the Circle checkout does not initialize them by itself. Preparation must leave +every tracked input unchanged. `GenerateNUTBundleTest` intentionally invokes the +real script's `run()` and writes +`snapshots/upgrades/current-upgrade-bundle.json`. When that writer is selected, +the adapter retains this exact snapshot as both an initial fixture and a runtime +output. Its before/after payloads and hashes are sealed, source changes are +enumerated, and comparison requires the same generated payload on both providers. +Any other tracked input mutation still fails. Runtime fixture outputs never +enter reusable compiler outputs. + +Existing skip conditions now emit their reasons through Foundry's supported +`vm.skip(bool,string)` interface: production bytecode requirements, coverage +instrumentation, fork/ops-repository exclusions, and manual resource-metering +CSV generation. No guard or test assertion is weakened. Complete hosted reports +must verify the emitted reasons and unchanged outcomes. + +Linux fixtures execute the real production Just Go FFI recipe, production Go +convention validator, Forge discovery, both profiles and fresh original verdicts. +They reproduce an intentional failure, retain its original XML and diagnostic +rerun, reject a corrupt reused binary before tests, and prove empty changed-file +selection emits zero tests. The identity split fixture exercises stdin/CLI wiring +outside Circle; hosted comparison must use Circle's actual splitter. Comparison +fixtures reject omitted new files, duplicate assignments, matching wrong +commands, reduced fuzzing, stale inputs, corrupt originals, extra unsealed files +and unexplained retries. + +The live fixture also executes an invariant in the complete standard manifest +and verifies that the original changed-file selection includes only the modified +unit/fuzz file. It initializes a previously deinitialized submodule and rejects +undeclared source mutation and corrupt tracked fixture evidence. A timed-out +fixture terminates the runner gracefully so Forge receives cancellation before +temporary-file cleanup. + +A documentation-only comment in `test/libraries/Bytes.t.sol` exercises the actual +changed-file selector on the pilot branch. All of that existing file's cases +must run under the full heavy profile on both providers. No test behavior is +changed to obtain a passing check. + +At `8236d17b`, all four native heavy variants passed all 14 selected cases. +All four native standard test commands and naming validators passed, but the +adapter rejected the intentional tracked snapshot write. A fresh pinned Linux +reproduction retained the exact changed path and before/after hashes. Circle +failed earlier on uninitialized submodules. Its four heavy fallback diagnostics +were canceled after their original preparation failures were retained; they +provide no Circle heavy-suite verdict. The fallback now requires an actual +test-failure cache, preventing an empty `--rerun` from executing the full suite +after preparation failure. These are retained first failures, not parity proof. +The complete original file hashes, provider observations and exact diagnostic +change are retained in [first-failures.json](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-suites-evidence/first-failures.json). + +At `c702cfb2d41a0220383cebdf9013530dd25d50c0`, +[RWX run 96f1589c](https://cloud.rwx.com/optimism/runs/96f1589c68bc418eacd5d37bfc259585) +and [Circle pipeline 135567](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135567) +passed all eight complete original comparisons. Standard selection contains all +164 files and 2,882 signatures, including 23 non-executable abstract declarations. +The 2,859 executable outcomes per feature are: + +| Feature | Pass | Skip | +| --- | ---: | ---: | +| main | 2,313 | 546 | +| Custom gas token | 2,345 | 514 | +| Interop | 2,419 | 440 | +| ZK dispute game | 2,438 | 421 | + +Each heavy variant selects the actual modified Bytes and ResourceMetering files, +with all 28 cases: 27 passes and one explicitly skipped manual CSV generator. +All settings, file assignments, original XML/skip details, source/fixture hashes, +selected compiler signatures and invocation histories agree. No diagnostic rerun +or native task retry occurred in these successful verdicts. All 154 GitHub checks +at this benchmark are terminal: 153 successful and one neutral, including the +four required Circle gates and every optional RWX check. + +Restored Foundry outputs contain 25 additional obsolete line-numbered +`VmContractHelper` interfaces. Every one has empty creation bytecode and no test +or invariant selector. Complete original bindings are retained; comparison checks +every executable contract and test-bearing abstract declaration and records these +non-test interface differences explicitly. Regression coverage rejects any extra +executable binding or test/invariant selector. The two original Interop L1Block +skips lack emitted reasons; their exact existing feature condition is sealed in +the evidence. All other original skip records have reasons. No original reason +is invented or rewritten. + +## Current-source follow-up + +All eight occurrences also pass complete original-report comparison at +`ffefdb34638470dd1126cbf2aaebb4644400b6fb` on +[native run ffab3bbe](https://cloud.rwx.com/optimism/runs/ffab3bbedad3416eb8424baef0d13abe) +and [Circle pipeline 135572](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135572). +The [follow-up index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-suites-evidence/ffef-parity.json) retains every +selection, outcome, skip, source/tool setting, compiler binding, fixture mutation +and original report hash. All 950 original files verify. Full originals remain +under `.ci/rwx-contract-suites-evidence/ffef/` and the provider artifacts. + +Standard selection and outcomes remain the complete 164-file/2,882-signature +workload above. The changed-file authority now selects three real files: +`ResourceMetering.t.sol`, `L1Block.t.sol` and `Bytes.t.sol`. The additional changed +L1Block file brings heavy selection to 58 signatures: 56 executable cases and two +abstract declarations. The full `ciheavy` counts/timeouts remain unchanged. + +| Heavy feature | Pass | Skip | +| --- | ---: | ---: | +| main | 50 | 8 | +| CUSTOM_GAS_TOKEN | 53 | 5 | +| OPTIMISM_PORTAL_INTEROP | 48 | 10 | +| ZK_DISPUTE_GAME | 50 | 8 | + +The two Interop L1Block cases now emit the exact original reason +`Interop is already enabled by the dev feature` in both providers' standard +JUnit. Their existing feature condition and assertions remain unchanged. The +comparison confirms these messages directly; the historical reasonless records +remain intact in the first benchmark evidence. + +[Compiler-only rehearsal ef8b816c](https://cloud.rwx.com/optimism/runs/ef8b816ca9fe470f8e34369cdc9d8fa7) +passed all eight producers at the original `c702cfb2` benchmark revision with zero tests and no +verdict tasks. Every build reports unchanged compilation skipped. Every contract +artifact and Go FFI binary is byte-identical; only +`cache/solidity-files-cache.json` changes. These tasks executed and reused native +compiler data; they were not filesystem task-cache hits. This is a CLI rehearsal, +not an observed protected `develop` cache-rebuild event or a speed comparison. + +[parity.json](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-suites-evidence/parity.json) retains the complete shared +source hashes, authoritative selections, per-case outcomes and original skips, +all provider report hashes, generated fixture changes, interface reconciliation, +final checks and the eight preparation-only original inventories. Raw originals +remain in both hosted runs and in `.ci/rwx-contract-suites-evidence/c702`. +Case and skip-reason catalogs deduplicate repeated details; decoding is checked +against every complete original comparison result without dropping fields. +Coverage, L2 fork and gate equivalents remain separate work. diff --git a/docs/ai/rwx-contract-upgrades.md b/docs/ai/rwx-contract-upgrades.md new file mode 100644 index 00000000000..34ecdd4c9cc --- /dev/null +++ b/docs/ai/rwx-contract-upgrades.md @@ -0,0 +1,123 @@ +# L1 upgrade shadow + +The seven Circle PR upgrade occurrences are implemented by +`.rwx/contract-upgrades.yml` and `ops/ci/contract-upgrades.py`. All seven pass complete hosted, same-revision original-report parity at +`58a81fbdb7f1c6184b881417f4d74c50c73f13d6`. The optional check is `optimism-contract-upgrades-shadow`; Circle keeps +its required contract gate. + +| Occurrence | Chain | Feature | +| --- | --- | --- | +| feature-main | op | main | +| feature-CUSTOM_GAS_TOKEN | op | CUSTOM_GAS_TOKEN | +| feature-OPTIMISM_PORTAL_INTEROP | op | OPTIMISM_PORTAL_INTEROP | +| feature-ZK_DISPUTE_GAME | op | ZK_DISPUTE_GAME | +| chain-op | op | main | +| chain-ink | ink | main | +| chain-unichain | unichain | main | + +The two op/main occurrences execute separately. Each has a dedicated compiler +and verdict task. Compilers and verdicts use 16 CPU / 32 GiB, with isolated +Foundry/Go compilation caches. Module preparation is shared and verified. +Fresh verdicts retain only the separate Foundry RPC cache; results and logs are +artifacts. Protected `develop` warming targets compilers and executes zero tests. + +The shared adapter preserves `liteci` on PR branches, `ci` on `develop`, fuzz +seed 42424242 and one fuzz run. The original `just test-upgrade` recipe selects +all L1, dispute and Cannon tests. Compiler preparation explicitly builds the +contracts before `forge test --list --json`: Forge 1.8.3's list mode alone +produces ABI-only artifacts on a cold checkout. Full method identifiers resolve +bare discovery names, including overloads, into the complete JUnit signatures. +Discovery also lists inherited tests on abstract contracts. The complete list +is retained; only declarations bound to empty compiler creation bytecode are +classified as non-executable. A skipped `setUp()` expands into skipped members +of that same deployable contract, with the original setup verdict and skip +reason retained. Every other absent, extra or conflicting verdict fails. +Runtime workers verify the revision, tools, settings, complete recursive +submodule commits, source inputs and every compiler artifact before execution. + +The existing test-only L1 archive vault is used by RPC preflight and runtime +workers. Preflight precedes expensive builds, verifies chain ID 1 and the +original Just daily 00:00 UTC block, and records its height, hash and timestamp. +Runtime verifies that same block again. Compiler tasks receive no RPC input. +Authentication is masked before dependent output is streamed, saved or hashed. +Circle uses the same adapter and retains the original JUnit plus separate +failed-test diagnostics; a passing diagnostic cannot replace a failed verdict. +Cancellation preserves the process signal and partial original output. + +Twenty-one execution/comparison fixtures pass with pinned Linux Go, Forge and +Just. The complete adapter fixture uses the production build/runtime recipes, +real Go FFI compilation, real Solidity tests and a local read-only RPC server. +Two runs from the same compiled manifest each execute the FFI test afresh; +corrupt FFI bytes fail before execution. An intentional failure retains both +original and diagnostic JUnit. Other fixtures exercise full signature discovery, +individual and whole-contract setup skips, abstract and inherited tests, +wrong/unavailable block inputs, redaction, process-group cancellation, +corrupt originals, stale revision/settings, missing cases and uninvestigated +retries. Routing and Circle adapters pass all 35 scenarios. RWX lint, shell +blocks and merged/activated Circle configuration validation pass. + +Hosted comparison must retain every original report, complete selection, +compiler/signature bindings, settings, exact block and retry history for all +seven occurrences on the same SHA. `ops/ci/compare-contract-upgrades.py` rejects +missing, extra, corrupt, failed or different inputs and outcomes. A date-boundary +block difference requires a new comparable observation. No workload has been +added to the verified count by these local fixtures. + +[First-failure evidence](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-upgrades-evidence/first-failure.json) +retains the initial shared-client RPC error on both providers. All seven +Circle preparations discovered 1,359 complete test signatures each, while the +native preflight prevented compilation and verdict execution. Archive-client +headers now match the passing Go preflight, with safe numeric error categories. +The CLI identity correctly remains unable to unlock this test-only archive +vault; original reports are retrieved through the existing authorized user +access without changing credential permissions. + +The follow-up observation at `59f3f33f72354f2521525a26a331fa771b6c1edc` +passed RPC preflight, every compiler and all seven original Just upgrade +commands on both providers. Complete raw JUnit identities, outcomes and skip +reasons match for every variant. The new adapter nevertheless failed because +it expected individual verdicts for setup-skipped contracts and four methods +on an abstract test initializer. The corrected compiler/skip classification +above is covered by actual pinned Forge execution. This failed observation +remains in the evidence index and adds zero verified occurrences pending a +successful hosted run of the corrected adapter. + +The initial compiler-only warm rehearsal also exposed skipped-preflight +propagation: referring to its absent `ready` value skipped every compiler. +Producers now use a status-only `after` condition that accepts successful or +intentionally skipped preflight, plus the existing contract-route/warm-only +condition. Failed preflight cannot start producers. RWX's +[task dependency documentation](https://www.rwx.com/docs/after) describes the +status scope; run-initiation validation rejects initialization parameters +inside that expression. A skipped-only run is not evidence of cache warming. + +The first hosted bytecode-classification attempt at `1926d60b` rejected multiple +compiler contexts before verdicts. The original artifacts include twenty +contracts compiled with more than one Solc version; identical method signatures +can have distinct creation bytecodes. Every artifact now retains its own size +and hash. Only a contract whose complete bound artifact set has empty creation +bytecode is non-executable. Cross-provider comparison requires equal signatures +and deployability while preserving each provider's compiler artifact hashes. + +The successful benchmark is [native run bcab8b6f](https://cloud.rwx.com/optimism/runs/bcab8b6f456b42da908aefb5d4867773) +and [Circle pipeline 135563](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135563). +The [parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-contract-upgrades-evidence/parity.json) retains complete +selection, original outcomes, skip reasons, settings, source/tool/compile +bindings, block identities and original-report hashes. Every variant has +1,359 discovered signatures: 1,355 executable and four non-executable abstract +declarations. Main, CGT and each chain occurrence report 861 passes / 494 skips; +Interop reports 983 / 372 and ZK reports 986 / 369. All seven original commands +succeeded with no diagnostic rerun or task retry. This adds seven verified +implementation occurrences, reaching 56/86 (65%); thirty remain. + +The compiler-only rehearsal completed all seven targets with zero tests. A +separate intentional preflight failure kept producers and verdicts skipped. +Protected `develop` cache-rebuild events remain unobserved until these +definitions reach that branch. The complete compiler-context rehearsal verified +the executed helper hash: an explicit CLI `commit-sha` override suppresses +uncommitted helper patches, so patched rehearsals must omit that override. +These rehearsals do not substitute for source-matched hosted verdicts. + +All four required Circle gates, dependency review and all nine optional RWX +checks passed at the benchmark revision: 146 successful checks, one neutral, +zero unfinished or failed checks. Later pushes require their own observations. diff --git a/docs/ai/rwx-evidence-index.md b/docs/ai/rwx-evidence-index.md new file mode 100644 index 00000000000..eb07568907a --- /dev/null +++ b/docs/ai/rwx-evidence-index.md @@ -0,0 +1,119 @@ +# RWX pilot evidence archive + +PR #23151 covers all 86 baseline CircleCI PR job occurrences. Its generated +reports are archived outside Git. The repository retains this index, +[summary checksums](rwx-evidence-summaries.sha256), and human-written workload +closeouts linked from [the coverage checklist](rwx-parity-todos.md). + +## Archive identity and custody + +The archive captures source revision +`a1aa49aaf3713a8172f3f615f094488fd8e39c3d` on October 5, 2026. +It contains 216,558 report paths, including complete provider originals, +preparation and test logs, selections, settings, retries, failure fixtures, +provider run/check observations, and the 92 previously committed summaries. +Deduplication and Zstandard compression reduce 38,238,271,163 logical bytes +to 1,347,568,690 bytes of report objects. The manifest and source bundle bring +the complete archive to about 2.4 GB. + +Two private copies are retained: + +- Operator workstation: + `/Users/edward/Workspace/op/rwx-ci-pilot-evidence/2026-10-05-a1aa49aa/` +- Existing CI validation host, accessed through the operator's `hetzner` SSH + alias: `/home/admin/.local/share/optimism-ci-evidence/2026-10-05-a1aa49aa/` + +Both archive directories are private to their owner. They are not public +downloads or managed artifact storage. An operator with access to either copy +must supply the archive to a reviewer. Keep both copies until the team assigns +an evidence storage location and retention policy. + +| File | SHA-256 | +| --- | --- | +| `manifest.json` | `450aaa5e07daa581c258feb3e94b713215d382f559cfd66186f8fa3efa09b8b3` | +| `source.bundle` | `9a548a480c0b2c8636d1074f3e2ed3a5d8ba2853353be83f23f8cc2919a87c3a` | +| `restore.py` | `bd419f52c0f7d5a9e707ac4d984334e769f1c386d591c1658e079e7eeb84e320` | + +The manifest binds each report path, byte count, mode, original SHA-256 and +compressed object parts. It retains original collection seals and labels the +historical exceptions below. `SHA256SUMS` in the archive covers every stored +file. `verification-record.json` records restoration and comparison results. +The self-contained Git bundle preserves the pilot branch's source history +through the captured revision. No archive, bundle or compressed object is added +to the repository. + +## Retrieve and restore + +Copy the whole archive directory, including `objects/`, outside a checkout. +For the existing private host: + +```bash +rsync -a hetzner:/home/admin/.local/share/optimism-ci-evidence/2026-10-05-a1aa49aa/ ./rwx-evidence-archive/ +``` + +Use Python 3.11 or newer and the `zstd` command. Check the three identity hashes +against the table above before running the archived helper. From the archive +directory, verify all stored files and restore into a new directory: + +```bash +sha256sum -c SHA256SUMS +python3 restore.py \ + --manifest-sha256 450aaa5e07daa581c258feb3e94b713215d382f559cfd66186f8fa3efa09b8b3 \ + --destination ../rwx-evidence-restored +``` + +On macOS, use `shasum -a 256 -c SHA256SUMS`. Omit `--destination` to verify +every original without retaining decompressed files. Add repeatable `--prefix` +arguments to restore selected families, for example +`--prefix .ci/rwx-l2-evidence/f821/ --prefix captures/`. +The helper rejects a changed manifest, missing or corrupt compressed parts, +changed original bytes and an existing destination. Identical restored files +can share hard links; treat the restored originals as read-only. + +Use `git clone source.bundle ../rwx-pilot-source` to recover the recorded source +and run the existing `ops/ci/compare-*.py` tools against restored `.ci/` reports. +Provider run/check captures reside under `captures/` or alongside their report +families. They retain original source revisions and run identities. + +Historical summary links in the workload closeouts use the immutable +[captured revision](https://github.com/ethereum-optimism/optimism/tree/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai). +Their exact bytes are also recoverable from the archive and checked by the +92-line summary checksum file. Deleting them from the PR's final tree does not +remove commits already pushed to the pilot branch. A future squash merge can +land the reviewed final tree without importing those earlier report blobs. + +## Verified coverage and historical exceptions + +Archive validation restored every report path on the workstation and independently +verified every original on the private CI host. All 23,141 unique objects passed +their compressed and decompressed hashes. The existing L2 fork, Contracts gate, +selector and flaky-report comparers passed against restored originals. Empty-file +restoration passed; changed manifests, missing/corrupt parts and an existing +restore destination were rejected. All 92 summary hashes and 97 historical report +links were verified before deleting the generated files from the final tree. +The source bundle passed a clean clone to its recorded revision and branch, +followed by `git fsck`. An initial bundle inherited shallow history and failed +that clone rehearsal; its original and failure log remain under `validation/`. + +The authoritative last three workload comparisons use +`f821983dd56cbd7e488ab903d1ac386a330de7f6`: complete L2 fork reports, the 21 +Contracts prerequisites, and selector publication/readback. Final readiness at +`a1aa49aa` passed all four required Circle gates, dependency review and all 23 +optional RWX checks. These are historical observations; a later PR head needs +its own terminal checks. Earlier workload revisions and complete failure +evidence remain in the archive. + +The archive labels these older collection limitations explicitly: + +- 113 unavailable files belong only to the incomplete preliminary selector + collection `.ci/rwx-selector-evidence/86f`. They are not part of the passing + `6384` or `f821` selector comparisons. +- 4,368 missing local paths were recovered from other collected copies with + the exact SHA-256 required by their original seals. +- 188 files declared empty by original manifests are retained as empty files. +- Ten regenerated derived files under `.ci/rwx-rust-stage-evidence/6a/derived` + differ from their older seals. The observed bytes and original expected + hashes are both recorded. The authoritative `68ad` Rust originals are intact. + +These exceptions receive no new parity credit. The archive preserves them for +audit rather than treating every historical collection as complete. diff --git a/docs/ai/rwx-evidence-summaries.sha256 b/docs/ai/rwx-evidence-summaries.sha256 new file mode 100644 index 00000000000..c0a7c08ee1d --- /dev/null +++ b/docs/ai/rwx-evidence-summaries.sha256 @@ -0,0 +1,92 @@ +7357ff4b8184339e8074da406e33d2554afa033106886c7678a5c5a58faf506f docs/ai/rwx-acceptance-evidence/cargo-freshness-failure.json +3d5018c20c8783ba96a1464aeb4e4066682edc050b86e30685730dabbf883d92 docs/ai/rwx-acceptance-evidence/first-failure.json +52b4f442bbff2d9064c8145f778d9ed292f8ce5305d7775cb8989d769242b6b2 docs/ai/rwx-acceptance-evidence/parity.json +7e37cec16c12da3ee3b54464447c99614f24d9b8ebd17c5ef8d07c82ae76e5bf docs/ai/rwx-cannon-go-evidence/cache-and-fixtures.json +6c75a3cfdf559228ed8fb2a83baa9b798db99823dfb62e55bbe64a529a3a8143 docs/ai/rwx-cannon-go-evidence/first-failures.json +5bf55dbe7d27f0549f6e761fb72cef20618a28e1ae12680e460f056bc8787238 docs/ai/rwx-cannon-go-evidence/parity.json +b44d7593f7b79e3c4f147482590251d96ba5b8f3ede12c042f29047d04131877 docs/ai/rwx-contract-coverage-evidence/cache-and-fixtures.json +b60db3aff31458441454198a5f16e8da0dcf48f4392b79faaacd8ede100d851a docs/ai/rwx-contract-coverage-evidence/first-failure.json +d6ccc0672d264baae35552cc6ee44e524a8ff0e36eea68c128bdfd565882944a docs/ai/rwx-contract-coverage-evidence/parity.json +c4726e477b90f11fcdc298ba944c507405dfc9f6d08a1c4cc56fd59089b29f2a docs/ai/rwx-contract-l2-fork-evidence/3be5-batch-parity.json +03605659438b00adaf3d8699f59ffd2626a701ec01b8d390fe28c8b408a03425 docs/ai/rwx-contract-l2-fork-evidence/first-hosted-failure.json +1f5139a0e9ac45430966c80bf59aa81e934887f0db260a09534d55d0221d2284 docs/ai/rwx-contract-l2-fork-evidence/parity.json +4902886b94e6b6802e16c2a1703fadea2ca79c869fc2cd8e595e60fb8eaeb9bb docs/ai/rwx-contract-suites-evidence/ffef-parity.json +4439c0c53a5d4826b886f5a207cca1ef83b605ba4b328f3c8c3d46766063796e docs/ai/rwx-contract-suites-evidence/first-failures.json +2315a4ed4775c264a82ba653b4969da46bf8101a1f7b7f9eaa0c4fb52c9d44c4 docs/ai/rwx-contract-suites-evidence/parity.json +36e1529dd10309372a6fb0a28557ec05df774570fb40ec8bb0a5c3cd178d02f8 docs/ai/rwx-contract-upgrades-evidence/first-failure.json +0557a64d64a0a8c0c905282075469004e90920f093942d0993afb9a2f4a32a39 docs/ai/rwx-contract-upgrades-evidence/parity.json +3c9733556eee4e056ac53a0806118b2fea11f69737e29898100c43321f8ae168 docs/ai/rwx-fetcher-artifacts-evidence/first-native-failure.json +4a86353e8c5ecd92288448fca17cd16985576a57880818742021abde4c3aa0a4 docs/ai/rwx-fetcher-artifacts-evidence/fixture-originals.json +8ed7900f077dc78809e0f8899b0613e4b26beaf3604fb84cde4a8b068459e598 docs/ai/rwx-fetcher-artifacts-evidence/parity.json +b97ab7885b0bd08e2c6452dc591ebff389f44e4f6ce90fd0d2243e2e5232dbfd docs/ai/rwx-fetcher-artifacts-evidence/preflight.json +09d63c59cb1a894cbacf0e1504f32811a03ca15b168fa9241123794653c03f33 docs/ai/rwx-fetcher-artifacts-evidence/tool-only-reuse.json +67a9cce163718c136090015447c2c2f40f0650dbf1fca8854168729711556d26 docs/ai/rwx-flaky-report-evidence/parity.json +3729034b49766f77c55486a09f8a9659dc63dce8cb5f672d6ef010cc363b654b docs/ai/rwx-go-evidence/parity.json +cede5340b94b509a0ba133243a8a3769fc1fe4e39bedb3fef5efa53248dafb6c docs/ai/rwx-kontrol-build-evidence/compiler-inventory-discrepancy.json +ae66f8467ff6bcc668cf71efeeef76f9f34a8c93252b17aa01d8cddd2207963e docs/ai/rwx-kontrol-build-evidence/complete-compiler-input-probe.json +0f641692d4235847b97f27ddb449eb1614a64896f7b23f51d7001045b87ecd7b docs/ai/rwx-kontrol-build-evidence/complete-input-fixtures.json +081db3cc1071e7661d4be1ec12ea94c7f2010f009e434e86716b05ed1b8697c2 docs/ai/rwx-kontrol-build-evidence/first-full-preflight.json +70958864ba916d8d15b759fc556b4db5714d007d29160eca584d46768922fe8b docs/ai/rwx-kontrol-build-evidence/first-native-image-failure.json +64017275e684bb2a56199421df80aa87eda7d4c7db8a8d1c7753afaf869d0718 docs/ai/rwx-kontrol-build-evidence/hosted-parity.json +458d81ba6133bfc8b37074080bb148974a6e1cadd15f1cfe09e48b682044ec2b docs/ai/rwx-kontrol-build-evidence/image-identity-fixtures.json +858f004144d61b005997f56bb8b7cd15673850286c96e0e5ec0502d9c9a9e204 docs/ai/rwx-kontrol-build-evidence/immutable-image-manifest.json +8f7f1f056746f8d56fe8dc3c5f09c80eae03511bb48fab2c64075a40f432e66a docs/ai/rwx-kontrol-build-evidence/local-fixtures.json +f006f62bf704e4cf1d837616a5291e66120299a26ea898bd843726e7ea132595 docs/ai/rwx-main-checks-evidence/first-failure.json +cc18a31f5f93f5e1adc22d87e2a74c3a74afcdb174fc9ce73aef94fd37e8393a docs/ai/rwx-main-checks-evidence/parity.json +a117f47b259bd5af31a904cf2b6aa93b99836b8cd22b71212b2c8cca7fc68665 docs/ai/rwx-nut-prefork-evidence/parity.json +6c33c80c4f716937befdaf8fc8d682d816f3d93b8eee1cd91266609e2557baae docs/ai/rwx-nut-prefork-evidence/pushed-reporting-confirmation.json +f3fc234509887057258618e587130c9d028a65b0c35b5a28f2f00fc5a836129a docs/ai/rwx-nut-prefork-evidence/runs-cache-and-fixtures.json +08eb077ca74fc1c6d56c78120851b6461386c1a57e4f8abef031547e206e0690 docs/ai/rwx-nut-prefork-evidence/verified-native-definition.yml +b0c69732aa1885ee7422d6f70812a2d6ba14d69efe2a88149f3c5d8295a3c51f docs/ai/rwx-nut-provenance-evidence/hosted-preflight.json +c76050dbcd20c83aec3ecfe954e60d9f88e16fe416e996e372beda889ebe75e5 docs/ai/rwx-nut-provenance-evidence/parity.json +3ec8c7f531be2c62041ab16194918087d567c7b95433f1d555ed5ab7f8ad818b docs/ai/rwx-nut-provenance-evidence/preflight.json +b7da8f6d2e5aaf4604796200aac5031d5b380d7e3a9542377456b87d63e25487 docs/ai/rwx-pr-checks-evidence/first-failure.json +35716dd39981394640830efbcaee28033b2c0535b9553b83f45bfc046fae26d6 docs/ai/rwx-pr-checks-evidence/parity.json +50cf001ee8455a0827217c18faa94491b0bd9765d78be706a1775ec76bab15fb docs/ai/rwx-pr-gates-evidence/.gitattributes +5ad48986c9ea82a143f9fad77e385f9bb136c2801e2d8acc41b7b347d412597a docs/ai/rwx-pr-gates-evidence/3be5-contracts-parity.json +ea66859c2d7d906484835d19e4456aa3185902a2af720e5ef26e5f78acc8ee01 docs/ai/rwx-pr-gates-evidence/contracts-first-batch.json +be87a57f6658857b359a9e843b6e72bedaba2963f9cd61f59769421e83e82c1e docs/ai/rwx-pr-gates-evidence/contracts-pacing-preflight.json +5ecd8a64e1a8e51e18d77c4cebff0c9a05beb5cf1244beaa995f636688ba50e2 docs/ai/rwx-pr-gates-evidence/contracts-parity.json +21f5270fb913aae33ddf0c0684f489016f4caeac811d7d88a4c5bb57c10d7a6d docs/ai/rwx-pr-gates-evidence/contracts-preflight.json +07551cd185cc537eaa72f2355507bb72d1eb949c5e3b6672d172ae60fec42248 docs/ai/rwx-pr-gates-evidence/contracts-second-batch.json +d456b883edb33d271c8d7df72997861ac66789a6dd1f2de712eca7faf44f121e docs/ai/rwx-pr-gates-evidence/contracts-shell-preflight.json +78b0cbd10b1b0f6962ee96c7b47a413272cb82df4be425dfdd7bf5f5b534703f docs/ai/rwx-pr-gates-evidence/embedded-preflight.json +58116abc983e14151f6f05bf37fecb63287aa5583f12c153a8f81d03b3b2a15e docs/ai/rwx-pr-gates-evidence/f821-coverage-closeout.json +fa88cfcbd409bf2bd2582661a40cdabc8a549873c7c19eef2c1c624b2a5cee58 docs/ai/rwx-pr-gates-evidence/f821-kontrol-helper-first-failure.txt +7632ae132eb59848758ed47284d3e4072d0e721814447f2936eeaa1fa69b1470 docs/ai/rwx-pr-gates-evidence/f821-main-aggregate-first-failure.txt +22dd66095774d7872b046cdc52c0549aab02be7b42b987be51fc31f50ba5eb77 docs/ai/rwx-pr-gates-evidence/f821-rust-e2e-source-first-failure.txt +bd4087f594666eb6d2a1970897ad6e0847ea0b1333c22c369e9d754529471265 docs/ai/rwx-pr-gates-evidence/first-hosted-failure.json +3c6cf6147fc194082ea1d9b919245f7885312c5b991691427eeef2a541118ac0 docs/ai/rwx-pr-gates-evidence/hosted-correction-preflight.json +19d9b7be2bee1a33b0594a3ae18b6791580edca046f25347bcd2ac5e255167d6 docs/ai/rwx-pr-gates-evidence/main-parity.json +16281e99daad6b132ff962b5d443aab59faa7c498a63d0f143e3e710b4a8f001 docs/ai/rwx-pr-gates-evidence/main-preflight.json +9547148b072606e0a6650dfc66f10fe59e83ac692f00402b7dd91753cda65d60 docs/ai/rwx-pr-gates-evidence/pr-closeout.json +2c8fe4662a73f08a2030a56176d7d6a7c20465c53e7d44fe9f49b5d7b7f8825c docs/ai/rwx-pr-gates-evidence/preflight.json +0b618e1fabd0069b504bf019fa42fd094805b8a34d6d4a652e5057de3053a225 docs/ai/rwx-pr-gates-evidence/rust-parity.json +7386457e0c0ba9b2aade13c76723c2812ef6367b2a963a68a35576b062a0efa5 docs/ai/rwx-pr-gates-evidence/status-preflight.json +491d9bd12bd9f646fa13574d3eb8f25672f997e1d401a8d4cacf5e2cae34550d docs/ai/rwx-rust-e2e-evidence/6245-circle-sysgo-failure.json +65fc77ae26cef769571cd2edbd75ccd64c573d758ab91cdc0cf0f05161c51c34 docs/ai/rwx-rust-e2e-evidence/cache-and-warming.json +a6b0fdd44088051aebfc78f3b849472b2d8ce6d07c226b7eacbc67e2fcb38f94 docs/ai/rwx-rust-e2e-evidence/first-failures.json +5ef4710e179d763a8752984ec2fdeaeb7518611b4362e35a4e8e3d92917d77cb docs/ai/rwx-rust-e2e-evidence/negative-probes.json +d2f6122e5869a3eab976298f168d11f4da34510ff19c09fd33b205272f98db5e docs/ai/rwx-rust-e2e-evidence/parity.json +ba17f0c3d95c2b163287750756ccfcf626a8c1e15696e39d4b186a76e33d2d92 docs/ai/rwx-rust-evidence/cannon-cache-and-failure.json +75b84a965c2324fb1c58682ad049707293ed5fa4db862c457b4d83fb36190112 docs/ai/rwx-rust-evidence/cannon-parity.json +bb4b20ff29f34d95608d152b4e87916b5303b5369ae7cc4c2bd6d0e47a927dcf docs/ai/rwx-rust-evidence/extra-cache-reuse.json +387be2aa892005af939426fa1b4d2db0558dbab09e7277968342f0b81c9447c2 docs/ai/rwx-rust-evidence/extra-parity.json +408f62b782d587b0ee1f678f06c5eed47333b39d8a2a92c39e890f8bca08743a docs/ai/rwx-rust-evidence/first-failures.json +33a4fa5e0b993dd98359b6bb6188c35f5525f105a13eda11075f9f7daa24f073 docs/ai/rwx-rust-evidence/parity.json +515c77d24dbbd964a6494318d3c1a70e3b880c587f46649aa34ece4a3cfce4e7 docs/ai/rwx-selector-upload-evidence/3be5-batch-parity.json +10860cf6e8d69f3a49c211f9a08fe66244a343de6cb52345bc0cc5fb440defc0 docs/ai/rwx-selector-upload-evidence/a3a0-batch-parity.json +90b691175a1f29dd2ea86158a9c127f9f31efadbd9cb2d2f78d99bdc6f4a73c6 docs/ai/rwx-selector-upload-evidence/batch-parity.json +e5ff7219a037dc5ac4748dc191d03fe9027d776ba6b73c9420ea55546967dff1 docs/ai/rwx-selector-upload-evidence/cb34-batch-parity.json +a1b5f417d0c9a0381fd27e9af70a4ff3b577cd5370649f86a139704df155124a docs/ai/rwx-selector-upload-evidence/compiler-warm.json +5de31e81a6ed77e3948b0325bc2e33b340983ec37bf054824b273f28f828b084 docs/ai/rwx-selector-upload-evidence/f821-batch-parity.json +b1bbb2d0abd9c368d71524a14b73c38fd96e57a4e5f1b354b8ae83e2df16d43d docs/ai/rwx-selector-upload-evidence/parity.json +cd0de166c7788b3bb93b7ea45ce76bcdeb2d874b5d297c1ad8ecaaa6814d3d4a docs/ai/rwx-selector-upload-evidence/preflight.json +f38e078a127e2d92a5c07b80b3cc7828c77f36a488c2ac34967906251166cab7 docs/ai/rwx-sp1-guest-evidence/canonical-path-probe.json +403e14f55a8b9bb7018319e0d1389f743e74d1bcd97e6567c2b241c25ae50c72 docs/ai/rwx-sp1-guest-evidence/first-full-preflight.json +4ebd67e7c470f766317b7dafb8538463fd06671f9919adf263958b51a6f292fe docs/ai/rwx-sp1-guest-evidence/first-hosted-elf-discrepancy.json +2834247f0ba3f998ddc989ee9916d63ce81c6a1bd3ed8c5fa32339ac94c7bcb8 docs/ai/rwx-sp1-guest-evidence/hosted-parity.json +6299403bf67127e79cd8f2e0a8001105c548955b7aaefc570e423be29b6a1ed1 docs/ai/rwx-sp1-guest-evidence/latest-full-preflight.json +3dd8cfab24f3f5ff0d3dbfd03d42f1940adcd1aeaf374c8ecc9b50674b2d841b docs/ai/rwx-sp1-guest-evidence/local-fixtures.json +6d3b545f7bcddd65947176af2ae9ffc823074b1615d503317579ef8a372cdfaa docs/ai/rwx-static-checks-evidence/first-failures.json +803d75c8148d54f15f4d60d962b85cde53911a0c450772664a4dc6e38f8c4b51 docs/ai/rwx-static-checks-evidence/parity.json diff --git a/docs/ai/rwx-fetcher-artifacts.md b/docs/ai/rwx-fetcher-artifacts.md new file mode 100644 index 00000000000..3cfae4fca02 --- /dev/null +++ b/docs/ai/rwx-fetcher-artifacts.md @@ -0,0 +1,92 @@ +# Fetcher compiler artifact shadow + +The optional `optimism-fetcher-artifacts-shadow` implements Main's +`diff-fetcher-forge-artifacts` workload through shared `run-main` routing. +Full hosted same-SHA original-report comparison passes at +`6245472e81405c62cf92a0a1668f467bc157b0f6`. This completes one additional +Main occurrence; verified coverage is 77/86 (90%). + +## Correct comparison inputs + +The previous Circle command ran `just build-contracts`, which copied newly built +artifacts over the committed comparison inputs before `diff -qr`. The same +behavior exists on protected baseline `c8e4ba85`. A real complete Linux build +exposed stale compiler/source metadata that this copy hid; all four original +ABIs, method identifiers, creation/runtime bytecode and link references still +match exactly. The [preflight index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-fetcher-artifacts-evidence/preflight.json) +retains that baseline, the real isolated revision and complete original hashes. +This local preflight contributes no hosted coverage. + +Both providers now run `just compile-contracts` in `op-fetcher`, preserving the +original clean build, lite profile, warning rejection and test exclusion. +Compilation leaves committed artifacts untouched. The developer +`just build-contracts` command still compiles and updates the embedded artifacts. +The refreshed metadata is generated by the actual full build; executable fields +remain unchanged. + +Both first hosted runs completed compilation but correctly failed the strict diff: +their available compiler sets selected 0.8.28, whereas the original stored artifacts +and full Linux preflight used 0.8.30. The complete partial compiler archive and +27 native original file seals and the Circle original report are retained in the +[first-failure index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-fetcher-artifacts-evidence/first-native-failure.json). +The script now requires exactly 0.8.30, installed with bounded retries before +the build and verified in every original fetched artifact. Native installation +is a reusable tool layer and is included in protected tool-only warming. This +script/tool selection change and regenerated metadata preserve every original +ABI, executable bytecode and link reference. This failed attempt adds no coverage. + +Exports retain every original compiler field and source hash. Only remapping +contexts under the actual contracts checkout become relative; duplicate +remappings are removed, order is sorted, and JSON serialization is canonical. +Typed metadata and original raw solc metadata are preserved independently: +Foundry's typed object omits some original error userdocs and empty ABI fields. +Foreign absolute remappings fail. Every export validates before changing any +existing artifact, and each file replacement is atomic. + +The fresh comparison uses original `diff -qr` against the portable build export. +It requires the complete artifact inventory, retaining future contracts, missing +or extra files and actual differing bytes. No ABI, bytecode, source-map, AST, +storage-layout, source-hash or compiler-setting differences are ignored. + +## Evidence and validation + +Circle pipeline 135584, job 5635755 and native run +[`579ffecc`](https://cloud.rwx.com/optimism/runs/579ffecc8c844f919fec52f679bed77d) +both succeeded. The [complete parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-fetcher-artifacts-evidence/parity.json) +binds every original report file and validates all 729 compiler artifacts, +complete compiler source graphs, and all four embedded outputs. The only +cross-provider differences are 1,428 checkout paths in original metadata. +Every executable field, source hash, compiler setting, cache input and selected +portable artifact agrees. Both providers correctly report zero tests for this +build and artifact job. The native helper ran nine actual fixtures; their +[complete original hashes](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-fetcher-artifacts-evidence/fixture-originals.json) +retain success, drift, missing artifacts, compiler failure and cancellation. + + +Both providers retain original build/diff commands, cwd, logs, exit/signal, +complete source/tool/environment/submodule settings, untouched committed inputs, +raw compiled artifacts and a sealed archive of every compiler artifact/cache +file. Complete partial compiler output also survives a build failure or +cancellation. The comparer verifies every original seal, rederives portable +exports, validates source/settings and compares all compiler outputs. Known +compiler node identifiers, checkout metadata and cache timestamps are accounted +for across providers by the existing contract-artifact comparer; the portable +fetcher artifacts themselves must match committed bytes exactly. + +Nine Linux fixtures pass with actual Forge, Just and diff: complete fresh builds +including future contracts, strict comparison, resealed wrong source/selection/ +coverage/commands, untouched drift, missing artifacts, real compiler failure and +cancellation. They also verify portable remappings, original raw solc fields and +unchanged exports after failed validation. RWX lint and merged/activated Circle +config validation pass. + +Native verdicts run freshly on 8 CPUs / 16 GiB. Tool and source setup are reused; +the original clean compilation is preserved. Protected warming selects only +tool installation and executes zero artifact verdicts or helper tests. Circle's +job name, gate dependency, required ownership and production publishers remain +unchanged. + +The exact-input [tool-only reuse rehearsal](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-fetcher-artifacts-evidence/tool-only-reuse.json) +`a31e04c6` succeeded on the same SHA. Foundry and the pinned fetcher compiler +were complete native cache hits; no verdict or helper ran and test count was +zero. This is a CLI rehearsal; a protected `develop` event remains unobserved. diff --git a/docs/ai/rwx-flaky-report.md b/docs/ai/rwx-flaky-report.md new file mode 100644 index 00000000000..ed359c8bf5c --- /dev/null +++ b/docs/ai/rwx-flaky-report.md @@ -0,0 +1,54 @@ +# Native flaky-test reporting + +The optional `optimism-flaky-report-shadow` executes the original acceptance +reporting script through shared `run-main` routing. Complete same-SHA hosted +parity passed at `b472a22e8374c797ecaee5a6ef5b735fcc979028`: native run +`5620ba604af94bd38b1a710469ebbf4b`, Circle pipeline 135620/job 5637736. +The [parity index](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-flaky-report-evidence/parity.json) binds all 16 original +files from each provider. This adds one verified Main occurrence. + +The workload requests the real Circle Insights flaky-test API for this public +repository. Circle retains its existing authenticated context; RWX uses the +public endpoint anonymously and receives no Circle credential. The API is +project-wide and branch agnostic. The branch label identifies the requesting CI +branch rather than claiming that the API selected observations from that branch. + +Both providers retain the entire unfiltered response, each original HTTP attempt +and failure body, the existing acceptance-only JSON, CSV, HTML and top-ten text +reports, complete stdout/stderr, source hashes and actual process metadata. +Validation independently derives every selected row from the unfiltered source +and rejects missing, duplicated or corrupt inputs. HTTP authorization/path errors +fail immediately; transient failures have six attempts with bounded backoff. +Cancellation retains a failed process verdict and never produces report credit. + +`c-flaky_report_replay` defaults to false. An explicit pilot API replay with +`main_dispatch=false` selects only the original reporter. It can also run beside +the isolated selector replay. Fixtures verify normal push/main dispatch behavior, +the pilot branch guard and the complete selected workflow set. Protected +`develop` cache warming builds tools and executes zero reports or tests. + +The anonymous live preflight returned all 79 source observations and all 12 +acceptance observations, with every derived report checked. The retained API +fixture comes from that real response; fixture executions add zero hosted +coverage. Wrapper tests execute actual shell and child processes to verify +successful generation, original HTTP failure capture and cancellation. + +Run the strict hosted comparison after collecting both complete report trees, +Circle's full job/config/log originals, the native run metadata and the same-SHA +GitHub check snapshot: + +```sh +mise exec yq@4.44.5 -- python3 ops/ci/compare-flaky-report.py \ + --circle .ci/rwx-flaky-evidence/SHA/circle \ + --native .ci/rwx-flaky-evidence/SHA/native/report \ + --run .ci/rwx-flaky-evidence/SHA/native-run.json \ + --github .ci/rwx-flaky-evidence/SHA/github.json \ + --output .ci/rwx-flaky-evidence/SHA/parity.json +``` + +Comparison preserves every original API row, date, ordering and report cell. +Only provider workspace prefixes in output paths are normalized. Both complete +retry histories remain in the parity index. Both actual requests succeeded on +their first attempt with identical complete response bytes: 79 source +observations and 12 acceptance rows. If future live API snapshots differ, +investigate the original responses before adding coverage. diff --git a/docs/ai/rwx-go-parity.md b/docs/ai/rwx-go-parity.md new file mode 100644 index 00000000000..62de96c3002 --- /dev/null +++ b/docs/ai/rwx-go-parity.md @@ -0,0 +1,147 @@ +# Aggregate Go RWX shadow + +The full Circle `go-tests` workload is implemented in `.rwx/go-tests.yml`, with +optional status `optimism-go-tests-shadow`. Circle still owns every required +gate. Acceptance, Cannon's dedicated test suite, Rust suites, and +`op-deployer/pkg/deployer/forge` remain outside this workload, exactly as excluded +by `just list-test-packages`. Packages without tests remain in the selection. + +## Execution contract + +`go-suite.py` runs the shared Just selector, validates every selected package +with `go list -e -tags=ci -json`, and produces an exhaustive, duplicate-free +manifest. Duration estimates affect assignment only. Historical Circle job +5625852 seeds the balancer; cached historical observations are estimates and +cannot establish fresh parity. + +Each of the initial 12 shards compiles on 16 CPU / 32 GiB, starting after the +Go support and `ci` contract producers finish. Fresh verdicts start independently +on 8 CPU / 16 GiB once their binaries and all runtime artifacts are ready. +Compilation never invokes TestMain. The runner executes verified binaries in +the original package working directories with `-test.count=1`, `-parallel=8`, +a 40-minute package timeout, and at most four concurrent packages. Gotestsum +retains the original events and permits three retries with a 50-failure ceiling. +Native reporting consumes a compact projection that preserves every verdict and +retry event, and bounded failure/skip output. Its metadata hashes the unchanged +complete original JSON, which remains available with JUnit and per-test logs. +The runner restores default interrupt/quit signal dispositions before executing +each binary: `test2json` command mode otherwise leaks ignored signals into +subprocess fixtures. It also retains Go's one-minute backup cleanup grace. +The CLI retains `--suite go-rollup` compatibility. + +[Captain's Go support](https://www.rwx.com/docs/captain/test-frameworks/go/go-test) +does not currently provide partitioning, so assignment uses the repository's +package balancer while RWX consumes native Go JSON reports. + +## Dependency and cache boundaries + +Local packages produce Go modules, the verified superchain ZIP, Cannon binaries +and embeds, hello ELF (Go 1.24.13), contracts with the Circle `ci` profile and +script preparation, the embedded deployer artifact archive, Kona host/client/node and op-zk-proposer release +binaries, plus op-reth, and all configured reproducible prestates. +The existing op-reth shadow calls the same release package. + +Native Go objects, Foundry state, Cargo targets and sccache use isolated tool +cache keys. The prestate task uses `docker: preserve-data`, retaining Docker and +BuildKit data through [RWX's supported cache](https://www.rwx.com/docs/docker). +Verdicts receive dependency archives through filtered producer filesystem inputs. +The runtime package excludes Go compiler caches, Cargo targets and Rust caches +before transfer. It restores the archives after checking revision, tool pins, +settings, archive hash and individual file hashes. Compiler metadata additionally +binds suite, shard assignment, effective settings, binary hashes, Go version and +absolute source path. Test results and logs never enter reusable outputs. +Runtime Go builds have a separate compiler cache. + +Compilation and verdicts use separate [local packages](rwx-local-packages.md). +The definition retains twelve explicit calls for each phase. Compilation starts +without waiting for Rust or prestates; each verdict depends on only its own +compilation. The experimental 24-shard tasks and 24-shard CLI configuration are removed. + +Only runtime preflight and verdict tasks receive the two existing archive RPC +inputs from the locked `optimism-go-tests-rpc-shadow` vault. Repository access is +restricted to `codex/rwx-ci-pilot` and `develop`. Protected develop warming targets +producers and compilation only, executing zero tests. + +## Fresh Circle comparison + +Dispatch Circle with public parameter `c-go_fresh_tests: true`. Its default is +false. Setup forwards it as `c-go_fresh_tests_effective` to avoid Circle's conflict +when a nondefault setup parameter is also passed to continuation under the same +name. The shared Go runner adds `-count=1` while retaining Circle's existing +`nproc` concurrency. An exploratory fresh run exposed `nproc=32` inside the +8-CPU Circle container. RWX concurrency is independently tunable through +`test-parallel` (initially 8), with 16 and 32 as candidates on the same +8 CPU / 16 GiB verdict workers. Preserve the complete workload, fresh execution, +timeout and retry limits; record execution-setting differences explicitly. +Circle retains tagged discovery, +complete selection, effective settings, per-node assignments, original Go JSON, +JUnit and per-test logs. Benchmark both providers at the same immutable SHA. + +## Stage closeout (October 2, 2026) + +The selected configuration is **12 duration-balanced shards, `-parallel=8`, +`-p=4`, 16 CPU / 32 GiB compilation and 8 CPU / 16 GiB verdict workers**. +The full native rollup trigger is retired; its CLI mode and regression coverage +remain. Circle continues to own required gates. + +Same-revision fresh parity is verified at +`cf7f3f2d51ea5e75b9eb21adcb4cf7a860a15bce` between +[RWX](https://cloud.rwx.com/optimism/runs/fee5ce5d0613477397c69755dded1950) +and [Circle job 5629727](https://circleci.com/gh/ethereum-optimism/optimism/5629727). +Both selected all 459 packages exactly once, including packages without tests, +and reported 11,613 identities: 11,490 passes, 123 skips and zero retries. +No identities are missing or extra. The strict comparison reports seven differing +skip messages in flaky-handling self-tests. Each has identical annotations and +source-relative traces; provider log routing and absolute workspace paths account +for the differences. All seven are investigated, with zero unresolved differences. +The retained [comparison evidence](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-go-evidence/parity.json) records each resolution; +it does not change the original reports or claim strict textual equivalence. + +| Observation at that SHA | Configuration | Wall time | Measurement boundary | +| --- | --- | ---: | --- | +| RWX native default | 12 shards, parallel 8 | 1,075s | RWX run start through completion | +| RWX concurrency trial | 12 shards, parallel 32 | 1,054s | RWX run start through completion | +| Circle fresh validation | 12 nodes, effective parallel 32 | 1,222.7s | Pipeline creation through final Go job | + +The [default run](https://cloud.rwx.com/optimism/runs/3fbfa393c684449eb73f3d6372943f5f) +passed all 11,613 cases. These are individual observations with different provider +creation boundaries and variable producer cache state, not repeated warm medians +or a proven speed win. The 21-second concurrency difference does not establish +a repeatable improvement at no greater whole-run cost. At the user's request, +performance experimentation ends here: repeated three-run benchmarks and 24-shard +selection are deferred. The CLI retains both shard configurations for later use. + +Validated evidence includes: + +- Full hosted runtime Go/Forge fixtures and RPC preflight, alongside helper tests + for unavailable RPC inputs, stale settings/SHA, corrupt binaries, discovery, + assignments, runtime paths, report collection, signals and cancellation. +- [Isolated intentional failure](https://cloud.rwx.com/optimism/runs/c8144a8d36614520a3274f78d50a78b0): + initial execution plus three fresh retries, original failures and a failed CLI + run. A native GitHub webhook failure rehearsal remains an operational follow-up. +- [Empty-target Kona sccache](https://cloud.rwx.com/optimism/runs/ea88798123de459686653de6b95e96d4): + 1,568 Rust hits and eight misses; complete statistics retain other compiler + feature probes and cache errors rather than presenting them as all hits. +- Isolated dirty-snapshot invalidation probes for + [Go/prestate](https://cloud.rwx.com/optimism/runs/5e980f4644f84328b2cc4b89e9ff070e), + [Rust/prestate](https://cloud.rwx.com/optimism/runs/b9569fc31bcb48958df03508c5240e59), + [contracts](https://cloud.rwx.com/optimism/runs/47b445bedeb44b3891cae01ed00e6910), and + [toolchain](https://cloud.rwx.com/optimism/runs/91ce4d1927294bea9e4dbd912c589f47). + These ran zero tests and are cache diagnostics, not clean-SHA benchmarks. +- Unchanged Go/contract artifact reuse, native compiler caches, and zero-test + warming rehearsals. An actual protected `develop` cache-rebuild event requires + merge and remains unobserved. +- Both configured prestate hashes matched Circle job 5629322: + `kona-client=0x03e384aad91052e86a9912ad763cd32027586d99b5e8e2024115c606f35b6afa`, + `kona-client-int=0x03d56f7fd7d39b381efc142e127f41109709812d788c436ebad8f6de0633bc39`. + +Original JSON, JUnit, per-test logs, effective settings and manifests remain in +provider artifacts and retained comparison archives. The checked-in comparison +is a compact evidence index; provider retention is finite. Preserve downloaded +archives before provider retention expires when auditing or resuming benchmarks. + +Follow-up work is acceptance-suite parity, operational webhook/gate rehearsals, +and optional performance work. Current bottlenecks are the longest Go packages +and Docker prestate cache transfer (about 5.6 GB and 138–143 seconds despite an +approximately two-second warm build). Full producer output reuse across CLI/native +contexts also needs investigation before making stronger cache or cost claims. diff --git a/docs/ai/rwx-kontrol-build.md b/docs/ai/rwx-kontrol-build.md new file mode 100644 index 00000000000..dc07d60f9aa --- /dev/null +++ b/docs/ai/rwx-kontrol-build.md @@ -0,0 +1,126 @@ +# Kontrol summary and proof build shadow + +The optional `optimism-kontrol-build-shadow` ports Main's `check-kontrol-build` +through shared `run-main` routing. The complete native job and same-SHA Circle job +pass at `c6b294069888978775a3693a9a6ee35de37942f0`. The +[complete hosted comparison](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/hosted-parity.json) +verifies both summaries, all nine proof sources, four generated files, runtime +state and every compiler artifact/graph/cache input across all four phases. +This occurrence is now counted; overall verified coverage is 79/86 (92%). + +Both providers run the original `just kontrol-summary-full`, generating default +and fault proof deployment summaries, followed by +`just forge-build ./test/kontrol/proofs`. The summaries run with the original +default Foundry profile. Native CI contract preparation now uses the separate +`contracts-kontrol` producer, retaining all original CI contract artifacts plus +the legacy 0.8.28 script graph found in Circle's workspace. Its compiler set, +source inputs and complete original preparation commands are sealed. Changed +source or compiler inputs discard previous compiler outputs; unchanged inputs +may reuse them. The incoming complete artifacts are compared with Circle's +original attached CI workspace. This PR job builds proof files +and executes no proof tests, so it reports zero tests. + +The mise-selected Kontrol 1.0.255 image is fixed to its verified Linux amd64 +digest. Preparation pulls that immutable image with bounded retries, verifies +its executable version, and sets the original local tag used by the scripts. +The image manifest also pins its configuration digest. Classic Docker reports +that configuration digest as `Id`; the containerd image store reports the +manifest digest, as shown in [Docker's implementation](https://github.com/moby/moby/blob/master/daemon/containerd/image_inspect.go). +Both representations must match the pinned manifest/configuration digests; +repository digest, platform, complete configuration and layer identities remain +verified. The [original immutable manifest](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/immutable-image-manifest.json) +is retained byte for byte. +Original Docker invocations and cleanup are preserved. A separate native +producer retains the image using +[RWX Docker caching](https://www.rwx.com/docs/docker#preserving-docker-data), +while the verdict uses disposable Docker data and runs freshly on 8 CPUs / +16 GiB. Protected warming selects only image and CI contract producers; no +summary generation, proof build verdict or helper runs. + +Opt-in capture hooks retain each variant's raw deployment state diff, cleaned +load-state input, original and reversed contract maps, and generated Solidity. +The shared adapter seals original commands, working directories, logs, exits, +effective Forge configuration, tool/image versions, complete tracked inputs, +submodules, initial and intermediate compiler archives and final runtime files. +It permits only the four expected generated Solidity files to change. Partial +compiler, image and runtime outputs survive original failures and cancellation. +Generated summaries and verdict reports are excluded from reusable outputs. + +The comparer derives all selected proof source files from the bound original +input inventory, retaining future files. It checks every original seal and +image binding, validates the complete native contract producer, requires both +summary variants and every capture phase, and compares all generated bytes, +state diffs, name maps and compiler outputs. Existing compiler identifier, +checkout-path and cache-timestamp accounting retains every structural, content +and settings check. Selected proof files must appear in the final compiler +source graphs. Missing, extra, corrupt, stale or mismatched inputs fail. + +The [complete initial-compiler probe](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/complete-compiler-input-probe.json) +compares all 1,093 contract artifacts and every compiler graph/cache input. Fourteen +filenames differ, with matching source, contract, compiler and profile identities +in the actual Foundry caches. The comparer validates a complete one-to-one binding +for every artifact and compares every payload before accounting for those aliases. +It retains the explicit path pairs; missing, duplicate, unbound, corrupt or +mismatched artifacts still fail. Summary generation invalidates cache references +before the final compile while retaining previous compiler outputs. An output +without a current reference must retain its exact bytes and validated identity +from the immediately preceding original phase. These retained payloads remain +included in the complete comparison; changed, extra or missing payloads fail. +The subsequent full four-phase hosted comparison passes with 1,093 artifacts in +the first two phases and 1,095 in the final two. Foundry retains four qualified +earlier summary artifacts beside newly compiled outputs. Each retained payload +keeps its exact bytes and original bound phase, distinguishing historical and +current outputs with the same source/compiler/profile identity. Every payload +and source graph is compared; invented history, changes, missing files and extra +duplicates fail. All six real-tool regression fixtures pass in 238.715 seconds, +including this hosted filename pattern, source invalidation and original failure +and cancellation evidence. The zero-test input probe adds no coverage. + +The [complete-input real-tool fixtures](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/complete-input-fixtures.json) +pass all six scenarios, including exact complete comparison across all four +compiler phases. Actual preparation is cold on first execution, reuses unchanged +inputs on the second execution and discards outputs after a tracked source +change. Resealed changed, extra and missing retained artifacts are rejected. +The original failure and cancellation reports remain retained. These verifier +fixtures add no hosted workload coverage. + +The first full Linux preflight completed both actual summaries and the proof +build. Real-tool fixtures cover fresh complete execution, future proof discovery, +strict comparisons, actual Kontrol and compiler failures, cancellation and +stale/corrupt contract or image inputs. All six actual fixtures pass. Their +[complete original hashes](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/local-fixtures.json) and +[first full preflight](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/first-full-preflight.json) +remain retained. The image identity correction also passes all six actual +scenarios in 178.216 seconds, including both pinned ID representations and +rejection of an unknown ID in a fully resealed report; its +[complete fixture originals](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/image-identity-fixtures.json) +remain retained. These use unpublished isolated revisions and add no hosted +coverage. ShellCheck, RWX lint, all 35 Linux routing/Circle adapter scenarios, +and merged/activated Circle configs pass. Circle's job name, dependency graph, +four required gates and production publishers remain unchanged. + +The first automatic run at `dc51e446` and Circle job 5635847 failed before any +summary generation because the initial image verifier assumed a single Docker +`Id` representation. The immutable registry manifest confirms the separate +configuration digest; this is an adapter failure. Its +[complete original seals and diagnostics](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/first-native-image-failure.json) +remain retained. The correction is hosted successfully at `ba787b45902fe828580de16b14cbde87a3cd3c60`: +[automatic native run](https://cloud.rwx.com/optimism/runs/593b05bfe691454294c9adee396344d8) +and [Circle job 5635938](https://circleci.com/gh/ethereum-optimism/optimism/5635938) +pass both complete summaries and the proof compilation. All six native fixtures +also pass. Complete generated files, runtime maps, state diffs, source/settings, +image layers and configuration agree. Strict comparison still rejects complete +compiler inventories: the incoming native cache used 0.8.28 where Circle also +retained 0.8.30 artifacts and other compilation history. An isolated fresh native +producer with 0.8.30 reduces the difference to 14 Circle-only 0.8.28 artifacts; +a real script preparation probe confirms it does not add those files. Their +complete compiler-cache graph bindings and originals remain retained for further +investigation. The [complete discrepancy and probe originals](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-kontrol-build-evidence/compiler-inventory-discrepancy.json) +retain all four compiler phases and all 394 native fixture files. +Those original discrepancies remain retained. The complete producer and compiler +history corrections are now verified in +[automatic native run 36fd9282](https://cloud.rwx.com/optimism/runs/36fd9282c63949b8bf7b3efc655b914c) +and [Circle job 5636116](https://circleci.com/gh/ethereum-optimism/optimism/5636116) +at `c6b29406`. No compiler artifact is discarded to make parity pass. Historical +green checks apply to their exact revisions; every new final head requires its +own terminal check verification. diff --git a/docs/ai/rwx-local-packages.md b/docs/ai/rwx-local-packages.md new file mode 100644 index 00000000000..d418ce60532 --- /dev/null +++ b/docs/ai/rwx-local-packages.md @@ -0,0 +1,99 @@ +# RWX local package contracts + +The pilot retains one draft PR and the chosen full Go configuration: twelve +shards, parallelism eight, 16 CPU / 32 GiB compilation and 8 CPU / 16 GiB verdicts. +Acceptance keeps eight shards per variant and its existing per-variant resources. +Circle owns the required gates. Workload selection and parity coverage are unchanged. + +The [report helper contracts](rwx-ci-helpers.md) describe shared subprocess and +artifact mechanics. Runtime runners no longer load offline comparison scripts. + +## Orchestration and dependencies + +The five Go, acceptance, standard/modified contract, coverage and upgrade +definitions contain explicit calls to family-specific compile and verdict +packages. Each repeated body has one implementation under `.rwx/packages`. +The experimental 24-shard Go tasks and 24-shard CLI configuration are removed; the +narrower rollup CLI mode remains available. + +Go compilation depends on source, Go tools and discovery after the Go/contract +producers. It does not depend on Rust or prestates. Each verdict uses only its +own compiler leaf and the required runtime producers. Acceptance discovery +and verdicts remain separate. Contract variants retain their own Foundry +outputs, prepared signatures, profiles, feature selection and runtime behavior. + +Local package tasks explicitly use `package.use`. Callers use producer leaves +such as `compile-0.build`, so a consumer does not acquire a barrier across all +shards. See [RWX local packages](https://www.rwx.com/docs/local-packages). + +## Input and cache boundaries + +Package parameters carry scalar settings and relative paths. Native execution +rejected artifact mount expressions in `with`, despite lint accepting them. +Archive bytes instead arrive through filtered `package.use` filesystem inputs. +Go and acceptance verdicts exclude `.ci/go-cache`, `.ci/rust-cache` and +`rust/target` from their incoming snapshots. Their existing artifact restorer +then checks revision, toolchain pins, settings, archive hashes and file hashes +before restoring runtime files. Compiler metadata and binary validation retain +their existing owners. + +Common tool preparation imports only the pinned configuration, install script +and named CI helper files. Bootstrap output filtering excludes the checkout and +Git history. Go, Foundry and Rust setup remain separate layers. Existing native +Go, Foundry, Cargo target, sccache and Docker/BuildKit cache keys and output paths +remain isolated. Moving a task into a package can invalidate its native task +cache; this refactor does not claim that every prior task cache entry is reused. + +Contract consumers already require their producer's Foundry filesystem. The +producer now includes its sealed preparation metadata in that snapshot. Output +filesystem filters use static family roots: a native probe showed that parameter +expressions in these filters silently omitted the metadata. Each isolated +producer contains only its own preparation evidence. +Coverage/upgrades also import their sealed preflight inputs. RPC values remain +only in availability checks and executing runtime verdicts, outside package +arguments, bootstrap setup and compilation. + +A colder Docker rebuild exposed an existing Cannon handoff assumption. +`build-cannon-client` rebuilds the environment, so its sealed image ID can differ +from the initial environment producer. The offline consumer verifies the latest +build producer's image and ELFs. It still rejects mismatched image IDs, bindings +and original checksums before guest execution. + +Verdicts remain fresh. Reusable outputs contain runtime compiler state, not test +results. Original reports and native test reporting remain task artifacts. +Protected `develop` warming retains its compiler-only targets and executes zero +tests. New generated evidence stays outside Git. + +## Gate and execution verification + +The existing caller task names, receipts, gate manifest and optional check names +remain stable. `pr-gate.py` resolves each package to exactly one unconditional +executable verdict and checks freshness there. A call to a compiler, a cached +verdict, a no-op, a conditional leaf or a multi-task verdict package cannot pass. +Receipts continue to use actual engine-bound caller states, including failures, +cancellations, retries and safe skips. + +The CLI-only `.rwx/local-package-fixture.yml` proves filtered input transfer, +compiler-cache exclusion, nested values/artifacts and caller terminal states. +Its normal mode succeeds, `intentional-failure=true` fails with retained exit +code 17, and `cache-warm=true` skips every verdict. It publishes no PR status. +The coordinator runs the gate fixtures and package graph regression tests. + +Native verification retained outside Git: + +- [Successful filtered transfer](https://cloud.rwx.com/optimism/runs/2f3e90605eda4e7d94384c687840402d): the verdict executes, compiler state is absent, and the nested report reaches its consumer. +- [Intentional failure](https://cloud.rwx.com/optimism/runs/72fa847783914af9956902dbe803a0fa): the executable leaf and caller both fail; the observer runs and the retained report records exit code 17. +- [Static output filtering](https://cloud.rwx.com/optimism/runs/283698c71923491c9a46cd25342cd5eb): both parent and child consumers receive the prepared metadata after replacing the unsupported parameterized output filter. +- [Warm-only run](https://cloud.rwx.com/optimism/runs/63c5909d32be4c198bf509c44c34248f): compilation is reused, verdicts/report consumers are skipped, and the observer confirms the skip. + +The five main definitions shrink from 5,380 to 2,122 lines. Including all 638 +lines of new workload, shared toolchain and probe packages, that is a net +reduction of 2,620 lines (49%). Local validation passes 161 tests with four live +tool tests skipped, plus ShellCheck using RWX's `bash -e -o pipefail` context and +RWX lint across all 49 definitions/packages. Final-head hosted checks provide +the live workload verification and are recorded in the PR. + +Validate the completed refactor as one batch: relevant helper tests, ShellCheck +in RWX's shell context, RWX lint, then one combined hosted PR run. Additional +runs follow actual failures or material fixes. This stage does not add Circle +benchmark replays or reopen shard tuning. diff --git a/docs/ai/rwx-main-checks.md b/docs/ai/rwx-main-checks.md new file mode 100644 index 00000000000..17fd8ccc2b8 --- /dev/null +++ b/docs/ai/rwx-main-checks.md @@ -0,0 +1,83 @@ +# Main validator shadows + +Seven additional Main occurrences now pass complete original-report parity at +`c2d2b810ad429a0c8f62e6f8d76b077b50b8a631`, using the existing optional +`optimism-pr-checks-shadow` and shared `run-main` route. Total implementation +coverage is 63/86 (73%); 23 occurrences remain. + +[Complete parity evidence](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-main-checks-evidence/parity.json) retains every +selection, effective setting, source input and original file hash. Circle pipeline +[135565](https://app.circleci.com/pipelines/github/ethereum-optimism/optimism/135565) +and native run [a883f061](https://cloud.rwx.com/optimism/runs/a883f061b373443196e5e91902d97ccf) +passed all seven original validators, with one initial native attempt each. + +| Occurrence | Original command | Native resources | +| --- | --- | --- | +| todo-issues-check | `./ops/scripts/todo-checker.sh --verbose --strict` | 2 CPU / 8 GiB | +| l2-chains-sync-check | `bash .circleci/scripts/check-l2-chains-sync.sh` | 2 CPU / 8 GiB | +| op-deployer-forge-version | `just check-forge-version`, in `op-deployer` | 2 CPU / 8 GiB | +| check-op-geth-version | `just check-op-geth-version` | 4 CPU / 8 GiB | +| check-nut-locks | `go run ./ops/scripts/check-nut-locks` | 4 CPU / 8 GiB | +| check-generated-mocks-op-node | `just generate-mocks-op-node && git diff --exit-code` | 8 CPU / 16 GiB | +| check-generated-mocks-op-service | `just generate-mocks-op-service && git diff --exit-code` | 8 CPU / 16 GiB | + +The shared runner retains every original command, exit/signal, log, complete +selection, effective Go/tool settings and tracked source/link/submodule-pointer +hashes before and after execution. JUnit reports each validator as one command verdict; it does not invent +individual tests. The original PR TODO setting remains `check_closed: false`; +scheduled closed-issue checks keep their original command. No GitHub issue +credential is needed by the PR validator. + +Mock discovery uses Go's `generate` build tag, includes ordinary and test files, +retains ignored files and every directive, and rejects package/dependency errors. +Original Go source bytes and the dry generator plan remain available for +independent selection verification. Fresh execution regenerates the mocks and +runs the original complete Git diff assertion. NUT discovery retains the lock, +every configured bundle/state file and the fetched protected `develop` revision +used by the original ancestry validator. Version and L2 matrix checks retain +their complete module/configuration inputs. + +The initial hosted observation at `7c547ece` passes complete original-report +parity for the five non-generator validators. Both mock discoveries fail at the +missing gitignored superchain ZIP, before their original commands. The preceding +Circle mock jobs passed at `58a81fbd`; this is a new discovery dependency gap. +[First-failure evidence](https://github.com/ethereum-optimism/optimism/blob/a1aa49aaf3713a8172f3f615f094488fd8e39c3d/docs/ai/rwx-main-checks-evidence/first-failure.json) retains all +fourteen original reports and both diagnostics. Those first observations added +zero occurrences. The corrected stage closes all seven comparisons and retains +these failures without replacing their evidence. + +A minimal native producer now builds the bundle from the exact registry gitlink +and verifies its committed checksum. Both adapters verify the bundle before +discovery and retain complete original bytes. Runtime also validates the native +producer's source, settings and file hashes before consuming it. Fixtures use +the production sync script and Just submodule recipe with a local fixture +registry, reproduce cold missing-embed discovery, and reject a corrupt reused +ZIP before generation. The producer emits no JUnit and records zero tests. + +Every Main verdict has `cache: false` and records run/attempt identity. Go +compilation caches are isolated by validator; shared modules are downloaded and +verified by the existing producer. Compiler state is reusable, while reports +and verdicts are excluded from filesystem outputs. Protected warming prepares +tools/modules/bundle and executes zero Main verdicts. CLI rehearsal +[4e241bd3](https://cloud.rwx.com/optimism/runs/4e241bd36cd546fa9b5e4e2abf53d99f) +passed preparation of the exact benchmark SHA, with zero test executions and +zero Main verdict tasks. Its bundle producer binds the registry revision, ZIP +checksum, complete inputs and tools. This does not establish a protected +`develop` cache-rebuild event. + +Fixtures execute real Go/Mockery generation twice, including a test-file +directive, then commit a changed interface and verify the stale mock fails the +original Git diff. Real shell/Just fixtures verify strict invalid TODO detection, +complete L2 matrices and Forge pin mismatch with the original nested working +directory. Cancellation retains the process signal and partial output. Piped +parent stdin can make ripgrep read an empty pipe instead of the checkout; all +adapter subprocesses explicitly use closed stdin. Comparison rejects matching +wrong commands, omitted directives/bundles/chains, corrupt source/originals, +stale revision/tools, reused verdicts, unexplained retries and target drift. + +Circle remains the required provider. Production publishers, rulesets and the +single PR's draft state are unchanged. + +All four required Circle gates, dependency review and all nine optional RWX +checks pass at the benchmark revision: 146 successes, one neutral, zero +unfinished or failed checks. Later observations stay bound to their own SHA. diff --git a/docs/ai/rwx-migration.md b/docs/ai/rwx-migration.md new file mode 100644 index 00000000000..c8e635c3681 --- /dev/null +++ b/docs/ai/rwx-migration.md @@ -0,0 +1,660 @@ +# CircleCI to RWX migration + +CircleCI remains the merge-gating CI provider. The RWX pilot is for comparing +execution, caching, and feedback before moving required checks. It does not change +GitHub rulesets, fork authorization, schedules, or publishing credentials. + +Track the full PR workload and remaining validation in +[rwx-parity-todos.md](rwx-parity-todos.md). Implementation and evidence updates +stay in the single draft +[PR #23151](https://github.com/ethereum-optimism/optimism/pull/23151) +against `develop`. + +Generated pilot evidence is retained outside Git. The +[archive index](rwx-evidence-index.md) records its two private copies, +checksums, restoration procedure and historical collection limitations. + +## Run the pilot + +`.rwx/pilot.yml` runs shared routing tests plus policy-selected Go lint (with the +superchain bundle) and Rust formatting/upstream-mirror checks. Verdict tasks use +`cache: false`; tool setup can be reused. This is partial coverage, with the +optional push status `RWX: optimism-pilot`. No fork PR trigger or vault/token is +configured. Authorized `external-fork/*` pushes follow the existing Bailiff path. +Terminal verdict tasks disable filesystem output to avoid uploading unused state; +logs remain available. Export any future test reports as explicit artifacts. + +Install repo tools using [dev-workflow.md](dev-workflow.md). RWX is initially a +standalone pinned CLI, outside the mise toolset. On Linux x86_64, install v3.32.1 +into `~/.local/bin` following the [official CLI installation](https://www.rwx.com/docs/cli#pinning-a-version-for-scripts): + +```bash +set -euo pipefail +RWX_CLI_DOWNLOAD=$(mktemp) +curl -fsSL --retry 5 --retry-delay 2 -o "$RWX_CLI_DOWNLOAD" https://github.com/rwx-cloud/rwx/releases/download/v3.32.1/rwx-linux-x86_64 +printf '%s %s\n' b67326b892b301f6c0abaaa69287fb9f53869c766216cb568e861131311991ef "$RWX_CLI_DOWNLOAD" | sha256sum -c - && { + mkdir -p "$HOME/.local/bin" + install -m 0755 "$RWX_CLI_DOWNLOAD" "$HOME/.local/bin/rwx" +} +rm -f "$RWX_CLI_DOWNLOAD" +export PATH="$HOME/.local/bin:$PATH" +``` + +The [linter](https://www.rwx.com/docs/cli-reference/rwx-lint) needs an installed +Node.js on `PATH` (22 or newer recommended). From the repository root: + +```bash +mise exec -- bash ops/ci/test-decision-tree.sh +mise exec -- bash .circleci/scripts/test-decision-tree.sh +mise exec -- python -m unittest ops/ci/test_rwx_metadata.py +rwx --version +mise exec -- rwx lint .rwx/pilot.yml --warnings-as-errors +rwx login +rwx whoami +mise exec -- rwx run .rwx/pilot.yml --wait +``` + +Lint does not need an RWX login. A remote run requires an RWX organization and +authenticated CLI. [CLI runs](https://www.rwx.com/docs/cli-reference/rwx-run) +include local changes through Git patching; use a clean, pushed commit when +comparing providers. For automatic pushes, an organization administrator must +connect the repository using the [RWX GitHub App](https://www.rwx.com/docs/getting-started/github). +Account/app setup and remote execution must be verified separately from local lint. + +## Go rollup shadow + +`.rwx/go-rollup.yml` adds the optional GitHub push status +`RWX: optimism-go-rollup-shadow` and accepts authenticated CLI execution, which +does not post a VCS status. Both paths use the existing `run-main` routing value. +It runs every package under `./op-node/rollup/...` with `-tags=ci`, without `-short` or a +test-name filter. This is a complete component workload, not a replacement for +the aggregate Go gate or its dependent acceptance, Cannon, contract and Rust jobs. + +`ops/ci/go-rollup-tests.sh prepare` builds one authoritative package manifest. +Four native RWX shards use longest-package-first placement from the recorded +package durations in `ops/ci/go-rollup-timings.json`. New packages receive a +median estimate; timing data only affects placement. Discovery and every shard +validate complete, duplicate-free assignment, including packages without tests. +The timing seed names the original RWX run and commit; refresh it from retained +Go JSON when package durations change. + +A content-cached producer compiles each package with `go test -c -p=4 -tags=ci`. +Its native [tool cache](https://www.rwx.com/docs/tool-caches) retains downloaded +modules and Go compiler objects on source changes. Fresh verdict tasks consume +compiled binaries and source/fixtures as [artifact dependencies](https://www.rwx.com/docs/artifacts), +without inheriting the compiler object cache or Git history. They preserve the +package working directory, `-count=1`, at most four concurrent packages, +`-parallel=nproc`, `-timeout=40m`, and Go's panic-on-exit-zero behavior. +The existing gotestsum wrapper retains three failure retries, original Go JSON, +JUnit and per-test logs. Its raw-command reruns append `-test.run` plus a package; +only those diagnostic retry calls may select individual tests. + +The import guards execute `go/packages` at runtime, so verdicts retain the pinned +Go toolchain and an artifact with the root package's production module sources +and downloaded module graph metadata. They do not bypass these tests or replace +them with a precomputed result. Runtime source, reporter and binaries are bound +to the compiler's authoritative package manifest and tested commit. `cache: false` +keeps verdicts fresh; terminal tasks only publish reports and test results. + +The shared bootstrap installs Just, JQ, YQ and Python; Go, Go lint, Rust and +Foundry add separate pinned tool layers. Small bootstrap artifacts avoid passing +the checkout's Git history into tool preparation. Full source/Git state remain +compiler and routing inputs. This scope needs no RPC credentials, publishing +credentials, Docker, contract artifacts or Rust binaries. Mutable tool caches +use the existing cache-only vault, writable by `develop` and the temporary pilot +branch, with read-only access for other branches. + +From a trusted checkout, validate and run it with: + +```bash +mise exec -- python ops/ci/test_go_package_shards.py +mise exec -- rwx lint .rwx/go-rollup.yml --warnings-as-errors +mise exec -- rwx run .rwx/go-rollup.yml --wait +``` + +## Standard contracts shadow + +`.rwx/contracts.yml` adds the optional GitHub push status +`RWX: optimism-contracts-shadow` and accepts authenticated CLI execution without +posting a VCS status. Its standard and changed-file feature matrices now use the +shared contract-suite adapter described in [rwx-contract-suites.md](rwx-contract-suites.md). +All four variants use the shared `c-run_contracts_feature_tests` route. Circle's +complete test-file discovery and actual timing split are retained; native selects +each file exactly once. Standard PR/develop profiles remain `liteci`/`ci`, with +128 fuzz runs and 64 invariant runs at depth 32. Changed-file tests retain +`ciheavy`: 20,000 fuzz runs, 128 invariant runs, depth 512 and 300-second fuzz +and invariant timeouts. + +Compilation and fresh verdicts use full Git/source/submodule state, the Mise +Go/Forge/Cast pins, and the same four solc versions as Circle. Each suite/feature +has its own native Foundry/compiler cache. Artifacts bind complete tracked inputs, +source SHA, branch/profile, effective configuration, compiler signatures, bytecode +and file hashes. Runtime validates every input before consuming compilation, +keeps Go available for the original convention checker and fixtures, and uses a +separate Go compiler cache. Initial JUnit, diagnostic reruns, source selection, +commands, settings, logs and generated fixtures remain explicit evidence. + +This definition uses the cache-only vault with no RPC or publishing credentials. +Its protected compiler warming executes zero verdicts. [Coverage](rwx-contract-coverage.md), +[L2 fork](rwx-contract-l2-fork.md), L1 upgrades and contract-fast checks have +their own completed definitions and evidence. The [Contracts aggregate](rwx-pr-gates.md) +now verifies all 21 exact prerequisites. Circle continues to own the required gate. + +From a trusted checkout: + +```bash +RWX_LIVE_CONTRACT_SUITE_FIXTURE=1 mise exec -- python ops/ci/test_contract_suites.py +mise exec -- python ops/ci/test_compare_contract_suites.py +mise exec -- rwx lint .rwx/contracts.yml --warnings-as-errors +mise exec -- rwx run .rwx/contracts.yml --wait +``` + +## Cache warming and resource trials + +The producer definitions configure [cache-rebuild triggers](https://www.rwx.com/docs/cache-rebuild-triggers) +restricted to `develop`. Go/Foundry/op-reth triggers target preparation and +compiler tasks only; the pilot warms its independent language tool layers. +Warm-only routing validates the checkout and emits false verdict flags without +running PR change detection. Keep this routing task successful: RWX automatically +skips a task when a referenced dependency was skipped, even if an `if` expression +could bypass its value. Compiler tasks opt in explicitly through the warm flag. +Warming does not run verdicts, publish check successes, or invoke publishers. +A CLI `--init cache-warm=true --target ` rehearses task selection; +it is not proof that the native protected-branch cache-rebuild event fired. +The definitions must reach `develop` before protected-branch warming is active. +RWX rebuild events reset the native cache's initial layer and 48-hour TTL. + +Use `--init build-probe=