Skip to content

Commit 2f6aeca

Browse files
committed
go-template: Use cygpath for Windows portability
Cygwin and MSYS2 environments both contain the `cygpath` utility for translating between Unix and native Windows file paths. `cygpath -m` is just what's needed when passing file paths and URLs to native binary programs such as `curl` and `git`. This commit adds the `windows_native_path` helper function, which I'll eventually extract into a `lib/` module at some point. It also adds logic to ensure `GO_SCRIPT_BASH_DOWNLOAD_URL` begins with a protocol, adds a test case to validate this, removes redundant comments and assertions from several `git clone` test cases, and updates the `create_forwarding_script` helper to only generate a script when a program is present on the system.
1 parent d27dc12 commit 2f6aeca

File tree

2 files changed

+94
-55
lines changed

2 files changed

+94
-55
lines changed

go-template

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,23 @@ download_go_script_bash_tarball() {
5353
local unpacked_core="$unpacked_dir/go-core.bash"
5454
local core_dir_parent="${GO_SCRIPT_BASH_CORE_DIR%/*}"
5555
local url="$GO_SCRIPT_BASH_DOWNLOAD_URL"
56+
local protocol="${url%%://*}"
5657
local download_cmd=()
5758

59+
if [[ "$protocol" == "$url" ]]; then
60+
printf 'GO_SCRIPT_BASH_DOWNLOAD_URL has no protocol: %s\n' "$url" >&2
61+
return 1
62+
elif command -v cygpath >/dev/null && [[ "$protocol" == 'file' ]]; then
63+
url="file://$(cygpath -m "${url#file://}")"
64+
fi
65+
5866
if command -v curl >/dev/null; then
5967
download_cmd=(curl -LfsS "$url")
6068
elif command -v fetch >/dev/null; then
6169
download_cmd=(fetch -o - "$url")
62-
elif [[ "$url" =~ file:// ]] && command -v cat; then
70+
elif [[ "$protocol" == 'file' ]] && command -v cat; then
71+
# `wget` can't handle 'file://' urls. Though input redirection would work
72+
# below, this method is consistent with the process substitution logic.
6373
download_cmd=(cat "${url#file://}")
6474
elif command -v wget >/dev/null; then
6575
download_cmd=(wget -O - "$url")

tests/template.bats

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,23 @@ GO_SCRIPT_BASH_DOWNLOAD_URL="$GO_ARCHIVE_URL"
3535
RELEASE_TARBALL="${GO_SCRIPT_BASH_VERSION}.tar.gz"
3636
FULL_DOWNLOAD_URL="$GO_SCRIPT_BASH_DOWNLOAD_URL/$RELEASE_TARBALL"
3737
LOCAL_DOWNLOAD_URL="$TEST_ARCHIVE_URL/$RELEASE_TARBALL"
38+
NATIVE_LOCAL_URL=
39+
EXPECTED_URL=
40+
CLONE_DIR=
3841

3942
setup() {
4043
test_filter
4144
export GO_SCRIPT_BASH_{VERSION,REPO_URL,DOWNLOAD_URL}
45+
NATIVE_LOCAL_URL="$(windows_native_path "$LOCAL_DOWNLOAD_URL")"
46+
CLONE_DIR="$(windows_native_path "$TEST_GO_SCRIPTS_DIR")/go-script-bash"
47+
EXPECTED_URL="$FULL_DOWNLOAD_URL"
48+
49+
if [[ -z "$TEST_USE_REAL_URL" ]]; then
50+
EXPECTED_URL="$NATIVE_LOCAL_URL"
51+
fi
52+
53+
# Ensure `cygpath` is always available if we need it.
54+
create_forwarding_script 'cygpath'
4255

4356
mkdir -p "$TEST_GO_ROOTDIR"
4457
cp "$_GO_CORE_DIR/go-template" "$TEST_GO_ROOTDIR"
@@ -60,6 +73,31 @@ assert_go_core_unpacked() {
6073
restore_bats_shell_options "$result"
6174
}
6275

76+
# Converts a Unix path or 'file://' URL to a Windows native path.
77+
#
78+
# This is useful when passing file paths or URLs to native programs on MSYS2
79+
# or Cygwin, or validating the output of such programs, to ensure portability.
80+
# The resulting path will contain forward slashes.
81+
#
82+
# Prints both converted and unconverted paths and URLs to standard output.
83+
#
84+
# Arguments:
85+
# path: Path or 'file://' URL to convert
86+
windows_native_path() {
87+
local path="$1"
88+
local protocol="${path%%://*}"
89+
90+
if ! command -v cygpath >/dev/null ||
91+
[[ "$protocol" != "$path" && "$protocol" != 'file' ]]; then
92+
printf '%s' "$path"
93+
elif [[ "$protocol" == 'file' ]]; then
94+
printf 'file://'
95+
cygpath -m "${path#file://}"
96+
else
97+
cygpath -m "$path"
98+
fi
99+
}
100+
63101
# This mimics the tarball provided by GitHub.
64102
#
65103
# This could probably become a general-purpose utility one day.
@@ -101,7 +139,8 @@ create_fake_tarball_if_not_using_real_url() {
101139
# installed on the system to test cases when specific programs can't be found,
102140
# while others remain available.
103141
#
104-
# Creates `BATS_TEST_BINDIR` if it doesn't already exist.
142+
# Creates `BATS_TEST_BINDIR` if it doesn't already exist. If the program
143+
# doesn't exist on the system, no forwarding script will be created.
105144
#
106145
# Arguments:
107146
# program_name: Name of the system program to forward
@@ -110,11 +149,13 @@ create_forwarding_script() {
110149
local real_program="$(command -v "$1")"
111150
local script="$BATS_TEST_BINDIR/$1"
112151

113-
if [[ ! -d "$BATS_TEST_BINDIR" ]]; then
114-
mkdir "$BATS_TEST_BINDIR"
152+
if [[ ! -d "$BATS_TEST_BINDIR" ]] && ! mkdir -p "$BATS_TEST_BINDIR"; then
153+
restore_bats_shell_options '1'
154+
return
155+
elif [[ -n "$real_program" ]]; then
156+
printf '%s\n' "#! $BASH" "PATH='$PATH' \"$real_program\" \"\$@\"" >"$script"
157+
chmod 700 "$script"
115158
fi
116-
printf '%s\n' "#! $BASH" "PATH='$PATH' \"$real_program\" \"\$@\"" >"$script"
117-
chmod 700 "$script"
118159
restore_bats_shell_options
119160
}
120161

@@ -158,28 +199,31 @@ run_with_download_program() {
158199
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
159200
}
160201

161-
@test "$SUITE: download $GO_SCRIPT_BASH_VERSION from $GO_SCRIPT_BASH_REPO_URL" {
202+
@test "$SUITE: download $GO_SCRIPT_BASH_VERSION from $FULL_DOWNLOAD_URL" {
162203
create_fake_tarball_if_not_using_real_url
163204
run "$TEST_GO_ROOTDIR/go-template"
164205

165206
# Without a command argument, the script will print the top-level help and
166207
# return an error, but the core repo should exist as expected.
167208
assert_failure
168-
assert_output_matches "Downloading framework from '$FULL_DOWNLOAD_URL'\.\.\."
209+
assert_output_matches "Downloading framework from '$EXPECTED_URL'\.\.\."
210+
assert_output_matches "Download of '$EXPECTED_URL' successful."$'\n\n'
169211
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
170212
assert_go_core_unpacked
171213
}
172214

173215
@test "$SUITE: download locally using curl" {
174216
run_with_download_program 'curl'
175-
assert_output_matches "Downloading framework from '$LOCAL_DOWNLOAD_URL'\.\.\."
217+
assert_output_matches "Downloading framework from '$NATIVE_LOCAL_URL'\.\.\."
218+
assert_output_matches "Download of '$NATIVE_LOCAL_URL' successful."$'\n\n'
176219
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
177220
assert_go_core_unpacked
178221
}
179222

180223
@test "$SUITE: download locally using fetch" {
181224
run_with_download_program 'fetch'
182-
assert_output_matches "Downloading framework from '$LOCAL_DOWNLOAD_URL'\.\.\."
225+
assert_output_matches "Downloading framework from '$NATIVE_LOCAL_URL'\.\.\."
226+
assert_output_matches "Download of '$NATIVE_LOCAL_URL' successful."$'\n\n'
183227
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
184228
assert_go_core_unpacked
185229
}
@@ -188,7 +232,8 @@ run_with_download_program() {
188232
# We'll actually use `cat` with `file://` URLs, since `wget` only supports
189233
# HTTP, HTTPS, and FTP.
190234
run_with_download_program 'cat'
191-
assert_output_matches "Downloading framework from '$LOCAL_DOWNLOAD_URL'\.\.\."
235+
assert_output_matches "Downloading framework from '$NATIVE_LOCAL_URL'\.\.\."
236+
assert_output_matches "Download of '$NATIVE_LOCAL_URL' successful."$'\n\n'
192237
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
193238
assert_go_core_unpacked
194239
}
@@ -197,7 +242,8 @@ run_with_download_program() {
197242
# As mentioned in the above test case, we'll actually use `cat` with `file://`
198243
# URLs, but we're simulating `wget` by pretending `cat` doesn't exist.
199244
run_with_download_program 'wget'
200-
assert_output_matches "Downloading framework from '$LOCAL_DOWNLOAD_URL'\.\.\."
245+
assert_output_matches "Downloading framework from '$NATIVE_LOCAL_URL'\.\.\."
246+
assert_output_matches "Download of '$NATIVE_LOCAL_URL' successful."$'\n\n'
201247
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
202248
assert_go_core_unpacked
203249
}
@@ -211,7 +257,7 @@ run_with_download_program() {
211257
GO_SCRIPT_BASH_CORE_DIR="$core_dir" run "$TEST_GO_ROOTDIR/go-template"
212258

213259
assert_failure
214-
assert_output_matches "Download of '$FULL_DOWNLOAD_URL' successful."
260+
assert_output_matches "Download of '$EXPECTED_URL' successful."
215261
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
216262
assert_go_core_unpacked "$core_dir"
217263
}
@@ -224,13 +270,13 @@ run_with_download_program() {
224270
restore_program_in_path 'mkdir'
225271

226272
assert_failure
227-
assert_output_matches "Download of '$FULL_DOWNLOAD_URL' successful."
273+
assert_output_matches "Download of '$EXPECTED_URL' successful."
228274
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
229275
assert_go_core_unpacked
230276
}
231277

232278
@test "$SUITE: fail to download a nonexistent repo" {
233-
local url='bogus-url-that-does-not-exist'
279+
local url='https://bogus-url-that-does-not-exist'
234280
local repo='bogus-repo-that-does-not-exist'
235281
GO_SCRIPT_BASH_DOWNLOAD_URL="$url" GO_SCRIPT_BASH_REPO_URL="$repo" \
236282
run "$TEST_GO_ROOTDIR/go-template"
@@ -250,22 +296,21 @@ run_with_download_program() {
250296
assert_failure
251297
assert_output_matches 'Using git clone as fallback'
252298
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
253-
assert_output_matches "Cloning into '$TEST_GO_SCRIPTS_DIR/go-script-bash'"
299+
assert_output_matches "Cloning into '$CLONE_DIR'"
254300
assert_output_matches "warning: Could not find remote branch $branch to clone"
255301
assert_output_matches "fatal: Remote branch $branch not found in upstream"
256302
assert_output_matches "Failed to clone '$GO_SCRIPT_BASH_REPO_URL'; aborting."
257303
}
258304

259-
@test "$SUITE: fail to find download program uses git clone" {
260-
create_forwarding_script 'git'
261-
PATH="$BATS_TEST_BINDIR" run "$BASH" "$TEST_GO_ROOTDIR/go-template"
305+
@test "$SUITE: use git clone if GO_SCRIPT_BASH_DOWNLOAD_URL lacks a protocol" {
306+
local url='bogus-url-with-no-protocol'
262307

263-
# Without a command argument, the script will print the top-level help and
264-
# return an error, but the core repo should exist as expected.
265-
assert_output_matches "Failed to find cURL, wget, or fetch"
308+
GO_SCRIPT_BASH_DOWNLOAD_URL="$url" run "$TEST_GO_ROOTDIR/go-template"
309+
310+
assert_output_matches "GO_SCRIPT_BASH_DOWNLOAD_URL has no protocol: $url"
266311
assert_output_matches "Using git clone as fallback"
267312
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
268-
assert_output_matches "Cloning into '$TEST_GO_SCRIPTS_DIR/go-script-bash'"
313+
assert_output_matches "Cloning into '$CLONE_DIR'"
269314
assert_output_matches \
270315
"Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n'
271316
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
@@ -277,26 +322,28 @@ run_with_download_program() {
277322
assert_output_matches "go-script-bash $_GO_CORE_VERSION"
278323
}
279324

325+
@test "$SUITE: fail to find download program uses git clone" {
326+
create_forwarding_script 'git'
327+
PATH="$BATS_TEST_BINDIR" run "$BASH" "$TEST_GO_ROOTDIR/go-template"
328+
329+
assert_output_matches "Failed to find cURL, wget, or fetch"
330+
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
331+
assert_output_matches "Cloning into '$CLONE_DIR'"
332+
assert_output_matches \
333+
"Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n'
334+
}
335+
280336
@test "$SUITE: fail to find tar uses git clone" {
281337
create_forwarding_script 'curl'
282338
create_forwarding_script 'git'
283339
PATH="$BATS_TEST_BINDIR" run "$BASH" "$TEST_GO_ROOTDIR/go-template"
284340

285-
# Without a command argument, the script will print the top-level help and
286-
# return an error, but the core repo should exist as expected.
287341
assert_output_matches "Failed to find tar"
288342
assert_output_matches "Using git clone as fallback"
289343
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
290-
assert_output_matches "Cloning into '$TEST_GO_SCRIPTS_DIR/go-script-bash'"
344+
assert_output_matches "Cloning into '$CLONE_DIR'"
291345
assert_output_matches \
292346
"Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n'
293-
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
294-
assert_go_core_unpacked
295-
296-
cd "$TEST_GO_SCRIPTS_DIR/go-script-bash"
297-
run git log --oneline -n 1
298-
assert_success
299-
assert_output_matches "go-script-bash $_GO_CORE_VERSION"
300347
}
301348

302349
@test "$SUITE: fail to create directory uses git clone" {
@@ -305,26 +352,17 @@ run_with_download_program() {
305352
run "$TEST_GO_ROOTDIR/go-template"
306353
restore_program_in_path mkdir
307354

308-
# Without a command argument, the script will print the top-level help and
309-
# return an error, but the core repo should exist as expected.
310-
assert_output_matches "Downloading framework from '$FULL_DOWNLOAD_URL'\.\.\."
355+
assert_output_matches "Downloading framework from '$EXPECTED_URL'\.\.\."
311356

312357
# Note that the go-template defines `GO_SCRIPTS_DIR`, but the framework's own
313358
# `go` script doesn't. Hence, we use `TEST_GO_SCRIPTS_RELATIVE_DIR` below,
314359
# which should always match the default `GO_SCRIPTS_DIR` in the template.
315360
assert_output_matches "Failed to create scripts dir '$TEST_GO_SCRIPTS_DIR'"
316361
assert_output_matches "Using git clone as fallback"
317362
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
318-
assert_output_matches "Cloning into '$TEST_GO_SCRIPTS_DIR/go-script-bash'."
363+
assert_output_matches "Cloning into '$CLONE_DIR'"
319364
assert_output_matches \
320365
"Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n'
321-
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
322-
assert_go_core_unpacked
323-
324-
cd "$TEST_GO_SCRIPTS_DIR/go-script-bash"
325-
run git log --oneline -n 1
326-
assert_success
327-
assert_output_matches "go-script-bash $_GO_CORE_VERSION"
328366
}
329367

330368
@test "$SUITE: fail to move extracted directory uses git clone" {
@@ -335,20 +373,11 @@ run_with_download_program() {
335373
run "$TEST_GO_ROOTDIR/go-template"
336374
restore_program_in_path mv
337375

338-
# Without a command argument, the script will print the top-level help and
339-
# return an error, but the core repo should exist as expected.
340-
assert_output_matches "Downloading framework from '$FULL_DOWNLOAD_URL'\.\.\."
376+
assert_output_matches "Downloading framework from '$EXPECTED_URL'\.\.\."
341377
assert_output_matches "Failed to install downloaded directory in '$target'"
342378
assert_output_matches "Using git clone as fallback"
343379
assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'"
344-
assert_output_matches "Cloning into '$TEST_GO_SCRIPTS_DIR/go-script-bash'"
380+
assert_output_matches "Cloning into '$CLONE_DIR'"
345381
assert_output_matches \
346382
"Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n'
347-
assert_output_matches "Usage: $TEST_GO_ROOTDIR/go-template <command>"
348-
assert_go_core_unpacked
349-
350-
cd "$TEST_GO_SCRIPTS_DIR/go-script-bash"
351-
run git log --oneline -n 1
352-
assert_success
353-
assert_output_matches "go-script-bash $_GO_CORE_VERSION"
354383
}

0 commit comments

Comments
 (0)