Skip to content

Commit 7383d93

Browse files
authored
Merge pull request #4 from dstmodders/victorpopkov/feature-1
Add version bumping script for packages
2 parents 97ef789 + 8da0b49 commit 7383d93

File tree

1 file changed

+321
-0
lines changed

1 file changed

+321
-0
lines changed

bin/bump-packages.sh

+321
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
#!/bin/bash
2+
3+
# define constants
4+
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
DOCKER_ALPINE_IMAGE='alpine:3.19.1'
6+
DOCKER_DEBIAN_IMAGE='debian:bookworm-slim'
7+
EXCLUDED_ALPINE_PACKAGES=()
8+
EXCLUDED_DEBIAN_PACKAGES=('wget')
9+
PROGRAM="$(basename "$0")"
10+
11+
readonly BASE_DIR
12+
readonly DOCKER_ALPINE_IMAGE
13+
readonly DOCKER_DEBIAN_IMAGE
14+
readonly EXCLUDED_ALPINE_PACKAGES
15+
readonly EXCLUDED_DEBIAN_PACKAGES
16+
readonly PROGRAM
17+
18+
# define flags
19+
FLAG_COMMIT=0
20+
FLAG_DRY_RUN=0
21+
22+
usage() {
23+
cat <<EOF
24+
Bump package in Dockerfiles.
25+
26+
Usage:
27+
$PROGRAM [flags]
28+
29+
Flags:
30+
-c, --commit commit changes
31+
-d, --dry-run only check and don't apply or commit any changes
32+
-h, --help help for $PROGRAM
33+
EOF
34+
}
35+
36+
print_bold() {
37+
local value="$1"
38+
local output="${3:-1}"
39+
40+
if [ "$DISABLE_COLORS" = '1' ] || ! [ -t 1 ]; then
41+
printf '%s' "$value" >&"$output"
42+
else
43+
printf "$(tput bold)%s$(tput sgr0)" "$value" >&"$output"
44+
fi
45+
}
46+
47+
print_bold_color() {
48+
local color="$1"
49+
local value="$2"
50+
local output="${3:-1}"
51+
52+
if [ "$DISABLE_COLORS" = '1' ] || ! [ -t 1 ]; then
53+
printf '%s' "$value" >&"$output"
54+
else
55+
printf "$(tput bold)$(tput setaf "$color")%s$(tput sgr0)" "$value" >&"$output"
56+
fi
57+
}
58+
59+
print_error() {
60+
print_bold_color 1 "error: $1" 2
61+
echo '' >&2
62+
}
63+
64+
print_title() {
65+
print_bold "[$1]"
66+
printf '\n\n'
67+
}
68+
69+
get_packages_from_dockerfile() {
70+
local dockerfile="$1"
71+
sed -n \
72+
-e '/apk add --no-cache/,/&&/p' \
73+
-e '/apk add --no-cache --virtual/,/&&/p' \
74+
-e '/apt-get install -y --no-install-recommends/,/&&/p' \
75+
"$dockerfile" \
76+
| sed -E ':a;N;$!ba;s/\\\n/ /g' \
77+
| grep -oE '([a-zA-Z0-9+]+(-[a-zA-Z0-9+]+)*=[^[:space:]]+)' \
78+
| sed "s/'//g" \
79+
| sort \
80+
| uniq
81+
}
82+
83+
get_latest_apk_package_version() {
84+
local name="$1"
85+
86+
local escaped_name
87+
# shellcheck disable=SC2001
88+
escaped_name="$(echo "$name" | sed "s/[.[\*^$(){}+?|]/\\\\&/g")"
89+
90+
local version
91+
version="$(docker run --rm -u root "$DOCKER_ALPINE_IMAGE" /bin/sh -c "
92+
apk update &>/dev/null \
93+
&& apk info '$name' \
94+
| grep '^$name.*description' \
95+
| sed -E 's/^$escaped_name-(.*) description:/\1/' \
96+
| head -1
97+
" 2>&1)"
98+
99+
if [ -z "$version" ]; then
100+
echo ''
101+
else
102+
echo "$version"
103+
fi
104+
}
105+
106+
get_latest_apt_package_version() {
107+
local package_name="$1"
108+
109+
local version
110+
version="$(docker run --rm -u root "$DOCKER_DEBIAN_IMAGE" /bin/bash -c "
111+
apt-get update &>/dev/null \
112+
&& apt-cache show '$package_name' \
113+
| grep '^Version:' \
114+
| awk '{print \$2}' \
115+
| sort -V \
116+
| tail -n 1
117+
" 2>&1)"
118+
119+
if [ -z "$version" ] || [ "$version" = 'E: No packages found' ]; then
120+
echo ''
121+
else
122+
echo "$version"
123+
fi
124+
}
125+
126+
replace_package_in_dockerfile() {
127+
local escaped_package_name
128+
local escaped_current_version
129+
local escaped_new_version
130+
131+
local dockerfile="$1"
132+
local package_name="$2"
133+
local current_version="$3"
134+
local new_version="$4"
135+
136+
escape_for_sed() {
137+
printf '%s\n' "$1" | sed -e 's/[\/&]/\\&/g'
138+
}
139+
140+
escaped_package_name="$(escape_for_sed "$package_name")"
141+
escaped_current_version="$(escape_for_sed "$current_version")"
142+
escaped_new_version="$(escape_for_sed "$new_version")"
143+
144+
sed -i "s/${escaped_package_name}='${escaped_current_version}'/${escaped_package_name}='${escaped_new_version}'/g" "$dockerfile"
145+
}
146+
147+
update_package_in_dockerfile() {
148+
local dockerfile="$1"
149+
local package_name="$2"
150+
local current_version="$3"
151+
local latest_version="$4"
152+
153+
if [ -z "$latest_version" ]; then
154+
print_error "couldn't find the latest version for $package_name"
155+
exit 1
156+
fi
157+
158+
if [ "$current_version" != "$latest_version" ]; then
159+
printf '%s %s => %s ' "$package_name" "$current_version" "$latest_version"
160+
print_bold_color 3 'outdated'
161+
else
162+
printf '%s %s ' "$package_name" "$current_version"
163+
print_bold_color 2 'up-to-date'
164+
fi
165+
printf '\n'
166+
167+
if [ "$FLAG_DRY_RUN" -eq 1 ]; then
168+
return 0
169+
fi
170+
171+
replace_package_in_dockerfile "$dockerfile" "$package_name" "$current_version" "$latest_version"
172+
}
173+
174+
commit_changes() {
175+
local dockerfile="$1"
176+
local commit_message_first_line="$2"
177+
local commit_message="$3"
178+
179+
if [ "$FLAG_DRY_RUN" -eq 0 ] && [ "$FLAG_COMMIT" -eq 1 ]; then
180+
printf 'Committing...'
181+
git add "$dockerfile"
182+
183+
if [ -n "$(git diff --cached --name-only)" ]; then
184+
printf '\n'
185+
echo '---'
186+
git commit -m "$commit_message_first_line" -m "$commit_message"
187+
else
188+
printf ' Skipped\n'
189+
fi
190+
fi
191+
}
192+
193+
is_excluded_alpine_package() {
194+
local package_name="$1"
195+
for excluded_package in "${EXCLUDED_ALPINE_PACKAGES[@]}"; do
196+
if [ "$excluded_package" == "$package_name" ]; then
197+
return 0
198+
fi
199+
done
200+
return 1
201+
}
202+
203+
is_excluded_debian_package() {
204+
local package_name="$1"
205+
for excluded_package in "${EXCLUDED_DEBIAN_PACKAGES[@]}"; do
206+
if [ "$excluded_package" == "$package_name" ]; then
207+
return 0
208+
fi
209+
done
210+
return 1
211+
}
212+
213+
update_alpine_dockerfile() {
214+
local commit_list=()
215+
local dockerfile="$1"
216+
local commit_message_first_line="$2"
217+
218+
while IFS= read -r line; do
219+
package_name="$(echo "$line" | cut -d '=' -f 1)"
220+
221+
if is_excluded_alpine_package "$package_name"; then
222+
continue
223+
fi
224+
225+
current_version="$(echo "$line" | cut -d '=' -f 2)"
226+
latest_version="$(get_latest_apk_package_version "$package_name")"
227+
update_package_in_dockerfile "$dockerfile" "$package_name" "$current_version" "$latest_version"
228+
229+
if [ "$FLAG_DRY_RUN" -eq 0 ] && [ "$FLAG_COMMIT" -eq 1 ] && [ "$current_version" != "$latest_version" ]; then
230+
commit_list+=("- Bump $package_name from $current_version to $latest_version")
231+
fi
232+
done <<< "$(get_packages_from_dockerfile "$dockerfile")"
233+
234+
if [ "$FLAG_DRY_RUN" -eq 0 ] && [ "$FLAG_COMMIT" -eq 1 ] && [ "${#commit_list[@]}" -gt 0 ]; then
235+
mapfile -t sorted_commit_list < <(printf "%s\n" "${commit_list[@]}" | sort)
236+
commit_message="$(printf "%s\n" "${sorted_commit_list[@]}")"
237+
echo '---'
238+
commit_changes "$dockerfile" "$commit_message_first_line" "$commit_message"
239+
fi
240+
}
241+
242+
update_debian_dockerfile() {
243+
local commit_list=()
244+
local dockerfile="$1"
245+
local commit_message_first_line="$2"
246+
247+
while IFS= read -r line; do
248+
package_name="$(echo "$line" | cut -d '=' -f 1)"
249+
250+
if is_excluded_debian_package "$package_name"; then
251+
continue
252+
fi
253+
254+
current_version="$(echo "$line" | cut -d '=' -f 2)"
255+
latest_version="$(get_latest_apt_package_version "$package_name")"
256+
update_package_in_dockerfile "$dockerfile" "$package_name" "$current_version" "$latest_version"
257+
258+
if [ "$FLAG_DRY_RUN" -eq 0 ] && [ "$FLAG_COMMIT" -eq 1 ] && [ "$current_version" != "$latest_version" ]; then
259+
commit_list+=("- Bump $package_name from $current_version to $latest_version")
260+
fi
261+
done <<< "$(get_packages_from_dockerfile "$dockerfile")"
262+
263+
if [ "$FLAG_DRY_RUN" -eq 0 ] && [ "$FLAG_COMMIT" -eq 1 ] && [ "${#commit_list[@]}" -gt 0 ]; then
264+
mapfile -t sorted_commit_list < <(printf "%s\n" "${commit_list[@]}" | sort)
265+
commit_message="$(printf "%s\n" "${sorted_commit_list[@]}")"
266+
echo '---'
267+
commit_changes "$dockerfile" "$commit_message_first_line" "$commit_message"
268+
fi
269+
}
270+
271+
cd "$BASE_DIR/.." || exit 1
272+
273+
while [ $# -gt 0 ]; do
274+
key="$1"
275+
case "$key" in
276+
-c|--commit)
277+
FLAG_COMMIT=1
278+
;;
279+
-d|--dry-run)
280+
FLAG_DRY_RUN=1
281+
;;
282+
-h|--help)
283+
usage
284+
exit 0
285+
;;
286+
-*)
287+
print_error 'unrecognized flag'
288+
exit 1
289+
;;
290+
*)
291+
;;
292+
esac
293+
shift 1
294+
done
295+
296+
# define extra constants
297+
readonly FLAG_COMMIT
298+
readonly FLAG_DRY_RUN
299+
300+
print_title 'DOCKER'
301+
echo 'Pulling Docker images...'
302+
echo '---'
303+
docker pull "$DOCKER_ALPINE_IMAGE"
304+
echo '---'
305+
docker pull "$DOCKER_DEBIAN_IMAGE"
306+
printf '\n'
307+
308+
print_title 'LATEST ALPINE PACKAGES'
309+
update_alpine_dockerfile './latest/alpine/Dockerfile' 'Bump packages in latest alpine image'
310+
printf '\n'
311+
312+
print_title 'LATEST DEBIAN PACKAGES'
313+
update_debian_dockerfile './latest/debian/Dockerfile' 'Bump packages in latest debian image'
314+
printf '\n'
315+
316+
print_title 'OFFICIAL ALPINE PACKAGES'
317+
update_alpine_dockerfile './official/alpine/Dockerfile' 'Bump packages in official alpine image'
318+
printf '\n'
319+
320+
print_title 'OFFICIAL DEBIAN PACKAGES'
321+
update_debian_dockerfile './official/debian/Dockerfile' 'Bump packages in official debian image'

0 commit comments

Comments
 (0)