Skip to content

Commit dcfbfc4

Browse files
authored
Install system dependencies in swiflty-install.sh (#54)
1 parent 81d7607 commit dcfbfc4

13 files changed

+374
-55
lines changed

docker/install-test.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ ENV LANG en_US.UTF-8
88
ENV LANGUAGE en_US.UTF-8
99

1010
# dependencies
11-
RUN apt-get update && apt-get install -y curl
11+
RUN apt-get update --fix-missing && apt-get install -y curl
1212
RUN echo 'export PATH="$HOME/.local/bin:$PATH"' >> $HOME/.profile

docker/test.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ ENV LANG en_US.UTF-8
1313
ENV LANGUAGE en_US.UTF-8
1414

1515
# dependencies
16-
RUN apt-get update && apt-get install -y curl build-essential
16+
RUN apt-get update --fix-missing && apt-get install -y curl build-essential
1717
COPY ./scripts/install-libarchive.sh /
1818
RUN /install-libarchive.sh
1919

install/swiftly-install.sh

Lines changed: 142 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
# Unless the --disable-confirmation flag is set, this script will allow the runner to
2323
# configure either of those two directory paths.
2424
#
25+
# Unless the --no-install-system-deps flag is set, this script will attempt to install Swift's
26+
# system dependencies using the system package manager.
27+
#
2528
# curl is required to run this script.
2629

2730
set -o errexit
@@ -48,6 +51,47 @@ read_input_with_default () {
4851
fi
4952
}
5053

54+
yn_prompt () {
55+
if [[ "$1" == "true" ]]; then
56+
echo "(Y/n)"
57+
else
58+
echo "(y/N)"
59+
fi
60+
}
61+
62+
# Read a y/n input.
63+
# First argument is the default value (must be "true" or "false").
64+
#
65+
# Sets READ_INPUT_RETURN to "true" for an input of "y" or "Y", "false" for an input
66+
# of "n" or "N", or the default value for a blank input
67+
#
68+
# For all other inputs, a message is printed and the user is prompted again.
69+
read_yn_input () {
70+
while [[ true ]]; do
71+
read_input_with_default "$1"
72+
73+
case "$READ_INPUT_RETURN" in
74+
"y" | "Y")
75+
READ_INPUT_RETURN="true"
76+
return
77+
;;
78+
79+
"n" | "N")
80+
READ_INPUT_RETURN="false"
81+
return
82+
;;
83+
84+
"$1")
85+
return
86+
;;
87+
88+
*)
89+
echo "Please input either \"y\" or \"n\", or press ENTER to use the default."
90+
;;
91+
esac
92+
done
93+
}
94+
5195
# Replaces the actual path to $HOME at the beginning of the provided string argument with
5296
# the string "$HOME". This is used when printing to stdout.
5397
# e.g. "home/user/.local/bin" => "$HOME/.local/bin"
@@ -71,9 +115,68 @@ bold () {
71115
echo "$(tput bold)$1$(tput sgr0)"
72116
}
73117

118+
# Fetch the list of required system dependencies from the apple/swift-docker
119+
# repository and attempt to install them using the system's package manager.
120+
#
121+
# $docker_platform_name, $docker_platform_version, and $package manager need
122+
# to be set before calling this function.
123+
install_system_deps () {
124+
if [[ "$(id --user)" != "0" ]] && ! has_command sudo ; then
125+
echo "Warning: sudo not installed and current user is not root, skipping system dependency installation."
126+
return
127+
elif ! has_command "$package_manager" ; then
128+
echo "Warning: package manager \"$package_manager\" not found, skipping system dependency installation."
129+
return
130+
fi
131+
132+
dockerfile_url="https://raw.githubusercontent.com/apple/swift-docker/main/nightly-main/$docker_platform_name/$docker_platform_version/Dockerfile"
133+
dockerfile="$(curl --silent --retry 3 --location --fail $dockerfile_url)"
134+
if [[ "$?" -ne 0 ]]; then
135+
echo "Error enumerating system dependencies, skipping installation of system dependencies."
136+
fi
137+
138+
# Find the line number of the RUN command associated with installing system dependencies.
139+
beg_line_num=$(printf "$dockerfile" | grep -n --max-count=1 "$package_manager.*install" | cut -d ":" -f1)
140+
141+
# Starting from there, find the first line that starts with an & or doesn't end in a backslash.
142+
relative_end_line_num=$(printf "$dockerfile" |
143+
tail --lines=+"$((beg_line_num + 1))" |
144+
grep -n --max-count=1 --invert-match '[[:space:]]*[^&].*\\$' | cut -d ":" -f1)
145+
end_line_num=$((beg_line_num + relative_end_line_num))
146+
147+
# Read the lines between those two, deleting any spaces and backslashes.
148+
readarray -t package_list < <(printf "$dockerfile" | sed -n "$((beg_line_num + 1)),${end_line_num}p" | sed -r 's/[\ ]//g')
149+
150+
# If the installation command from the Dockerfile included some cleanup as part of a second command, drop that.
151+
if [[ "${package_list[-1]}" =~ ^\&\& ]]; then
152+
unset 'package_list[-1]'
153+
fi
154+
155+
install_args=(--quiet)
156+
if [[ "$DISABLE_CONFIRMATION" == "true" ]]; then
157+
install_args+=(-y)
158+
fi
159+
160+
# Disable errexit since failing to install system dependencies is not swiftly installation-fatal.
161+
set +o errexit
162+
if [[ "$(id --user)" == "0" ]]; then
163+
"$package_manager" install "${install_args[@]}" "${package_list[@]}"
164+
else
165+
sudo "$package_manager" install "${install_args[@]}" "${package_list[@]}"
166+
fi
167+
if [[ "$?" -ne 0 ]]; then
168+
echo "System dependency installation failed."
169+
if [[ "$package_manager" == "apt-get" ]]; then
170+
echo "You may need to run apt-get update before installing system dependencies."
171+
fi
172+
fi
173+
set -o errexit
174+
}
175+
74176
SWIFTLY_INSTALL_VERSION="0.1.0"
75177

76178
MODIFY_PROFILE="true"
179+
SWIFTLY_INSTALL_SYSTEM_DEPS="true"
77180

78181
for arg in "$@"; do
79182
case "$arg" in
@@ -89,6 +192,7 @@ FLAGS:
89192
-y, --disable-confirmation Disable confirmation prompt.
90193
--no-modify-profile Do not attempt to modify the profile file to set environment
91194
variables (e.g. PATH) on login.
195+
--no-install-system-deps Do not attempt to install Swift's required system dependencies.
92196
-h, --help Prints help information.
93197
--version Prints version information.
94198
EOF
@@ -103,6 +207,10 @@ EOF
103207
MODIFY_PROFILE="false"
104208
;;
105209

210+
"--no-install-system-deps")
211+
SWIFTLY_INSTALL_SYSTEM_DEPS="false"
212+
;;
213+
106214
"--version")
107215
echo "$SWIFTLY_INSTALL_VERSION"
108216
exit 0
@@ -134,23 +242,31 @@ case "$ID" in
134242
fi
135243
PLATFORM_NAME="amazonlinux2"
136244
PLATFORM_NAME_FULL="amazonlinux2"
245+
docker_platform_name="amazonlinux"
246+
docker_platform_version="2"
247+
package_manager="yum"
137248
;;
138249

139250
"ubuntu")
251+
docker_platform_name="ubuntu"
252+
package_manager="apt-get"
140253
case "$UBUNTU_CODENAME" in
141254
"jammy")
142255
PLATFORM_NAME="ubuntu2204"
143256
PLATFORM_NAME_FULL="ubuntu22.04"
257+
docker_platform_version="22.04"
144258
;;
145259

146260
"focal")
147261
PLATFORM_NAME="ubuntu2004"
148262
PLATFORM_NAME_FULL="ubuntu20.04"
263+
docker_platform_version="20.04"
149264
;;
150265

151266
"bionic")
152267
PLATFORM_NAME="ubuntu1804"
153268
PLATFORM_NAME_FULL="ubuntu18.04"
269+
docker_platform_version="18.04"
154270
;;
155271

156272
*)
@@ -167,6 +283,9 @@ case "$ID" in
167283
fi
168284
PLATFORM_NAME="ubi9"
169285
PLATFORM_NAME_FULL="ubi9"
286+
docker_platform_name="rhel-ubi"
287+
docker_platform_version="9"
288+
package_manager="yum"
170289
;;
171290

172291
*)
@@ -243,6 +362,7 @@ while [ -z "$DISABLE_CONFIRMATION" ]; do
243362
printf " %40s: $(bold $(replace_home_path $HOME_DIR))\n" "Data and configuration files directory"
244363
printf " %40s: $(bold $(replace_home_path $BIN_DIR))\n" "Executables installation directory"
245364
printf " %40s: $(bold $MODIFY_PROFILE)\n" "Modify login config ($(replace_home_path $PROFILE_FILE))"
365+
printf " %40s: $(bold $SWIFTLY_INSTALL_SYSTEM_DEPS)\n" "Install system dependencies"
246366
echo ""
247367
echo "Select one of the following:"
248368
echo "1) Proceed with the installation (default)"
@@ -265,30 +385,17 @@ while [ -z "$DISABLE_CONFIRMATION" ]; do
265385
read_input_with_default "$BIN_DIR"
266386
BIN_DIR="$(expand_home_path $READ_INPUT_RETURN)"
267387

268-
if [[ "$MODIFY_PROFILE" == "true" ]]; then
269-
MODIFY_PROFILE_PROMPT="(Y/n)"
270-
else
271-
MODIFY_PROFILE_PROMPT="(y/N)"
272-
fi
273-
echo "Modify login config ($(replace_home_path $PROFILE_FILE))? $MODIFY_PROFILE_PROMPT"
274-
read_input_with_default "$MODIFY_PROFILE"
275-
276-
case "$READ_INPUT_RETURN" in
277-
"y" | "Y")
278-
MODIFY_PROFILE="true"
279-
;;
280-
281-
"n" | "N")
282-
MODIFY_PROFILE="false"
283-
;;
388+
echo "Modify login config ($(replace_home_path $PROFILE_FILE))? $(yn_prompt $MODIFY_PROFILE)"
389+
read_yn_input "$MODIFY_PROFILE"
390+
MODIFY_PROFILE="$READ_INPUT_RETURN"
284391

285-
*)
286-
;;
287-
esac
392+
echo "Install system dependencies? $(yn_prompt $SWIFTLY_INSTALL_SYSTEM_DEPS)"
393+
read_yn_input "$SWIFTLY_INSTALL_SYSTEM_DEPS"
394+
SWIFTLY_INSTALL_SYSTEM_DEPS="$READ_INPUT_RETURN"
288395
;;
289396

290397
*)
291-
echo "Cancelling installation"
398+
echo "Cancelling installation."
292399
exit 0
293400
;;
294401
esac
@@ -300,23 +407,11 @@ if [[ -d "$HOME_DIR" ]]; then
300407
else
301408
echo "Existing swiftly installation detected at $(replace_home_path $HOME_DIR), overwrite? (Y/n)"
302409

303-
while [[ true ]]; do
304-
read_input_with_default "y"
305-
case "$READ_INPUT_RETURN" in
306-
"y" | "Y")
307-
break
308-
;;
309-
310-
"n" | "N" | "q")
311-
echo "Cancelling installation"
312-
exit 0
313-
;;
314-
315-
*)
316-
echo "Please input \"y\" or \"n\"."
317-
;;
318-
esac
319-
done
410+
read_yn_input "true"
411+
if [[ "$READ_INPUT_RETURN" == "false" ]]; then
412+
echo "Cancelling installation."
413+
exit 0
414+
fi
320415
fi
321416

322417
rm -r $HOME_DIR
@@ -343,10 +438,6 @@ echo "$JSON_OUT" > "$HOME_DIR/config.json"
343438
# Verify the downloaded executable works. The script will exit if this fails due to errexit.
344439
SWIFTLY_HOME_DIR="$HOME_DIR" SWIFTLY_BIN_DIR="$BIN_DIR" "$BIN_DIR/swiftly" --version > /dev/null
345440

346-
echo ""
347-
echo "swiftly has been succesfully installed!"
348-
echo ""
349-
350441
ENV_OUT=$(cat <<EOF
351442
export SWIFTLY_HOME_DIR="$(replace_home_path $HOME_DIR)"
352443
export SWIFTLY_BIN_DIR="$(replace_home_path $BIN_DIR)"
@@ -367,6 +458,16 @@ if [[ "$MODIFY_PROFILE" == "true" ]]; then
367458
fi
368459
fi
369460

461+
if [[ "$SWIFTLY_INSTALL_SYSTEM_DEPS" != "false" ]]; then
462+
echo ""
463+
echo "Installing Swift's system dependencies via $package_manager (note: this may require root access)..."
464+
install_system_deps
465+
fi
466+
467+
echo ""
468+
echo "swiftly has been succesfully installed!"
469+
echo ""
470+
370471
if ! has_command "swiftly" || [[ "$HOME_DIR" != "$DEFAULT_HOME_DIR" || "$BIN_DIR" != "$DEFAULT_BIN_DIR" ]] ; then
371472
echo "Once you log in again, swiftly should be accessible from your PATH."
372473
echo "To begin using swiftly from your current shell, first run the following command:"

install/test-util.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,52 @@ test_fail () {
2323
test_pass () {
2424
exit 0
2525
}
26+
27+
get_os () {
28+
if [[ -f "/etc/os-release" ]]; then
29+
OS_RELEASE="/etc/os-release"
30+
elif [[ -f "/usr/lib/os-release" ]]; then
31+
OS_RELEASE="/usr/lib/os-release"
32+
else
33+
echo "Error: could not detect OS information"
34+
exit 1
35+
fi
36+
37+
source "$OS_RELEASE"
38+
39+
case "$ID" in
40+
"amzn")
41+
echo "amazonlinux2"
42+
;;
43+
44+
"ubuntu")
45+
case "$UBUNTU_CODENAME" in
46+
"jammy")
47+
echo "ubuntu2204"
48+
;;
49+
50+
"focal")
51+
echo "ubuntu2004"
52+
;;
53+
54+
"bionic")
55+
echo "ubuntu1804"
56+
;;
57+
58+
*)
59+
echo "Unsupported Ubuntu version: $PRETTY_NAME"
60+
exit 1
61+
;;
62+
esac
63+
;;
64+
65+
"rhel")
66+
echo "rhel-ubi9"
67+
;;
68+
69+
*)
70+
echo "Unsupported platform: $PRETTY_NAME"
71+
exit 1
72+
;;
73+
esac
74+
}

install/tests/custom-home-install.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ cleanup () {
1818
}
1919
trap cleanup EXIT
2020

21-
# Make sure that the "~" character is handled properly.
22-
printf "2\n\$HOME/${CUSTOM_HOME_DIR_NAME}\n\$HOME/${CUSTOM_HOME_DIR_NAME}/bin\ny\n1\n" | ./swiftly-install.sh
21+
printf "2\n\$HOME/${CUSTOM_HOME_DIR_NAME}\n\$HOME/${CUSTOM_HOME_DIR_NAME}/bin\ny\nn\n1\n" | ./swiftly-install.sh
2322

2423
# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR.
2524
bash --login -c "swiftly --version"

install/tests/custom-install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ cleanup () {
2424
}
2525
trap cleanup EXIT
2626

27-
printf "2\n$CUSTOM_HOME_DIR\n$CUSTOM_BIN_DIR\ny\n1\n" | ./swiftly-install.sh
27+
printf "2\n$CUSTOM_HOME_DIR\n$CUSTOM_BIN_DIR\ny\nn\n1\n" | ./swiftly-install.sh
2828

2929
# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR.
3030
bash --login -c "swiftly --version"

install/tests/custom-tilde-install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ cleanup () {
1919
trap cleanup EXIT
2020

2121
# Make sure that the "~" character is handled properly.
22-
printf "2\n~/${CUSTOM_HOME_DIR_NAME}\n~/${CUSTOM_HOME_DIR_NAME}/bin\ny\n1\n" | ./swiftly-install.sh
22+
printf "2\n~/${CUSTOM_HOME_DIR_NAME}\n~/${CUSTOM_HOME_DIR_NAME}/bin\ny\nn\n1\n" | ./swiftly-install.sh
2323

2424
# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR.
2525
bash --login -c "swiftly --version"

0 commit comments

Comments
 (0)