install_next_test.sh RawHistoryFolder
1#!/bin/sh
2set -eu
3
4ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
5PASS=0
6FAIL=0
7SHA=0123456789abcdef0123456789abcdef01234567
8
9fail() {
10 echo "not ok - $1"
11 FAIL=$((FAIL + 1))
12}
13
14pass() {
15 echo "ok - $1"
16 PASS=$((PASS + 1))
17}
18
19run_case() {
20 shift
21 case_dir=$(mktemp -d)
22 mkdir -p "$case_dir/bin" "$case_dir/home" "$case_dir/install"
23
24 cat >"$case_dir/bin/uname" <<'EOF'
25#!/bin/sh
26[ "$1" = "-s" ] && echo Linux || echo x86_64
27EOF
28 cat >"$case_dir/bin/id" <<'EOF'
29#!/bin/sh
30[ "$1" = "-u" ] && { echo 1000; exit 0; }
31exec /usr/bin/id "$@"
32EOF
33 cat >"$case_dir/bin/curl" <<EOF
34#!/bin/sh
35printf '%s\n' "\$*" >>'$case_dir/curl.log'
36case "\$*" in
37 *api.github.com/repos/bnema/gordon/commits/next*) printf '%s\n' '{"sha":"$SHA"}' ;;
38 *codeload.github.com/bnema/gordon/tar.gz/$SHA*) : >'$case_dir/source.tar.gz' ;;
39 *) exit 90 ;;
40esac
41EOF
42 cat >"$case_dir/bin/tar" <<EOF
43#!/bin/sh
44printf '%s\n' "\$*" >>'$case_dir/tar.log'
45case "\$1" in
46 -tzf) printf '%s\n' 'gordon-$SHA/' 'gordon-$SHA/go.mod' 'gordon-$SHA/main.go' ;;
47 -tvzf) printf '%s\n' 'drwxr-xr-x root/root 0 date gordon-$SHA/' '-rw-r--r-- root/root 0 date gordon-$SHA/go.mod' '-rw-r--r-- root/root 0 date gordon-$SHA/main.go' ;;
48 -xzf)
49 while [ "\$#" -gt 0 ]; do
50 if [ "\$1" = -C ]; then
51 shift
52 mkdir -p "\$1/gordon-$SHA"
53 printf 'module example\n\ngo 1.27\n' >"\$1/gordon-$SHA/go.mod"
54 : >"\$1/gordon-$SHA/main.go"
55 exit 0
56 fi
57 shift
58 done
59 ;;
60esac
61EOF
62 cat >"$case_dir/bin/go" <<EOF
63#!/bin/sh
64printf '%s\n' "GOOS=\${GOOS-} GOARCH=\${GOARCH-} GOTOOLCHAIN=\${GOTOOLCHAIN-} \$*" >>'$case_dir/go.log'
65if [ "\$1 \$2" = 'env GOVERSION' ]; then echo go1.27.1; exit 0; fi
66if [ "\$1" = build ]; then
67 while [ "\$#" -gt 0 ]; do [ "\$1" = -o ] && { shift; : >"\$1"; chmod 755 "\$1"; exit 0; }; shift; done
68fi
69exit 91
70EOF
71 cat >"$case_dir/bin/install" <<EOF
72#!/bin/sh
73printf '%s\n' "\$*" >>'$case_dir/install.log'
74cp "\$3" "\$4"
75EOF
76 cat >"$case_dir/bin/mv" <<EOF
77#!/bin/sh
78printf '%s\n' "\$*" >>'$case_dir/mv.log'
79[ "\$1" = -f ] && shift
80/bin/mv "\$1" "\$2"
81EOF
82 chmod +x "$case_dir/bin/"*
83 mkdir -p "$case_dir/tmp"
84
85 if (PATH="$case_dir/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=0 "$@") >"$case_dir/out" 2>&1; then
86 status=0
87 else
88 status=$?
89 fi
90}
91
92test_next_build() {
93 run_case next-build env GORDON_CHANNEL=next sh "$ROOT/install.sh"
94 [ "$status" -eq 0 ] || { cat "$case_dir/out"; fail "next channel builds pinned source"; return; }
95 if grep -q "commits/next" "$case_dir/curl.log" &&
96 grep -q "codeload.github.com/bnema/gordon/tar.gz/$SHA" "$case_dir/curl.log" &&
97 grep -q -- "-X main.version=next-$SHA" "$case_dir/go.log" &&
98 grep -q "GOOS=linux GOARCH=amd64 GOTOOLCHAIN=local" "$case_dir/go.log" &&
99 grep -q "UNVERIFIED DEVELOPMENT BUILD" "$case_dir/out" &&
100 [ -x "$case_dir/home/.local/bin/gordon" ]; then
101 pass "next channel builds pinned source"
102 else
103 fail "next channel builds pinned source"
104 fi
105}
106
107test_conflicts() {
108 run_case version-conflict env GORDON_CHANNEL=next GORDON_VERSION=v1.2.3 sh "$ROOT/install.sh"
109 if [ "$status" -ne 0 ] && grep -q "GORDON_VERSION cannot be used" "$case_dir/out"; then
110 pass "next rejects GORDON_VERSION"
111 else
112 fail "next rejects GORDON_VERSION"
113 fi
114
115 run_case prerelease-conflict env GORDON_CHANNEL=next GORDON_PRERELEASE=1 sh "$ROOT/install.sh"
116 if [ "$status" -ne 0 ] && grep -q "GORDON_PRERELEASE cannot be used" "$case_dir/out"; then
117 pass "next rejects GORDON_PRERELEASE"
118 else
119 fail "next rejects GORDON_PRERELEASE"
120 fi
121}
122
123test_go_version() {
124 run_case old-go env GORDON_CHANNEL=next MOCK_GO_VERSION=unused sh "$ROOT/install.sh"
125 # Replace the mock after setup, then run once more in the same fixture.
126 cat >"$case_dir/bin/go" <<'EOF'
127#!/bin/sh
128[ "$1 $2" = 'env GOVERSION' ] && { echo go1.26.9; exit 0; }
129exit 91
130EOF
131 chmod +x "$case_dir/bin/go"
132 if (PATH="$case_dir/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=0 GORDON_CHANNEL=next sh "$ROOT/install.sh") >"$case_dir/out" 2>&1; then status=0; else status=$?; fi
133 if [ "$status" -ne 0 ] && grep -q "requires Go 1.27 or newer" "$case_dir/out"; then
134 pass "next enforces go.mod Go version"
135 else
136 fail "next enforces go.mod Go version"
137 fi
138}
139
140test_default_and_path_modes() {
141 run_case default-dir env GORDON_CHANNEL=next sh "$ROOT/install.sh"
142 if [ "$status" -eq 0 ] && [ -x "$case_dir/home/.local/bin/gordon" ] &&
143 grep -q "export PATH='${case_dir}/home/.local/bin':\$PATH" "$case_dir/out"; then
144 pass "default install is user-local and prints current-shell instruction"
145 else
146 fail "default install is user-local and prints current-shell instruction"
147 fi
148
149 run_case path-present env GORDON_CHANNEL=next sh "$ROOT/install.sh"
150 if (PATH="$case_dir/bin:$case_dir/home/.local/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=0 GORDON_CHANNEL=next sh "$ROOT/install.sh") >"$case_dir/out" 2>&1; then status=0; else status=$?; fi
151 if [ "$status" -eq 0 ] && ! grep -q "Add Gordon to the current shell" "$case_dir/out"; then
152 pass "existing PATH needs no update"
153 else
154 fail "existing PATH needs no update"
155 fi
156
157 run_case forced-no env GORDON_CHANNEL=next GORDON_UPDATE_PATH=0 sh "$ROOT/install.sh"
158 if [ "$status" -eq 0 ] && [ ! -e "$case_dir/home/.bashrc" ]; then
159 pass "forced no does not modify shell config"
160 else
161 fail "forced no does not modify shell config"
162 fi
163
164 run_case invalid-update env GORDON_CHANNEL=next GORDON_UPDATE_PATH=maybe sh "$ROOT/install.sh"
165 if [ "$status" -ne 0 ] && grep -q "must be 0 or 1" "$case_dir/out"; then
166 pass "invalid PATH mode is rejected"
167 else
168 fail "invalid PATH mode is rejected"
169 fi
170
171 run_case unsafe-dir env GORDON_CHANNEL=next GORDON_INSTALL_DIR="/tmp/unsafe
172path" sh "$ROOT/install.sh"
173 if [ "$status" -ne 0 ] && grep -q "unsafe control character" "$case_dir/out"; then
174 pass "control characters in install directory are rejected"
175 else
176 fail "control characters in install directory are rejected"
177 fi
178
179 run_case trailing-newline env GORDON_CHANNEL=next GORDON_INSTALL_DIR="/tmp/unsafe
180" sh "$ROOT/install.sh"
181 if [ "$status" -ne 0 ] && grep -q "unsafe control character" "$case_dir/out"; then
182 pass "trailing newline in install directory is rejected"
183 else
184 fail "trailing newline in install directory is rejected"
185 fi
186}
187
188test_shell_updates() {
189 for shell in bash zsh fish; do
190 run_case "shell-$shell" env GORDON_CHANNEL=next GORDON_UPDATE_PATH=1 SHELL="/usr/bin/$shell" sh "$ROOT/install.sh"
191 case "$shell" in
192 bash) config=$case_dir/home/.bashrc; expected="export PATH='${case_dir}/home/.local/bin':\$PATH" ;;
193 zsh) config=$case_dir/home/.zshrc; expected="export PATH='${case_dir}/home/.local/bin':\$PATH" ;;
194 fish) config=$case_dir/home/.config/fish/config.fish; expected="fish_add_path '${case_dir}/home/.local/bin'" ;;
195 esac
196 if [ "$status" -eq 0 ] && grep -Fq "$expected" "$config"; then
197 pass "$shell PATH update"
198 else
199 fail "$shell PATH update"
200 fi
201 done
202
203 run_case idempotent env GORDON_CHANNEL=next GORDON_UPDATE_PATH=1 SHELL=/bin/bash sh "$ROOT/install.sh"
204 first_case=$case_dir
205 if (PATH="$case_dir/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=1 GORDON_CHANNEL=next sh "$ROOT/install.sh") >>"$case_dir/out" 2>&1 &&
206 [ "$(grep -c '^# >>> Gordon installer PATH >>>$' "$case_dir/home/.bashrc")" -eq 1 ]; then
207 pass "PATH marker block is idempotent"
208 else
209 cat "$first_case/out"
210 fail "PATH marker block is idempotent"
211 fi
212
213 run_case unsupported env GORDON_CHANNEL=next GORDON_UPDATE_PATH=1 SHELL=/bin/ksh sh "$ROOT/install.sh"
214 if [ "$status" -eq 0 ] && grep -q "unsupported or missing SHELL" "$case_dir/out"; then
215 pass "unsupported shell gets manual instructions"
216 else
217 fail "unsupported shell gets manual instructions"
218 fi
219}
220
221test_explicit_global_override() {
222 run_case home-bin env GORDON_CHANNEL=next sh "$ROOT/install.sh"
223 if (PATH="$case_dir/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=1 GORDON_INSTALL_DIR="$case_dir/home/bin" GORDON_CHANNEL=next sh "$ROOT/install.sh") >"$case_dir/out" 2>&1; then status=0; else status=$?; fi
224 if [ "$status" -eq 0 ] && [ -x "$case_dir/home/bin/gordon" ] &&
225 grep -Fq "export PATH='${case_dir}/home/bin':\$PATH" "$case_dir/home/.bashrc"; then
226 pass "HOME bin override is installed and added to PATH"
227 else
228 fail "HOME bin override is installed and added to PATH"
229 fi
230
231 run_case global env GORDON_CHANNEL=next sh "$ROOT/install.sh"
232 if (PATH="$case_dir/bin:/usr/bin:/bin" HOME="$case_dir/home" SHELL=/bin/bash TMPDIR="$case_dir/tmp" GORDON_UPDATE_PATH=0 GORDON_INSTALL_DIR="$case_dir/install" GORDON_CHANNEL=next sh "$ROOT/install.sh") >"$case_dir/out" 2>&1; then status=0; else status=$?; fi
233 if [ "$status" -eq 0 ] && [ -x "$case_dir/install/gordon" ]; then
234 pass "explicit global install override is preserved"
235 else
236 fail "explicit global install override is preserved"
237 fi
238
239 run_case sudo-default env SUDO_USER=test-user GORDON_CHANNEL=next sh "$ROOT/install.sh"
240 if [ "$status" -ne 0 ] && grep -q "Refusing a default user-local install" "$case_dir/out" && [ ! -e "$case_dir/home/.local/bin/gordon" ]; then
241 pass "sudo default install is refused"
242 else
243 fail "sudo default install is refused"
244 fi
245}
246
247test_next_build
248test_conflicts
249test_go_version
250test_default_and_path_modes
251test_shell_updates
252test_explicit_global_override
253printf '%s passed, %s failed\n' "$PASS" "$FAIL"
254[ "$FAIL" -eq 0 ]