Skip to content

Commit f1ea92a

Browse files
bin/verify-exercises-in-docker (#804)
* bin/verify-exercises-in-docker * update README.md
1 parent 89c9657 commit f1ea92a

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ just test-practice-example \*
3232
just test-practice-example '*'
3333
```
3434

35+
Run `bin/verify-exercises-in-docker` to run tests in the track's test runner Docker image.
36+
3537
### Raku icon
3638

3739
The Raku "Camelia" logo is owned by Larry Wall and is released under version 2.0 of the Artistic License.

bin/verify-exercises-in-docker

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
# Example: verify all exercises against specified test runner
15+
# bin/verify-exercises-in-docker -i my-local-image
16+
17+
set -e
18+
shopt -s nullglob
19+
20+
die() {
21+
echo "$*" >&2
22+
exit 1
23+
}
24+
25+
required_tool() {
26+
command -v "${1}" >/dev/null 2>&1 || die "${1} is required but not installed. Please install it and make sure it's in your PATH."
27+
}
28+
29+
copy_example_or_exemplar_to_solution() {
30+
local dir="${1}"
31+
jq -r '[.files.solution, .files.exemplar // .files.example] | transpose | map(select(.[0] and .[1]))[][]' "${dir}/.meta/config.json" \
32+
| while read -r dst; read -r src; do
33+
cp "${dir}/${src}" "${dir}/${dst}"
34+
done
35+
}
36+
37+
run_tests() {
38+
local slug="${1}" dir="${2}"
39+
local -a docker_args
40+
41+
docker_args+=( --rm --network none )
42+
docker_args+=( --mount "type=bind,src=${dir},dst=/solution" )
43+
# /tmp needs to be a proper volume to run the compiled executable; tmpfs is not executable.
44+
docker_args+=( --mount "type=volume,dst=/tmp" )
45+
46+
# /solution is used both as the location to read the code from and as a destination for the results.json file.
47+
docker run "${docker_args[@]}" "${image}" "${slug}" /solution /solution
48+
jq -e '.status == "pass"' "${dir}/results.json" >/dev/null 2>&1
49+
}
50+
51+
verify_exercise() {
52+
local dir slug tmpdir
53+
dir="${1%/}"
54+
slug="${dir##*/}"
55+
tmpdir="$(mktemp -d -t "exercism-verify-${slug}-XXXXX")"
56+
57+
if jq -e --arg slug "${slug}" '.exercises.practice[] | select(.slug == $slug and .status == "deprecated")' config.json >/dev/null 2>&1; then
58+
echo "Skipping ${slug} (deprecated)..."
59+
return 0
60+
fi
61+
62+
echo "Verifying ${slug} exercise..."
63+
(
64+
# The container is compiling the solution as user root.
65+
# The tmpdir created on your local machine (and bind mounted into the container)
66+
# will contain files owned by root.
67+
# Let docker clean it up for us.
68+
trap '
69+
docker run --rm \
70+
--mount "type=bind,src=$(dirname "${tmpdir}"),dst=/tmp/exercism" \
71+
--entrypoint rm \
72+
"${image}" \
73+
-r "/tmp/exercism/$(basename "${tmpdir}")"
74+
' EXIT
75+
76+
cp -r "${dir}/." "${tmpdir}" || exit
77+
copy_example_or_exemplar_to_solution "${tmpdir}"
78+
run_tests "${slug}" "${tmpdir}" || { cat "${tmpdir}/results.json"; exit 1; }
79+
)
80+
}
81+
82+
verify_exercises() {
83+
local -a exercises
84+
local parent path
85+
if (( $# )); then
86+
for slug; do
87+
for parent in concept practice; do
88+
path="./exercises/${parent}/${slug}"
89+
[[ -d "${path}" ]] && exercises+=( "${path}" )
90+
done
91+
done
92+
else
93+
exercises=( ./exercises/{concept,practice}/* )
94+
fi
95+
(( ${#exercises[@]} )) || die "No matching exercises found"
96+
97+
rc=0
98+
for exercise_dir in "${exercises[@]}"; do
99+
verify_exercise "${exercise_dir}" || rc=$?
100+
done
101+
return "$rc"
102+
}
103+
104+
105+
required_tool docker
106+
required_tool jq
107+
108+
image=''
109+
while getopts i: opt; do
110+
case "${opt}" in
111+
i) image="${OPTARG}" ;;
112+
?) die "Unknown option: -$OPTARG" ;;
113+
esac
114+
done
115+
shift "$((OPTIND - 1))"
116+
117+
if [[ -z "${image}" ]]; then
118+
image="exercism/raku-test-runner"
119+
docker pull "${image}" ||
120+
die "docker pull ${image} failed. Check the test runner docs at https://exercism.org/docs/building/tooling/test-runners for more information."
121+
fi
122+
123+
verify_exercises "$@"

0 commit comments

Comments
 (0)