Skip to content

Commit 88b0dde

Browse files
ondrejmirtesclaude
andcommitted
Reuse turbo extension binaries compiled from identical build inputs
A new turbo-origins job finds, for every binary, the newest earlier run of phar.yml triggered by a push to 2.3.x that compiled it from the same turbo-ext sources, build files and compile job definitions. The compile legs download that binary instead of building it, and still run the version check and the differential tests against it. Runs that reused a binary mark it, so later runs take it from the run that compiled it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSY1tPWMAi4tRmWvnj48vz
1 parent 1a521a1 commit 88b0dde

2 files changed

Lines changed: 311 additions & 6 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
# Finds, for every turbo extension binary the compile jobs in phar.yml
3+
# produce, the newest earlier run on $BRANCH that compiled it from build
4+
# inputs identical to HEAD's, and writes {"<artifact name>": <run id>} to
5+
# the `origins` step output. A compile leg whose artifact is listed
6+
# downloads it from that run instead of building it again (see the
7+
# turbo-origins job in phar.yml).
8+
#
9+
# Only these runs qualify, each condition checked here rather than trusted
10+
# from the API query:
11+
# - runs of .github/workflows/phar.yml in this repository, triggered by a
12+
# push to $BRANCH — never a pull request, whose code nobody reviewed yet
13+
# (a fork's branch can be named $BRANCH too, hence the event and head
14+
# repository checks)
15+
# - whose head commit is an ancestor of HEAD
16+
# - whose build inputs (BUILD_INPUT_PATHS, and the compile jobs' definitions
17+
# in phar.yml) are identical to HEAD's
18+
# - for each artifact separately: the run uploaded it (the upload is the
19+
# last step of a compile leg, after the differential tests) and did not
20+
# mark it as reused itself (turbo-reused-<artifact name>) — so a binary is
21+
# always taken from the run that compiled it, never from a chain of runs
22+
# that passed it along, and it stops being reused when that run's
23+
# artifacts expire.
24+
#
25+
# Anything unexpected (an API error, a commit missing from the checkout)
26+
# disqualifies the run or ends the search; nothing here fails the job, since
27+
# an empty map only means every leg compiles.
28+
29+
BUILD_INPUT_PATHS=(
30+
turbo-ext/src
31+
turbo-ext/Makefile
32+
turbo-ext/config.m4
33+
turbo-ext/config.w32
34+
turbo-ext/bin/pgo-train.sh
35+
.github/turbo-build
36+
.github/scripts/install-php86-windows.sh
37+
)
38+
WORKFLOW_PATH=".github/workflows/phar.yml"
39+
# Workflow-level env and the three compile jobs, as JSON: comment-only edits
40+
# do not change it.
41+
COMPILE_JOBS_QUERY='[.env, .jobs["turbo-compile"], .jobs["turbo-compile-musl-arm64"], .jobs["turbo-compile-windows"]]'
42+
RUNS_LIMIT=100
43+
44+
origins='{}'
45+
finish() {
46+
echo "origins=$origins" >> "${GITHUB_OUTPUT:-/dev/stdout}"
47+
exit 0
48+
}
49+
50+
if [ -z "${BRANCH:-}" ] || [ -z "${GITHUB_REPOSITORY:-}" ]; then
51+
echo "::warning::BRANCH and GITHUB_REPOSITORY must be set; compiling everything"
52+
finish
53+
fi
54+
55+
head="$(git rev-parse HEAD)" || finish
56+
if ! head_jobs="$(git show "$head:$WORKFLOW_PATH" | yq -o=json "$COMPILE_JOBS_QUERY")"; then
57+
echo "::warning::could not read the compile jobs of $WORKFLOW_PATH; compiling everything"
58+
finish
59+
fi
60+
61+
# The branch filter alone: combined with event=push the endpoint returns a
62+
# stale subset of the runs (observed 2026-09-25: 21 runs, the newest a week
63+
# old). The event is checked below instead.
64+
if ! runs="$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/phar.yml/runs?branch=$BRANCH&per_page=$RUNS_LIMIT" \
65+
| jq -r --arg repo "$GITHUB_REPOSITORY" --arg branch "$BRANCH" --arg path "$WORKFLOW_PATH" '
66+
.workflow_runs[]
67+
| select(.event == "push"
68+
and .head_branch == $branch
69+
and .path == $path
70+
and .repository.full_name == $repo
71+
and .head_repository.full_name == $repo)
72+
| "\(.id) \(.head_sha)"')"; then
73+
echo "::warning::could not list the runs on $BRANCH; compiling everything"
74+
finish
75+
fi
76+
77+
while read -r run_id sha; do
78+
[ -n "$run_id" ] || continue
79+
if ! git cat-file -e "$sha^{commit}" 2> /dev/null; then
80+
echo "run $run_id: $sha is not in the checkout"
81+
continue
82+
fi
83+
if ! git merge-base --is-ancestor "$sha" "$head"; then
84+
echo "run $run_id: $sha is not an ancestor of HEAD"
85+
continue
86+
fi
87+
if ! git diff --quiet "$sha" "$head" -- "${BUILD_INPUT_PATHS[@]}"; then
88+
echo "run $run_id: build inputs differ at $sha"
89+
continue
90+
fi
91+
if ! run_jobs="$(git show "$sha:$WORKFLOW_PATH" | yq -o=json "$COMPILE_JOBS_QUERY")" || [ "$run_jobs" != "$head_jobs" ]; then
92+
echo "run $run_id: compile jobs differ at $sha"
93+
continue
94+
fi
95+
if ! artifacts="$(gh api --paginate "repos/$GITHUB_REPOSITORY/actions/runs/$run_id/artifacts?per_page=100" \
96+
--jq '.artifacts[] | select(.expired | not) | .name')"; then
97+
echo "::warning::could not list the artifacts of run $run_id; stopping the search"
98+
break
99+
fi
100+
if ! origins="$(jq -c --argjson run "$run_id" --arg artifacts "$artifacts" '
101+
($artifacts | split("\n")) as $names
102+
| reduce ($names[] | select(startswith("phpstan_turbo-"))) as $name (.;
103+
if has($name) or ($names | any(. == "turbo-reused-" + $name)) then . else .[$name] = $run end)
104+
' <<< "$origins")"; then
105+
origins='{}'
106+
finish
107+
fi
108+
echo "run $run_id: identical build inputs at $sha"
109+
done <<< "$runs"
110+
111+
jq -r 'to_entries[] | "\(.key) <- run \(.value)"' <<< "$origins"
112+
finish

0 commit comments

Comments
 (0)