Issue
Consider, I have a shell script abc.sh
execute_curl() {
curl_command="curl"
for arg in "$@"; do
curl_command=$(printf "%s %s" "$curl_command" "$arg")
done
eval "$curl_command"
}
URL='https://some.github.com/api/v3/repos/gimlet-dev-repos/repository-name/contents/file.name?ref=branch-name’
execute_curl -s -H “Authorization: token ${TOKEN}” -H "Accept: application/vnd.github.v3+json" -L ”${URL}"
And test.bats for the same
@test "test-run-command perform" {
export PATH="${RESOURCES_DIR}/mocks/:${PATH}"
TEMP_DIR=$(mktemp -d)
debug "TEMP_DIR: ${TEMP_DIR}"
run abc -t TOKEN -d 'some.github.com' -o 'myorg' -r 'myrepo' -g "${RESOURCES_DIR}/mock-global" -b branch -a "${TEMP_DIR}"
assert_success
}
and in mocks directory, I have curl file to test the curl: for example:
#!/bin/sh
string_matches_glob() {
eval "
case \$1 in
$2) return 0 ;;
*) return 1 ;;
esac
"
}
while getopts "H:L:o:w:" OPT; do
case "${OPT}" in
o) OUTPUT_FILE="${OPTARG}" ;;
L) CURL_URL="${OPTARG}" ;;
?) ;;
esac
done
if string_matches_glob "${CURL_URL}" ‘*/contents/file.name*’; then
echo '
Content…
' >"${OUTPUT_FILE}"
printf '%s' '200'
else
exit 97
fi
Issue is if I don’t use execute_curl and use directly the curl command with argument, the tests pass but when I use execute_curl function test fails with 97.
more explanation of curl file above:
string_matches_glob evaluates whether the first argument matches the glob pattern specified in the second argument. The script then uses the getopts command to parse command-line options and arguments passed to the script.
There are three options that can be passed to the script:
-o: specifies the output file for the script to write to -L: specifies the URL to be passed to string_matches_glob
If the URL does not match any of the specified patterns, the script exits with an error code of 97.
My thinking is this line is not able to match the curl_url: if string_matches_glob "${CURL_URL}" ‘/contents/file.name’
Can you please help with changes in curl file so the test passes.
Solution
When I call bash abc.sh
, the call seems to work.
However, when debugging (using TOKEN=foo bash -x abc.sh
) reveals that the command that is executed is:
curl -s -H Authorization: token foo -H Accept: application/vnd.github.v3+json -L 'https://some.github.com/api/v3/repos/gimlet-dev-repos/repository-name/contents/file.name?ref=branch-name'
So one thing that immediately stands out is that the way the curl_command
is built and executed does not properly handle whitespace.
curl
will see -H Authorization:
and -H Accept:
but ignore the actual values of the provided headers.
There is page on the awesome BashFAQ dedicated to the pitfalls of how to properly do this: http://mywiki.wooledge.org/BashFAQ/050
Using a proper solution also removes the need for eval
:
curl_command=()
for arg in "$@"; do
curl_command+=("$arg")
done
curl "${curl_command[@]}"
This will create the expected curl
command, with proper whitespacing:
curl -s -H 'Authorization: token foo' -H 'Accept: application/vnd.github.v3+json' -L 'https://some.github.com/api/v3/repos/gimlet-dev-repos/repository-name/contents/file.name?ref=branch-name'
Not sure if this will resolve your issue but at least the call to curl
will be correct.
What stands out next, is that the call in the test does not match a curl
call.
As in, this call:
run abc -t TOKEN -d 'some.github.com' -o 'myorg' -r 'myrepo' -g "${RESOURCES_DIR}/mock-global" -b branch -a "${TEMP_DIR}"
would constitute this curl
call:
curl --telnet-option TOKEN --data 'some.github.com' --output 'myorg' --range 'myrepo' --globoff "${RESOURCES_DIR}/mock-global" --cookie branch --append "${TEMP_DIR}"
which I don't think is what you want.
This makes me think that there is more going on in the real code than what is represented in the more minimal example.
The most likely answer is that the curl_command
uses the input to construct the correct URL (-d
= 'domain', -o
= 'org', -r
='repo'?) but that is just a guess.
Finally, let's address the eval
in string_matches_glob
. I assume
this is to allow dynamically matching $1
against $2
. If that is the case, there are easier ways to do this, without eval
or `case.
For instance, using the =~
Regular Expression matching operator:
string_matches_glob() {
local string="$1"
local glob="$2"
[[ "$string" =~ $glob ]]
}
or, if that is not available or desirable, using grep
:
string_matches_glob() {
local string="$1"
local glob="$2"
echo "$string" | grep -q -E "$glob"
}
As both solutions are sufficiently small, they can be used inline, without the need for a separate function:
if [[ "${CURL_URL}" =~ */contents/file.name* ]]; then
...
fi
or:
if echo "${CURL_URL}" | grep -q -E '*/contents/file.name*'; then
...
fi
You may be wondering "Why does this matter?" but the less magic you have in your code, the easier it is to debug.
All of these things make me think we don't have enough of the real code to properly debug the issue, although the most likely culprit is the curl_command
construction.
Answered By - Potherca Answer Checked By - Dawn Plyler (WPSolving Volunteer)