Skip to content

Commit 73e0b63

Browse files
Add verify-exercises-in-docker script (#361)
1 parent c275c3c commit 73e0b63

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

bin/verify-exercises-in-docker

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env bash
2+
3+
# Synopsis:
4+
# Verify that each exercise's example/exemplar solution passes the tests
5+
# using the track's test runner Docker image.
6+
# You can either verify all exercises or a single exercise.
7+
8+
# Example: verify all exercises in Docker
9+
# bin/verify-exercises-in-docker
10+
11+
# Example: verify single exercise in Docker
12+
# bin/verify-exercises-in-docker two-fer
13+
14+
set -eo pipefail
15+
16+
die() { echo "$*" >&2; exit 1; }
17+
18+
required_tool() {
19+
command -v "${1}" >/dev/null 2>&1 ||
20+
die "${1} is required but not installed. Please install it and make sure it's in your PATH."
21+
}
22+
23+
required_tool docker
24+
25+
splice_example_with_tests() {
26+
local src example
27+
src=$(jq -r '.files.solution[0]' .meta/config.json)
28+
example=$(jq -r '.files.example[0]' .meta/config.json)
29+
30+
{
31+
cat "$example"
32+
sed -nE '
33+
/unittest/,$ {
34+
s/(int allTestsEnabled =).*/\1 1;/
35+
p
36+
}
37+
' "$src"
38+
} > "${src}.tmp"
39+
mv "${src}.tmp" "$src"
40+
}
41+
42+
pull_docker_image() {
43+
local msg
44+
# shellcheck disable=SC2016 # %s is a printf format specifier, not a shell expansion
45+
printf -v msg 'Could not find the `%s` Docker image.\nCheck the test runner docs at https://exercism.org/docs/building/tooling/test-runners for more information.' "${image}"
46+
docker pull "${image}" || die "${msg}"
47+
}
48+
49+
run_tests() {
50+
local slug
51+
slug="${1}"
52+
53+
local -a docker_args
54+
docker_args+=(--rm --network none --read-only)
55+
docker_args+=(--mount "type=bind,src=${PWD},dst=/solution")
56+
docker_args+=(--mount "type=bind,src=${PWD},dst=/output")
57+
docker_args+=(--mount "type=tmpfs,dst=/tmp" --mount "type=tmpfs,dst=/root")
58+
docker run "${docker_args[@]}" "${image}" "${slug}" /solution /output
59+
jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
60+
}
61+
62+
verify_exercise() {
63+
local dir
64+
local slug
65+
local tmp_dir
66+
dir=$(realpath "${1}")
67+
slug=$(basename "${dir}")
68+
tmp_dir=$(mktemp -d -t "exercism-verify-${slug}-XXXXX")
69+
70+
echo "Verifying ${slug} exercise..."
71+
72+
(
73+
trap 'rm -rf "${tmp_dir}"' EXIT # remove tempdir when subshell ends
74+
cp -r "${dir}/." "${tmp_dir}"
75+
cd "${tmp_dir}"
76+
77+
splice_example_with_tests
78+
run_tests "${slug}" || { cat "${PWD}/results.json"; exit 1; }
79+
)
80+
}
81+
82+
verify_exercises() {
83+
local exercise_slug
84+
exercise_slug="${1}"
85+
86+
shopt -s nullglob
87+
# shellcheck disable=SC2206 # intentional glob expansion
88+
exercises=(./exercises/{concept,practice}/${exercise_slug}/)
89+
local count=0
90+
for exercise_dir in "${exercises[@]}"; do
91+
[[ -d "${exercise_dir}" ]] || continue
92+
verify_exercise "${exercise_dir}"
93+
((++count))
94+
done
95+
((count > 0)) || die 'no matching exercises found!'
96+
}
97+
98+
image=''
99+
while getopts :i: opt; do
100+
case "${opt}" in
101+
i) image=$OPTARG ;;
102+
?) echo >&2 "Unknown option: -$OPTARG"; exit 1 ;;
103+
esac
104+
done
105+
shift "$((OPTIND - 1))"
106+
107+
if [[ -z "${image}" ]]; then
108+
image="exercism/d-test-runner"
109+
pull_docker_image
110+
fi
111+
112+
if [[ -z "${1:-}" ]]; then
113+
verify_exercises '*'
114+
else
115+
verify_exercises "$1"
116+
fi

0 commit comments

Comments
 (0)