-
Notifications
You must be signed in to change notification settings - Fork 63
/
ci-builder.sh
432 lines (319 loc) · 12.9 KB
/
ci-builder.sh
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#!/bin/bash
### do not use '-e'
### @accetto, September 2022
### Updated: January 2023, September 2024
### depends on the script 'builder.sh'
### set the environment variables first, e.g. 'source .secrets'
### usage: './<scrip-name> <command>', where <command>=[all|all-no-push]
die() {
local message="${1:-(unknown)}"
local -i code=${2:-1}
local place="${3:-$0}"
echo -e "\nEXITING at line "${BASH_LINENO[0]}" in '${place}' with code ${code}: ${message}\n" >&2
exit ${code}
}
clear_log() {
### just for debugging
# cp -f "${_ci_builder_log}" "${_ci_builder_log}_copy"
> "${_ci_builder_log}"
echo -e "\n==> EXECUTING @$(date -u +'%Y-%m-%d_%H-%M-%S'): ${0} $@\n"
}
execute_smart() {
if [[ -z "${_option_logall}" ]] ; then
_flag_skip_footer="1"
exec 1>/dev/null
{
### execute without logging
$@
} >&3
else
### execute with logging
$@
fi
}
show_error() {
### don't output to 'stderr' (>&2) here!
echo -e "\nERROR: ${@:-(unknown)}\n"
}
show_log_digest() {
echo -e "\n--> Log digest:\n"
grep -Po "${_regex_log_digest}" "${_ci_builder_log}" | sort -u
echo
}
show_log_stickers() {
echo -e "\n--> Version stickers:\n"
grep "${_regex_log_stickers}" "${_ci_builder_log}" | sort -u
echo
}
show_log_timing() {
echo -e "\n--> Building timing:\n"
grep -P "${_regex_log_timing}" "${_ci_builder_log}"
echo
}
show_log_errors() {
echo -e "\n--> Building errors:\n"
grep -iPn "${_regex_log_errors}" "${_ci_builder_log}"
echo
}
show_unlogged_help() {
# help is never logged
exec 1>&-
{
cat <<EOT
This script can:
- build sets of images using the builder script '${_builder_script}'
- extract selected information from the log
Usage: <script> <mode> <argument> [<optional-argument>]...
${0} [<options>] <command> group <blend> [<blend>]...
${0} [<options>] <command> family <parent-blend> [<child-suffix>]...
${0} [--log-all] log get (digest|stickers|timing|errors)
<options> := (--log-all|--no-cache)
<command> := (all|all-no-push)
<mode> := (group|family)
<blend> := pivotal
|(complete[-latest|-noble|-jammy|-focal|-chromium|-firefox])
|(latest|noble|jammy|focal[-chromium|-firefox])
<parent-blend> := (complete)|(latest|noble|jammy|focal[-chromium|-firefox])
<child-suffix> := depends on context, e.g. '-ver1|-ver2' (currently none supported)
Group mode : All images are processed independently.
Family mode: The children are skipped if a new parent image was not actually built.
Remark: Currently are both modes equivalent, because there are no child suffixes supported.
The command and the blend are passed to the builder script.
The result "<parent-blend><child-suffix>" must be a blend supported by the builder script.
The script creates a complete execution log.
EOT
} >&3
# Examples of family mode:
# Build and publish all blends, not using the Docker builder cache:
# ${0} --no-cache all family complete
# Build the 'latest' and 'latest-ver2' blends, but skip the publishing:
# ${0} all-no-push family latest -ver2
# Build and publish only the 'latest-firefox' and 'latest-firefox-ver2' blends:
# ${0} all family latest-firefox -ver2
# Build and publish only the 'latest' and 'latest-firefox-ver2' blends:
# ${0} all family latest -firefox-ver2
# Examples of group mode:
# Build and publish all blends:
# ${0} all group complete
# Build the 'latest' and 'latest-ver2' blends, but skip the publishing:
# ${0} all-no-push group latest latest-ver2
# Build and publish the images containing Firefox:
# ${0} all group complete-firefox
# Other examples:
# Build only the 'latest' blend and skip the publishing:
# ${0} all-no-push family latest
# ${0} all-no-push group latest
# Display log digest:
# ${0} log get digest
# Display version stickers of newly built images:
# ${0} log get stickers
# Display building timing:
# ${0} log get timing
# Display building errors:
# ${0} log get errors
}
build_single_image() {
local command="${1?Expected command}"
local blend="${2?Expected blend}"
local option_nocache="${3}"
local -i exit_code=0
echo -e "${_log_mark} Building image '${_builder_project}:${blend}'"
### call builder script
./"${_builder_script}" "${blend}" "${command}" "${option_nocache}"
exit_code=$?
if [[ ${exit_code} -ne 0 ]] ; then die "Script '${_builder_script}' failed with code ${exit_code}." ${exit_code} ; fi
if [[ $(tail "${_builder_log}" | grep -c "==> No build needed for '${blend}'") -eq 1 ]] ; then
echo -e "${_log_mark} No build needed for '${_builder_project}:${blend}'."
else
case "${command}" in
all-no-push )
if [[ $(tail "${_builder_log}" | grep -c "==> Built '${blend}'") -eq 1 ]] ; then
echo -e "${_log_mark} Built new '${_builder_project}:${blend}'."
else
echo -e "${_log_mark} Failed to build new '${_builder_project}:${blend}'."
fi
;;
all )
if [[ $(tail "${_builder_log}" | grep -c "==> Published '${blend}'") -eq 1 ]] ; then
echo -e "${_log_mark} Published new '${_builder_project}:${blend}'."
else
echo -e "${_log_mark} Failed to publish new '${_builder_project}:${blend}'."
fi
;;
* )
die "Unknown command: '${command}'"
;;
esac
fi
}
build_family() {
local command="${1?Expected command}"
local parent="${2?Expected parent blend}"
if [[ $# -ge 2 ]] ; then shift 2 ; fi
### note that the option '--no-cache' is passed in by the parent image
build_single_image "${command}" "${parent}" "${_option_nocache}"
### if the parent probe succeeded then probe the children
if [[ $(tail "${_builder_log}" | grep -cE "==> (Published|Built) '${parent}'") -eq 1 ]] ; then
for child in $@ ; do
# note that we do not pass in the option '--no-cache' by children
build_single_image "${command}" "${parent}${child}"
done
fi
}
build_group() {
local command="${1?Expected command}"
if [[ $# -gt 0 ]] ; then shift ; fi
for blend in $@ ; do
# note that the option '--no-cache' is passed in by each image
build_single_image "${command}" ${blend} "${_option_nocache}"
done
}
main() {
if [[ $# -eq 0 ]] ; then
show_unlogged_help
return 0
fi
while [[ $# -gt 0 && "${1}" =~ "--" ]] ; do
case "${1}" in
--no-cache ) _option_nocache="${1}" ;;
--log-all ) _option_logall="${1}" ;;
*)
execute_smart show_error "Unknown option '${1}'"
return 1
;;
esac
shift
done
local command="${1}"
local mode="${2}"
local subject="${3}"
if [[ $# -ge 3 ]] ; then shift 3 ; fi
case "${command}" in
help | --help | -h )
show_unlogged_help
return 0
;;
log )
case "${mode}" in
get )
case "${subject}" in
digest ) execute_smart show_log_digest ;;
stickers ) execute_smart show_log_stickers ;;
timing ) execute_smart show_log_timing ;;
errors ) execute_smart show_log_errors ;;
* )
execute_smart show_error "Unknown 'log get' command argument '${subject}'"
;;
esac
;;
* )
execute_smart show_error "Unknown 'log' command '${mode}'"
;;
esac
;;
all-no-push | all )
case "${mode}" in
group )
case "${subject}" in
pivotal )
clear_log
build_group "${command}" "latest" "jammy" "focal" "latest-firefox" "focal-firefox" "latest-chromium" "focal-chromium"
;;
complete )
clear_log
build_group "${command}" "latest" "jammy" "focal" "latest-firefox" "focal-firefox" "latest-chromium" "focal-chromium"
;;
complete-latest )
clear_log
build_group "${command}" "latest" "latest-firefox" "latest-chromium"
;;
complete-noble )
clear_log
build_group "${command}" "noble" "noble-firefox" "noble-chromium"
;;
complete-jammy )
clear_log
build_group "${command}" "jammy" "jammy-firefox" "jammy-chromium"
;;
complete-focal )
clear_log
build_group "${command}" "focal" "focal-firefox" "focal-chromium"
;;
complete-chromium )
clear_log
build_group "${command}" "latest-chromium" "jammy-chromium" "focal-chromium"
;;
complete-firefox )
clear_log
build_group "${command}" "latest-firefox" "jammy-firefox" "focal-firefox"
;;
latest | latest-chromium | latest-firefox \
| noble | noble-chromium | noble-firefox \
| jammy | jammy-chromium | jammy-firefox \
| focal | focal-chromium | focal-firefox )
clear_log
build_group "${command}" "${subject}" $@
;;
* )
execute_smart show_error "Unknown blend '${subject}'"
;;
esac
;;
family )
case "${subject}" in
complete )
clear_log
build_family "${command}" "latest"
build_family "${command}" "jammy"
build_family "${command}" "focal"
build_family "${command}" "latest-firefox"
build_family "${command}" "jammy-firefox"
build_family "${command}" "focal-firefox"
build_family "${command}" "latest-chromium"
build_family "${command}" "jammy-chromium"
build_family "${command}" "focal-chromium"
;;
latest | latest-chromium | latest-firefox \
| noble | noble-chromium | noble-firefox \
| jammy | jammy-chromium | jammy-firefox \
| focal | focal-chromium | focal-firefox )
clear_log
build_family "${command}" "${subject}" $@
;;
* )
execute_smart show_error "Unknown parent blend '${subject}'"
;;
esac
;;
* )
execute_smart show_error "Unknown mode '${mode}'"
;;
esac
;;
*)
execute_smart show_error "Unknown command '${command}'"
;;
esac
if [[ -z "${_flag_skip_footer}" ]] ; then
echo -e "\n==> FINISHED @$(date -u +'%Y-%m-%d_%H-%M-%S'): ${0} $@\n"
fi
}
declare _builder_project="${BUILDER_REPO:-headless-ubuntu-g3}"
declare _builder_log="scrap_builder.log"
declare _builder_script="builder.sh"
declare _ci_builder_log="scrap_ci-builder.log"
declare _log_mark="\n[CI-BUILDER]"
declare _regex_log_digest="(?<=\[CI-BUILDER\] ).+"
declare _regex_log_stickers="Current version sticker of "
declare _regex_log_timing="==> (EXECUTING @.*builder\.sh.*|FINISHED @.*builder\.sh.*)"
declare _regex_log_errors="\berror\b"
declare _flag_skip_footer=""
declare _option_nocache=""
declare _option_logall=""
### duplicate 'stdout' so we can close it when needed
exec 3>&1
### main entry point
declare -i __exit_code=0
main $@ 2>&1 | tee -a "${_ci_builder_log}"
__exit_code=${PIPESTATUS[0]}
exit ${__exit_code}