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
1 change: 1 addition & 0 deletions .github/scripts/fixtures/rust-ci-scope/collector.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rust_hft/tools/collector/src/polymarket/reference.rs
1 change: 1 addition & 0 deletions .github/scripts/fixtures/rust-ci-scope/control.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
deployment/aliyun/polymarket-raw-ops-cutover.sh
1 change: 1 addition & 0 deletions .github/scripts/fixtures/rust-ci-scope/docs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docs/reports/example.md
1 change: 1 addition & 0 deletions .github/scripts/fixtures/rust-ci-scope/live.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rust_hft/apps/live/src/main.rs
14 changes: 14 additions & 0 deletions .github/scripts/fixtures/rust-ci-scope/metadata.fixture
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"packages": [
{ "name": "hft-collector", "manifest_path": "rust_hft/tools/collector/Cargo.toml", "dependencies": [] },
{
"name": "alpha-harness",
"manifest_path": "rust_hft/alpha-harness/app/Cargo.toml",
"dependencies": [{ "name": "hft-collector", "path": "rust_hft/tools/collector" }]
},
{ "name": "hft-live", "manifest_path": "rust_hft/apps/live/Cargo.toml", "dependencies": [] },
{ "name": "hft-integration", "manifest_path": "rust_hft/market-core/integration/Cargo.toml", "dependencies": [] },
{ "name": "hft-data-adapter-ondo-perps", "manifest_path": "rust_hft/data-pipelines/adapters/adapter-ondo-perps/Cargo.toml", "dependencies": [] },
{ "name": "hft-paper", "manifest_path": "rust_hft/apps/paper/Cargo.toml", "dependencies": [] }
]
}
184 changes: 184 additions & 0 deletions .github/scripts/select-rust-ci-scope.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/env bash
set -euo pipefail

event=${GITHUB_EVENT_NAME:-}
base=
head=HEAD
changed_files=
metadata=
output=${GITHUB_OUTPUT:-/dev/stdout}

while (($#)); do
case "$1" in
--event) event=$2; shift 2 ;;
--base) base=$2; shift 2 ;;
--head) head=$2; shift 2 ;;
--changed-files) changed_files=$2; shift 2 ;;
--metadata) metadata=$2; shift 2 ;;
--output) output=$2; shift 2 ;;
*) printf 'unknown argument: %s\n' "$1" >&2; exit 2 ;;
esac
done

loop=false
handoff=false
json=false
ondo=false
collector=false
control=false
focused=false
toolchain=false

select_all() {
loop=true
handoff=true
json=true
ondo=true
collector=true
control=true
focused=true
toolchain=true
}

emit() {
printf '%s\n' \
"loop=$loop" \
"handoff=$handoff" \
"json=$json" \
"ondo=$ondo" \
"collector=$collector" \
"control=$control" \
"focused=$focused" \
"toolchain=$toolchain" >>"$output"
}

# Branch pushes and manual runs retain the complete build/package contract.
if [[ $event != pull_request ]]; then
select_all
emit
exit 0
fi

repo_root=$(git rev-parse --show-toplevel)
declare -a paths=()
if [[ -n $changed_files ]]; then
while IFS= read -r path; do paths+=("$path"); done <"$changed_files"
else
[[ -n $base ]] || { printf '%s\n' '--base is required for pull requests' >&2; exit 2; }
while IFS= read -r -d '' path; do paths+=("$path"); done \
< <(git diff --no-renames --name-only --diff-filter=ACMRD -z "$base...$head")
fi

needs_metadata=false
for path in "${paths[@]}"; do
case "$path" in
rust_hft/Cargo.toml|rust_hft/Cargo.lock|rust_hft/rust-toolchain*|rust_hft/.cargo/*|.cargo/*|.github/workflows/ci.yml|.github/scripts/select-rust-ci-scope.sh|.github/scripts/test-select-rust-ci-scope.sh|.github/scripts/fixtures/rust-ci-scope/*)
select_all
emit
exit 0
;;
deployment/aliyun/*)
control=true
toolchain=true
;;
rust_hft/*)
needs_metadata=true
;;
esac
done

if [[ $needs_metadata == false ]]; then
emit
exit 0
fi

if [[ -z $metadata ]]; then
metadata=$(mktemp)
trap 'rm -f "$metadata"' EXIT
(cd "$repo_root/rust_hft" && cargo metadata --format-version 1 --no-deps --locked) >"$metadata"
fi

declare -a package_names=() package_dirs=() package_dependencies=()
while IFS=$'\t' read -r name manifest dependencies; do
if [[ $manifest == "$repo_root/"* ]]; then manifest=${manifest#"$repo_root/"}; fi
package_names+=("$name")
package_dirs+=("${manifest%/Cargo.toml}")
package_dependencies+=("$dependencies")
done < <(jq -r '.packages[] | [.name, .manifest_path, ([.dependencies[] | select(.path != null) | .name] | join(","))] | @tsv' "$metadata")

affected=$'\n'
is_affected() { [[ $affected == *$'\n'"$1"$'\n'* ]]; }
mark_affected() { is_affected "$1" || affected+="$1"$'\n'; }

for path in "${paths[@]}"; do
[[ $path == rust_hft/* ]] || continue
owner=
owner_length=0
for ((index = 0; index < ${#package_names[@]}; index++)); do
name=${package_names[$index]}
directory=${package_dirs[$index]}
if [[ $path == "$directory"/* && ${#directory} -gt $owner_length ]]; then
owner=$name
owner_length=${#directory}
fi
done
if [[ -z $owner || $owner == rust-hft-workspace ]]; then
select_all
emit
exit 0
fi
mark_affected "$owner"
done

# Cargo.toml remains the source of truth for downstream package impact.
changed=true
while [[ $changed == true ]]; do
changed=false
for ((index = 0; index < ${#package_names[@]}; index++)); do
name=${package_names[$index]}
is_affected "$name" && continue
dependency_list=${package_dependencies[$index]}
[[ -n $dependency_list ]] || continue
IFS=',' read -ra dependencies <<<"$dependency_list"
for dependency in "${dependencies[@]}"; do
if [[ -n $dependency ]] && is_affected "$dependency"; then
mark_affected "$name"
changed=true
break
fi
done
done
done

select_if_affected() {
local flag=$1
shift
local package
for package in "$@"; do
if is_affected "$package"; then
case "$flag" in
loop) loop=true ;;
handoff) handoff=true ;;
json) json=true ;;
ondo) ondo=true ;;
collector) collector=true ;;
focused) focused=true ;;
esac
toolchain=true
return
fi
done
}

select_if_affected loop alpha-domain alpha-store alpha-engine alpha-onnx-evaluator \
alpha-harness hft-harnessctl hft-research-ml
select_if_affected handoff hft-live
select_if_affected json hft-integration hft-data-adapter-binance hft-infra-redis hft-live
select_if_affected ondo hft-data-adapter-ondo-perps hft-execution-adapter-ondo-perps hft-live
select_if_affected collector hft-collector
select_if_affected focused hft-live hft-paper hft-all-in-one

# Collector source and its host controls are one release boundary.
if [[ $collector == true ]]; then control=true; fi

emit
78 changes: 78 additions & 0 deletions .github/scripts/test-select-rust-ci-scope.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
selector="$script_dir/select-rust-ci-scope.sh"
fixtures="$script_dir/fixtures/rust-ci-scope"
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

run_case() {
local name=$1 event=$2 changed=$3
local output="$tmp_dir/$name.out"
"$selector" --event "$event" --changed-files "$fixtures/$changed" \
--metadata "$fixtures/metadata.fixture" --output "$output"
printf '%s\n' "$output"
}

assert_flag() {
local output=$1 flag=$2 expected=$3
grep -qx "$flag=$expected" "$output" || {
printf '%s: expected %s=%s\n' "$output" "$flag" "$expected" >&2
cat "$output" >&2
exit 1
}
}

collector=$(run_case collector pull_request collector.txt)
for flag in loop collector control toolchain; do assert_flag "$collector" "$flag" true; done
for flag in handoff json ondo focused; do assert_flag "$collector" "$flag" false; done

live=$(run_case live pull_request live.txt)
for flag in handoff json ondo focused toolchain; do assert_flag "$live" "$flag" true; done
for flag in loop collector control; do assert_flag "$live" "$flag" false; done

control=$(run_case control pull_request control.txt)
for flag in control toolchain; do assert_flag "$control" "$flag" true; done
for flag in loop handoff json ondo collector focused; do assert_flag "$control" "$flag" false; done

docs=$(run_case docs pull_request docs.txt)
for flag in loop handoff json ondo collector control focused toolchain; do
assert_flag "$docs" "$flag" false
done

full=$(run_case full push collector.txt)
for flag in loop handoff json ondo collector control focused toolchain; do
assert_flag "$full" "$flag" true
done

deletion_repo="$tmp_dir/deletion-repo"
mkdir -p "$deletion_repo/rust_hft/tools/collector/src"
git -C "$deletion_repo" init -q
touch "$deletion_repo/rust_hft/tools/collector/src/removed.rs"
git -C "$deletion_repo" add .
git -C "$deletion_repo" -c user.name=CI -c user.email=ci@example.invalid commit -qm base
deletion_base=$(git -C "$deletion_repo" rev-parse HEAD)
rm "$deletion_repo/rust_hft/tools/collector/src/removed.rs"
git -C "$deletion_repo" add -u
git -C "$deletion_repo" -c user.name=CI -c user.email=ci@example.invalid commit -qm deletion
deletion="$tmp_dir/deletion.out"
(cd "$deletion_repo" && "$selector" --event pull_request --base "$deletion_base" \
--head HEAD --metadata "$fixtures/metadata.fixture" --output "$deletion")
for flag in loop collector control toolchain; do assert_flag "$deletion" "$flag" true; done

rename_repo="$tmp_dir/rename-repo"
mkdir -p "$rename_repo/rust_hft/tools/collector/src" "$rename_repo/docs"
git -C "$rename_repo" init -q
touch "$rename_repo/rust_hft/tools/collector/src/moved.rs"
git -C "$rename_repo" add .
git -C "$rename_repo" -c user.name=CI -c user.email=ci@example.invalid commit -qm base
rename_base=$(git -C "$rename_repo" rev-parse HEAD)
git -C "$rename_repo" mv rust_hft/tools/collector/src/moved.rs docs/moved.rs
git -C "$rename_repo" -c user.name=CI -c user.email=ci@example.invalid commit -qm rename
rename="$tmp_dir/rename.out"
(cd "$rename_repo" && "$selector" --event pull_request --base "$rename_base" \
--head HEAD --metadata "$fixtures/metadata.fixture" --output "$rename")
for flag in loop collector control toolchain; do assert_flag "$rename" "$flag" true; done

printf 'rust CI scope selector tests passed\n'
Loading
Loading