Repository navigation
Expand file tree
/
Copy pathselect-matrix.sh
More file actions
executable file
·202 lines (170 loc) · 5.79 KB
/
Copy pathselect-matrix.sh
File metadata and controls
executable file
·202 lines (170 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/usr/bin/env bash
#
# Prints the build job matrix for a PHP branch: one entry per rendered
# <branch>/<suite>/<variant> directory × platform that build-config.json
# enables. Run it after scripts/pin-version.sh has rendered the branch.
#
# The output is a single matrix=<json> line, so it can be appended to
# $GITHUB_OUTPUT as-is and read back with fromJSON() as strategy.matrix.
#
# Usage: scripts/select-matrix.sh <version> [variants] [base]
# e.g. scripts/select-matrix.sh 8.4.13
# scripts/select-matrix.sh 8.4.13 cli,zts bookworm
# scripts/select-matrix.sh 8.6.0RC1
#
# variants is a comma-separated override of build-config.json's variants, and
# base the Debian suite to build on, by default the newest one versions.json
# lists. An empty argument takes the default, so workflow inputs can be passed
# through unchanged.
set -Eeuo pipefail
usage() {
echo >&2 "usage: ${0##*/} <version> [variants] [base] e.g. ${0##*/} 8.4.13 cli,zts bookworm"
exit 2
}
die() {
echo >&2 "error: $*"
exit 1
}
# Succeeds when the first argument equals any of the others.
contains() {
local needle="$1" item
shift
for item; do
[ "$item" = "$needle" ] && return 0
done
return 1
}
# Sets the global branch to the versions.json entry of a branch (8.4) or a
# full version (8.4.13), which for a pre-release (8.6.0RC1) is <branch>-rc.
parse_branch() {
local version="$1"
if [[ ! "$version" =~ ^([0-9]+\.[0-9]+)(\.[0-9]+((alpha|beta|RC)[0-9]+)?)?$ ]]; then
echo >&2 "error: '$version' is not a PHP branch or version like 8.4, 8.4.13 or 8.6.0RC1"
usage
fi
branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]:+-rc}"
}
# Prints the family a suite belongs to. Windows belongs to none, so no
# build-config.json setting can select it.
suite_family() {
case "$1" in
windows*) ;;
alpine*) echo alpine ;;
*) echo debian ;;
esac
}
family_enabled() {
local family="$1"
[ -n "$family" ] && jq -e --arg family "$family" '.families[$family] == true' build-config.json > /dev/null
}
# Prints the GitHub-hosted runner that builds a platform natively.
runner_for() {
case "$1" in
linux/amd64) echo ubuntu-24.04 ;;
linux/arm64) echo ubuntu-24.04-arm ;;
*) die "no runner builds platform '$1'" ;;
esac
}
# Prints the branch's suites in versions.json order, which upstream keeps
# newest first.
branch_suites() {
jq -r --arg branch "$branch" '.[$branch].variants[] | split("/")[0]' versions.json | awk '!seen[$0]++'
}
suite_variants() {
local suite="$1"
jq -r --arg branch "$branch" --arg prefix "$suite/" \
'.[$branch].variants[] | select(startswith($prefix)) | ltrimstr($prefix)' \
versions.json
}
# Sets the global suite to base, or to the newest Debian suite when base is
# empty. Fails for a suite the branch lacks or whose family is disabled.
select_suite() {
local base="$1" candidate family
local -a suites enabled=()
mapfile -t suites < <(branch_suites)
for candidate in "${suites[@]}"; do
if family_enabled "$(suite_family "$candidate")"; then
enabled+=( "$candidate" )
fi
done
if [ -z "$base" ]; then
for candidate in "${suites[@]}"; do
if [ "$(suite_family "$candidate")" = debian ]; then
base="$candidate"
break
fi
done
[ -n "$base" ] || die "versions.json lists no Debian suite for PHP $branch"
fi
family="$(suite_family "$base")"
if ! contains "$base" "${suites[@]}"; then
die "PHP $branch has no suite '$base'; valid suites: ${enabled[*]:-none}"
elif [ -z "$family" ]; then
die "suite '$base' belongs to no family this repo builds; valid suites: ${enabled[*]:-none}"
elif ! family_enabled "$family"; then
die "suite '$base' is in the $family family, which build-config.json disables (families.$family is false)"
fi
suite="$base"
}
# Sets the global array variants to the comma-separated override, or to
# build-config.json's variants when it is empty. Fails for any variant the
# suite does not have, so a typo surfaces before a build starts.
select_variants() {
local override="$1" variant
local -a requested available
variants=()
mapfile -t available < <(suite_variants "$suite")
if [ -n "$override" ]; then
IFS=', ' read -r -a requested <<< "$override"
else
mapfile -t requested < <(jq -r '.variants[]' build-config.json)
fi
for variant in "${requested[@]}"; do
if ! contains "$variant" "${available[@]}"; then
die "PHP $branch has no variant '$variant' on $suite; available: ${available[*]}"
fi
contains "$variant" "${variants[@]}" || variants+=( "$variant" )
done
[ "${#variants[@]}" -gt 0 ] || die "no variants selected"
}
# Prints the matrix as compact JSON, {"include": [...]}, with one entry per
# directory × platform.
build_matrix() {
local variant directory platform runner
local -a platforms entries=()
mapfile -t platforms < <(jq -r '.platforms[]' build-config.json)
[ "${#platforms[@]}" -gt 0 ] || die "build-config.json lists no platforms"
for variant in "${variants[@]}"; do
directory="$branch/$suite/$variant"
[ -d "$directory" ] || die "$directory does not exist; run scripts/pin-version.sh first"
for platform in "${platforms[@]}"; do
runner="$(runner_for "$platform")" || exit
entries+=( "$(
jq -nc \
--arg directory "$directory" \
--arg variant "$variant" \
--arg suite "$suite" \
--arg platform "$platform" \
--arg runner "$runner" \
'$ARGS.named'
)" )
done
done
printf '%s\n' "${entries[@]}" | jq -sc '{ include: . }'
}
main() {
[ "$#" -ge 1 ] && [ "$#" -le 3 ] || usage
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.."
parse_branch "$1"
if ! jq -e --arg branch "$branch" '.[$branch] | type == "object"' versions.json > /dev/null; then
die "PHP $branch is not in versions.json; merge upstream docker-library/php first"
fi
select_suite "${3:-}"
select_variants "${2:-}"
local matrix
matrix="$(build_matrix)"
echo "matrix=$matrix"
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi