Skip to content

Commit 2af6bcd

Browse files
committed
update-template.sh: add update-template.sh script to update all distributions
- Move functions common to all distributions here. - Scripts for specific distributions accept distribution-specific options and perform additional actions. Signed-off-by: Norio Nomura <[email protected]>
1 parent 19791f9 commit 2af6bcd

File tree

2 files changed

+246
-84
lines changed

2 files changed

+246
-84
lines changed

hack/update-template-ubuntu.sh

Lines changed: 37 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
#!/usr/bin/env bash
22

3+
set -eu -o pipefail
4+
5+
# Functions in this script assume error handling with 'set -e'.
6+
# To ensure 'set -e' works correctly:
7+
# - Use 'set +e' before assignments and '$(set -e; <function>)' to capture output without exiting on errors.
8+
# - Avoid calling functions directly in conditions to prevent disabling 'set -e'.
9+
# - Use 'shopt -s inherit_errexit' (Bash 4.4+) to avoid repeated 'set -e' in all '$(...)'.
10+
shopt -s inherit_errexit || error_exit "inherit_errexit not supported. Please use bash 4.4 or later."
11+
312
function ubuntu_print_help() {
413
cat <<HELP
514
$(basename "${BASH_SOURCE[0]}"): Update the Ubuntu image location in the specified templates
@@ -41,40 +50,11 @@ Flags:
4150
HELP
4251
}
4352

44-
scriptdir=$(dirname "${BASH_SOURCE[0]}")
45-
# shellcheck source=./cache-common-inc.sh
46-
# shellcheck disable=SC1091
47-
. "${scriptdir}/cache-common-inc.sh"
48-
49-
set -eu -o pipefail
50-
51-
# Functions in this script assume error handling with 'set -e'.
52-
# To ensure 'set -e' works correctly:
53-
# - Use 'set +e' before assignments and '$(set -e; <function>)' to capture output without exiting on errors.
54-
# - Avoid calling functions directly in conditions to prevent disabling 'set -e'.
55-
# - Use 'shopt -s inherit_errexit' (Bash 4.4+) to avoid repeated 'set -e' in all '$(...)'.
56-
shopt -s inherit_errexit || error_exit "inherit_errexit not supported. Please use bash 4.4 or later."
57-
5853
readonly -A ubuntu_base_urls=(
5954
[minimal]=https://cloud-images.ubuntu.com/minimal/releases/
6055
[server]=https://cloud-images.ubuntu.com/releases/
6156
)
6257

63-
# validate_url checks if the URL is valid and returns the location if it is.
64-
# If the URL is redirected, it returns the redirected location.
65-
# e.g.
66-
# ```console
67-
# validate_url https://cloud-images.ubuntu.com/server/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
68-
# https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
69-
# ```
70-
function validate_url() {
71-
local url=$1
72-
code_location=$(curl -sSL -o /dev/null -I -w "%{http_code}\t%{url_effective}" "${url}")
73-
read -r code location <<<"${code_location}"
74-
[[ ${code} -eq 200 ]] || error_exit "[${code}]: The image is not available for download from ${location}"
75-
echo "${location}"
76-
}
77-
7858
# ubuntu_base_url returns the base URL for the given flavor.
7959
# e.g.
8060
# ```console
@@ -115,48 +95,6 @@ function ubuntu_image_url_try_replace_release_with_version() {
11595
set -e
11696
}
11797

118-
# json prints the JSON object with the given arguments.
119-
# json [key value ...]
120-
# if the value is empty, both key and value are omitted.
121-
# e.g.
122-
# ```console
123-
# json location https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img arch amd64 digest sha256:...
124-
# {"location":"https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img","arch":"amd64","digest":"sha256:..."}
125-
# ```
126-
function json() {
127-
local args=() pattern='^(\[.*\]|\{.*\})$' value
128-
[[ ! -p /dev/stdin ]] && args+=(--null-input)
129-
while [[ $# -gt 0 ]]; do
130-
value="${2-}"
131-
if [[ ${value} =~ ${pattern} ]]; then
132-
args+=(--argjson "${1}" "${value}")
133-
elif [[ -n ${value} ]]; then
134-
args+=(--arg "${1}" "${value}")
135-
fi # omit empty values
136-
shift
137-
shift # shift 2 does not work when $# is 1
138-
done
139-
jq -c "${args[@]}" '. + $ARGS.named | if . == {} then empty else . end'
140-
}
141-
142-
# json_vars prints the JSON object with the given variable names.
143-
# e.g.
144-
# ```console
145-
# location=https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img
146-
# arch=amd64
147-
# digest=sha256:...
148-
# json_vars location arch digest
149-
# {"location":"https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img","arch":"amd64","digest":"sha256:..."}
150-
# ```
151-
function json_vars() {
152-
local args=() var
153-
for var in "$@"; do
154-
[[ -v ${var} ]] || error_exit "${var} is not set"
155-
args+=("${var}" "${!var}")
156-
done
157-
json "${args[@]}"
158-
}
159-
16098
# ubuntu_image_url_latest returns the latest image URL and its digest for the given flavor, version, arch, and path suffix.
16199
function ubuntu_image_url_latest() {
162100
local flavor=$1 version=$2 arch=$3 path_suffix=$4 base_url ubuntu_downloaded_json jq_filter location_digest_release
@@ -246,15 +184,6 @@ function ubuntu_image_entry_with_kernel_info() {
246184
json_vars kernel initrd <<<"${image_entry}"
247185
}
248186

249-
# limayaml_arch returns the arch in the lima.yaml format
250-
function limayaml_arch() {
251-
local arch=$1
252-
arch=${arch/amd64/x86_64}
253-
arch=${arch/arm64/aarch64}
254-
arch=${arch/armhf/armv7l}
255-
echo "${arch}"
256-
}
257-
258187
function ubuntu_flavor_from_location() {
259188
local location=$1 location_basename flavor
260189
location_basename=$(basename "${location}")
@@ -402,6 +331,29 @@ function ubuntu_image_entry_for_image_kernel_flavor_version() {
402331
fi
403332
}
404333

334+
# check if the script is executed or sourced
335+
# shellcheck disable=SC1091
336+
if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
337+
scriptdir=$(dirname "${BASH_SOURCE[0]}")
338+
# shellcheck source=./cache-common-inc.sh
339+
. "${scriptdir}/cache-common-inc.sh"
340+
341+
# shellcheck source=/dev/null # avoid shellcheck hangs on source looping
342+
. "${scriptdir}/update-template.sh"
343+
else
344+
# this script is sourced
345+
if [[ -v SUPPORTED_DISTRIBUTIONS ]]; then
346+
SUPPORTED_DISTRIBUTIONS+=("ubuntu")
347+
else
348+
declare -a SUPPORTED_DISTRIBUTIONS=("ubuntu")
349+
fi
350+
# required functions for Ubuntu
351+
function ubuntu_cache_key_for_image_kernel() { ubuntu_cache_key_for_image_kernel_flavor_version "$@"; }
352+
function ubuntu_image_entry_for_image_kernel() { ubuntu_image_entry_for_image_kernel_flavor_version "$@"; }
353+
354+
return 0
355+
fi
356+
405357
declare -a templates=()
406358
declare overriding_flavor=
407359
declare overriding_version=
@@ -411,6 +363,7 @@ while [[ $# -gt 0 ]]; do
411363
ubuntu_print_help
412364
exit 0
413365
;;
366+
-d | --debug) set -x ;;
414367
--flavor)
415368
if [[ -n $2 && $2 != -* ]]; then
416369
overriding_flavor="$2"
@@ -444,7 +397,7 @@ if [[ ${#templates[@]} -eq 0 ]]; then
444397
exit 0
445398
fi
446399

447-
declare -A ubuntu_image_entry_cache=()
400+
declare -A image_entry_cache=()
448401

449402
for template in "${templates[@]}"; do
450403
echo "Processing ${template}"
@@ -477,16 +430,16 @@ for template in "${templates[@]}"; do
477430
[[ $? -eq 0 ]] || continue
478431
image_entry=$(
479432
set -e # Enable 'set -e' for the next command.
480-
if [[ -v ubuntu_image_entry_cache[${cache_key}] ]]; then
481-
echo "${ubuntu_image_entry_cache[${cache_key}]}"
433+
if [[ -v image_entry_cache[${cache_key}] ]]; then
434+
echo "${image_entry_cache[${cache_key}]}"
482435
else
483436
ubuntu_image_entry_for_image_kernel_flavor_version "${location}" "${kernel_location}" "${overriding_flavor}" "${overriding_version}"
484437
fi
485438
) # Check exit status separately to prevent disabling 'set -e' by using the function call in the condition.
486439
# shellcheck disable=2181
487440
[[ $? -eq 0 ]] || continue
488441
set -e
489-
ubuntu_image_entry_cache[${cache_key}]="${image_entry}"
442+
image_entry_cache[${cache_key}]="${image_entry}"
490443
if [[ -n ${image_entry} ]]; then
491444
[[ ${kernel_cmdline} != "null" ]] &&
492445
jq -e 'has("kernel")' <<<"${image_entry}" >/dev/null &&

hack/update-template.sh

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env bash
2+
3+
set -eu -o pipefail
4+
5+
# Functions in this script assume error handling with 'set -e'.
6+
# To ensure 'set -e' works correctly:
7+
# - Use 'set +e' before assignments and '$(set -e; <function>)' to capture output without exiting on errors.
8+
# - Avoid calling functions directly in conditions to prevent disabling 'set -e'.
9+
# - Use 'shopt -s inherit_errexit' (Bash 4.4+) to avoid repeated 'set -e' in all '$(...)'.
10+
shopt -s inherit_errexit || error_exit "inherit_errexit not supported. Please use bash 4.4 or later."
11+
12+
function print_help() {
13+
cat <<HELP
14+
$(basename "${BASH_SOURCE[0]}"): Update the image location in the specified templates
15+
16+
Usage:
17+
$(basename "${BASH_SOURCE[0]}") <template.yaml>...
18+
19+
Description:
20+
This script updates the image location in the specified templates.
21+
If the image location in the template contains a release date in the URL, the script replaces it with the latest available date.
22+
23+
Examples:
24+
Update the Ubuntu image location in templates/**.yaml:
25+
$ $(basename "${BASH_SOURCE[0]}") templates/**.yaml
26+
27+
Update the Ubuntu image location in ~/.lima/ubuntu/lima.yaml:
28+
$ $(basename "${BASH_SOURCE[0]}") ~/.lima/ubuntu/lima.yaml
29+
30+
Flags:
31+
-h, --help Print this help message
32+
HELP
33+
}
34+
35+
# json prints the JSON object with the given arguments.
36+
# json [key value ...]
37+
# if the value is empty, both key and value are omitted.
38+
# e.g.
39+
# ```console
40+
# json location https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img arch amd64 digest sha256:...
41+
# {"location":"https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img","arch":"amd64","digest":"sha256:..."}
42+
# ```
43+
function json() {
44+
local args=() pattern='^(\[.*\]|\{.*\})$' value
45+
[[ ! -p /dev/stdin ]] && args+=(--null-input)
46+
while [[ $# -gt 0 ]]; do
47+
value="${2-}"
48+
if [[ ${value} =~ ${pattern} ]]; then
49+
args+=(--argjson "${1}" "${value}")
50+
elif [[ -n ${value} ]]; then
51+
args+=(--arg "${1}" "${value}")
52+
fi # omit empty values
53+
shift
54+
shift # shift 2 does not work when $# is 1
55+
done
56+
jq -c "${args[@]}" '. + $ARGS.named | if . == {} then empty else . end'
57+
}
58+
59+
# json_vars prints the JSON object with the given variable names.
60+
# e.g.
61+
# ```console
62+
# location=https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img
63+
# arch=amd64
64+
# digest=sha256:...
65+
# json_vars location arch digest
66+
# {"location":"https://cloud-images.ubuntu.com/minimal/releases/24.04/release-20210914/ubuntu-24.04-minimal-cloudimg-amd64.img","arch":"amd64","digest":"sha256:..."}
67+
# ```
68+
function json_vars() {
69+
local args=() var
70+
for var in "$@"; do
71+
[[ -v ${var} ]] || error_exit "${var} is not set"
72+
args+=("${var}" "${!var}")
73+
done
74+
json "${args[@]}"
75+
}
76+
77+
# limayaml_arch returns the arch in the lima.yaml format
78+
function limayaml_arch() {
79+
local arch=$1
80+
arch=${arch/amd64/x86_64}
81+
arch=${arch/arm64/aarch64}
82+
arch=${arch/armhf/armv7l}
83+
echo "${arch}"
84+
}
85+
86+
# validate_url checks if the URL is valid and returns the location if it is.
87+
# If the URL is redirected, it returns the redirected location.
88+
# e.g.
89+
# ```console
90+
# validate_url https://cloud-images.ubuntu.com/server/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
91+
# https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
92+
# ```
93+
function validate_url() {
94+
local url=$1
95+
code_location=$(curl -sSL -o /dev/null -I -w "%{http_code}\t%{url_effective}" "${url}")
96+
read -r code location <<<"${code_location}"
97+
[[ ${code} -eq 200 ]] || error_exit "[${code}]: The image is not available for download from ${location}"
98+
echo "${location}"
99+
}
100+
101+
# check if the script is executed or sourced
102+
# shellcheck disable=SC1091
103+
if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
104+
scriptdir=$(dirname "${BASH_SOURCE[0]}")
105+
# shellcheck source=./cache-common-inc.sh
106+
. "${scriptdir}/cache-common-inc.sh"
107+
108+
# Scripts for each distribution are expected to:
109+
# - Add their identifier to the SUPPORTED_DISTRIBUTIONS array.
110+
# - Register the following functions:
111+
# - ${distribution}_cache_key_for_image_kernel
112+
# - Arguments: location, kernel_location
113+
# - Returns: cache_key (string)
114+
# - Exits with an error if the image location is not supported.
115+
# - ${distribution}_image_entry_for_image_kernel
116+
# - Arguments: location, kernel_location
117+
# - Returns: image_entry (JSON object)
118+
# - Exits with an error if the image location is not supported.
119+
declare -a SUPPORTED_DISTRIBUTIONS=()
120+
121+
# shellcheck source=./update-template-ubuntu.sh
122+
. "${scriptdir}/update-template-ubuntu.sh"
123+
else
124+
# this script is sourced
125+
return 0
126+
fi
127+
128+
declare -a templates=()
129+
while [[ $# -gt 0 ]]; do
130+
case "$1" in
131+
-h | --help)
132+
print_help
133+
exit 0
134+
;;
135+
-d | --debug) set -x ;;
136+
*.yaml) templates+=("$1") ;;
137+
*)
138+
error_exit "Unknown argument: $1"
139+
;;
140+
esac
141+
shift
142+
done
143+
144+
if [[ ${#templates[@]} -eq 0 ]]; then
145+
print_help
146+
exit 0
147+
fi
148+
149+
declare -a distributions=()
150+
# Check if the distribution has the required functions
151+
for distribution in "${SUPPORTED_DISTRIBUTIONS[@]}"; do
152+
if declare -f "${distribution}_cache_key_for_image_kernel" >/dev/null &&
153+
declare -f "${distribution}_image_entry_for_image_kernel" >/dev/null; then
154+
distributions+=("${distribution}")
155+
fi
156+
done
157+
[[ ${#distributions[@]} -gt 0 ]] || error_exit "No supported distributions found"
158+
159+
declare -A image_entry_cache=()
160+
161+
for template in "${templates[@]}"; do
162+
echo "Processing ${template}"
163+
# 1. extract location by parsing template using arch
164+
yq_filter="
165+
.images[] | [.location, .kernel.location, .kernel.cmdline] | @tsv
166+
"
167+
parsed=$(yq eval "${yq_filter}" "${template}")
168+
169+
# 3. get the image location
170+
arr=()
171+
while IFS= read -r line; do arr+=("${line}"); done <<<"${parsed}"
172+
locations=("${arr[@]}")
173+
for ((index = 0; index < ${#locations[@]}; index++)); do
174+
[[ ${locations[index]} != "null" ]] || continue
175+
set -e
176+
IFS=$'\t' read -r location kernel_location kernel_cmdline <<<"${locations[index]}"
177+
for distribution in "${distributions[@]}"; do
178+
set +e # Disable 'set -e' to avoid exiting on error for the next assignment.
179+
cache_key=$(
180+
set -e # Enable 'set -e' for the next command.
181+
"${distribution}_cache_key_for_image_kernel" "${location}" "${kernel_location}"
182+
) # Check exit status separately to prevent disabling 'set -e' by using the function call in the condition.
183+
# shellcheck disable=2181
184+
[[ $? -eq 0 ]] || continue
185+
image_entry=$(
186+
set -e # Enable 'set -e' for the next command.
187+
if [[ -v image_entry_cache[${cache_key}] ]]; then
188+
echo "${image_entry_cache[${cache_key}]}"
189+
else
190+
"${distribution}_image_entry_for_image_kernel" "${location}" "${kernel_location}"
191+
fi
192+
) # Check exit status separately to prevent disabling 'set -e' by using the function call in the condition.
193+
# shellcheck disable=2181
194+
[[ $? -eq 0 ]] || continue
195+
set -e
196+
image_entry_cache[${cache_key}]="${image_entry}"
197+
if [[ -n ${image_entry} ]]; then
198+
[[ ${kernel_cmdline} != "null" ]] &&
199+
jq -e 'has("kernel")' <<<"${image_entry}" >/dev/null &&
200+
image_entry=$(jq ".kernel.cmdline = \"${kernel_cmdline}\"" <<<"${image_entry}")
201+
echo "${image_entry}" | jq
202+
limactl edit --log-level error --set "
203+
.images[${index}] = ${image_entry}|
204+
(.images[${index}] | ..) style = \"double\"
205+
" "${template}"
206+
fi
207+
done
208+
done
209+
done

0 commit comments

Comments
 (0)