Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 16 additions & 27 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,19 @@ orbs:

jobs:
# --------------------------------------------------------------------------
# This job prepares the dynamic continuation pipeline in 6 steps:
# This job prepares the dynamic continuation pipeline in 7 steps:
#
# 1. Install yq (YAML processor for merging configs)
# 2. Store string pipeline params → /tmp/pipeline-parameters.json
# 3. Store boolean pipeline params → /tmp/pipeline-parameters.json
# 4. Detect file-tree changes → /tmp/pipeline-parameters.json
# 5. Decision tree: read the JSON, enable c-run_* workflow flags, strip
# intermediate params, write final JSON
# 5. Routing policy (compute-workflow-conditions.sh): read the JSON, enable
# c-run_* workflow flags, strip intermediate params, write final JSON
# 6. Merge continuation YAML configs → /tmp/merged-config.yml
# 7. Call CircleCI continuation API with merged config + JSON params
#
# The routing data (schedules, dispatch flags, change patterns, passthrough
# params) is declarative in routing.yml; the scripts only hold the logic.
# The result: only workflows whose c-run_* flag is true will execute.
# --------------------------------------------------------------------------
prepare-continuation-config:
Expand Down Expand Up @@ -141,8 +143,8 @@ jobs:
# 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.
# These dispatch flags are consumed only by the decision tree below;
# they do NOT reach continuation configs.
# These dispatch flags are consumed only by the routing policy
# (compute-workflow-conditions.sh); they do NOT reach continuation configs.
- run:
name: Store boolean pipeline parameters in JSON file
command: .circleci/scripts/collect-params.sh bool
Expand All @@ -160,37 +162,24 @@ jobs:
c-rust_ci_dispatch: << pipeline.parameters.rust_ci_dispatch >>
c-rust_e2e_dispatch: << pipeline.parameters.rust_e2e_dispatch >>
c-l2_fork_test_dispatch: << pipeline.parameters.l2_fork_test_dispatch >>
# Each env var value is a POSIX Extended Regular Expression (ERE, like grep -E).
# Matched line-by-line against `git diff --name-only origin/develop...HEAD`.
# Result: the c-* key is set to true/false in the JSON.
# Change-detection patterns live in routing.yml (change_patterns.any and
# change_patterns.all). Each is a POSIX ERE matched line-by-line against
# `git diff --name-only origin/develop...HEAD`; collect-params.sh writes
# the resulting c-<name> boolean into the JSON.
#
# Test a pattern locally:
# git diff --name-only origin/develop...HEAD | grep -E "^your/pattern/"
#
# When to add: you need path-based workflow gating. Also add a corresponding
# is_true/run call in the decision tree below.
- run:
name: Detect file tree changes and store in JSON file
command: .circleci/scripts/collect-params.sh detect
environment:
c-circleci_changed: "^\\.circleci/"
c-contracts_changed: "^(packages/contracts-bedrock|op-core/forks|op-core/nuts|\\.circleci|\\.github|ops/check-changed)/|^(package\\.json|mise\\.toml)$"
c-docs_changes_detected: "^docs/public-docs/"
c-rust_changes_detected: "^(rust|op-e2e|\\.circleci)/"
# Safer "docs-only" detection: true iff EVERY changed file is under docs/public-docs/.
# Using an inclusion (all-match) check instead of an exclusion of known code categories
# makes the fast path safe-by-default — any path not enumerated by the patterns above
# still falls through to the full main workflow.
- run:
name: Detect docs-only changes (all-match)
command: .circleci/scripts/collect-params.sh detect_all
environment:
c-only_docs_changes: "^docs/public-docs/"
# Decision tree: determines which workflows to enable based on trigger,
# branch, and params. The logic lives in compute-workflow-conditions.sh
# (using the run/is_true/param/finalize helpers from workflow-helpers.sh);
# each enabled c-run_<name> maps 1:1 to
# "when: << pipeline.parameters.c-run_<name> >>" in a continuation config.
# Routing policy: enables the c-run_* workflow flags based on trigger,
# branch, schedule, and the detected params. The workflow lists are data
# in routing.yml; the conditions live in compute-workflow-conditions.sh.
# Each c-run_<name> maps 1:1 to "when: << pipeline.parameters.c-run_<name> >>"
# in a continuation config under .circleci/continue/.
- run:
name: Compute workflow conditions from pipeline parameters and store in JSON file
environment:
Expand Down
2 changes: 1 addition & 1 deletion .circleci/continue/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ parameters:
go-cache-version:
type: string
default: "v0.1"
# Workflow activation flags (set by the decision tree in config.yml)
# Workflow activation flags (set by compute-workflow-conditions.sh)
c-run_main:
type: boolean
default: false
Expand Down
2 changes: 1 addition & 1 deletion .circleci/continue/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ parameters:
c-base_image:
type: string
default: default
# Workflow activation flags (set by the decision tree in config.yml)
# Workflow activation flags (set by compute-workflow-conditions.sh)
c-run_rust_ci:
type: boolean
default: false
Expand Down
2 changes: 1 addition & 1 deletion .circleci/continue/rust-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ parameters:
c-go-cache-version:
type: string
default: "v0.1"
# Workflow activation flags (set by the decision tree in config.yml)
# Workflow activation flags (set by compute-workflow-conditions.sh)
c-run_rust_e2e_ci:
type: boolean
default: false
Expand Down
84 changes: 84 additions & 0 deletions .circleci/routing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 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_<name>" boolean parameter declared
# in a continuation config under .circleci/continue/. Enabling c-run_<name>
# 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
- circleci_schedule_trigger_check

# 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
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-<name> 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)/"
# "all": c-<name> 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
25 changes: 15 additions & 10 deletions .circleci/scripts/collect-params.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
# 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 — treat c-* env var values as ERE patterns; true iff ANY changed file matches
# collect-params.sh detect_all — treat c-* env var values as ERE patterns; true iff EVERY changed file matches (and there is at least one)
# collect-params.sh detect — match routing.yml change_patterns.any against changed files; c-<name> true iff ANY file matches
# collect-params.sh detect_all — match routing.yml change_patterns.all against changed files; c-<name> 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.
# Env vars whose name starts with c- are processed; all others are ignored.
set -euo pipefail

MODE="${1:?Usage: collect-params.sh <str|bool|detect>}"
MODE="${1:?Usage: collect-params.sh <str|bool|detect|detect_all>}"
Comment thread
ajsutton marked this conversation as resolved.
OUTPUT="/tmp/pipeline-parameters.json"
ROUTING="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/routing.yml"

[ -f "${OUTPUT}" ] || echo '{}' > "${OUTPUT}"

Expand Down Expand Up @@ -40,14 +42,17 @@ case "${MODE}" in
;;

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:-<none>}"
echo "====================="

while IFS='=' read -r key pattern; do
[[ "${key}" == c-* ]] || continue
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
Expand All @@ -65,13 +70,13 @@ case "${MODE}" in
result=false
fi
fi
json=$(echo "${json}" | jq --argjson v "${result}" '. + {"'"${key}"'": $v}')
echo " [${MODE}] ${key} = ${result} (pattern: ${pattern})"
done < <(env | sort)
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, or detect." >&2
echo "ERROR: Unknown mode '${MODE}'. Use str, bool, detect, or detect_all." >&2
exit 1
;;
esac
Expand Down
67 changes: 36 additions & 31 deletions .circleci/scripts/compute-workflow-conditions.sh
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
#!/usr/bin/env bash
# Workflow routing decision tree for the CircleCI setup pipeline.
# Workflow routing policy for the CircleCI setup pipeline.
#
# Reads the change-detection / dispatch params already written to
# /tmp/pipeline-parameters.json by earlier steps and enables the matching
# c-run_* workflow flags based on the trigger source, branch, tag, and schedule.
# 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_<name>: true in the JSON, which maps 1:1 to
# "when: << pipeline.parameters.c-run_<name> >>" in a continuation config.
#
# Inputs (set by the config.yml step environment):
# BRANCH, TRIGGER_SOURCE, TAG, SCHEDULE_NAME
#
# Helpers (workflow-helpers.sh):
# run foo → sets c-run_foo: true in JSON (maps 1:1 to
# "when: << pipeline.parameters.c-run_foo >>" in a continuation config)
# is_true x → checks if c-x is true in JSON (set by earlier steps)
# param x → reads raw value of c-x from JSON
# finalize → strips intermediate params, keeps only the listed whitelist + all c-run_* flags
# run <wf...> enable workflows by literal name
# run_group <sec> <key> enable the workflows listed under routing.yml sec.key
# is_true <x> true if c-x is true in the JSON
# param <x> raw value of c-x from the JSON
# finalize strip intermediate params, keep c-run_* + passthrough
#
# How to add a new workflow:
# 1. Add "c-run_your_workflow: {type: boolean, default: false}" in the relevant continuation config
# 2. Add "run your_workflow" in the appropriate branch below
# 1. Declare "c-run_<name>: {type: boolean, default: false}" in a continuation
# config under .circleci/continue/.
# 2. Wire it in here (literal "run <name>" for a one-off, or add it to the
# relevant routing.yml list and use run_group).
set -euo pipefail

# shellcheck disable=SC1091 # sourced helper resolved at runtime, not by shellcheck
Expand All @@ -26,12 +32,9 @@ init_json

case "${TRIGGER_SOURCE}" in

# Scheduled pipelines: map schedule name workflows
# Scheduled pipelines: map schedule name -> workflows (routing.yml schedules).
scheduled_pipeline)
case "${SCHEDULE_NAME}" in
build_four_hours) run scheduled_todo_issues scheduled_cannon_full_tests ;;
build_daily) run scheduled_preimage_reproducibility scheduled_stale_check scheduled_heavy_fuzz_tests scheduled_daily_tests circleci_schedule_trigger_check ;;
esac
run_group schedules "${SCHEDULE_NAME}"
;;

# Webhook (push events)
Expand Down Expand Up @@ -116,27 +119,29 @@ case "${TRIGGER_SOURCE}" in
fi
;;

# API triggers: dispatch flags select workflows
# 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 main contracts_feature_tests
run_group api_dispatch main_dispatch
fi
if is_true fault_proofs_dispatch; then run develop_fault_proofs; fi
if is_true kontrol_dispatch; then run develop_kontrol_tests; fi
if is_true cannon_full_test_dispatch; then run scheduled_cannon_full_tests; fi
if is_true reproducibility_dispatch; then run scheduled_preimage_reproducibility; fi
if is_true stale_check_dispatch; then run scheduled_stale_check; fi
if is_true heavy_fuzz_dispatch; then run scheduled_heavy_fuzz_tests; fi
if is_true publish_contract_artifacts_dispatch; then run publish_contract_artifacts; fi
if is_true l2_fork_test_dispatch; then run l2_fork_test; fi
if is_true rust_ci_dispatch; then run rust_ci; fi
if is_true rust_e2e_dispatch; then run rust_e2e_ci; 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 ;;
Comment thread
ajsutton marked this conversation as resolved.
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 close_issue
run_group api_dispatch labeled_pr
fi
;;
esac

# Params to forward to continuation configs (everything else is stripped)
finalize "c-default_docker_image,c-rust_base_image,c-base_image,c-github-event-base64,c-go-cache-version,c-publish_contract_artifacts_ref"
finalize
Loading
Loading