Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions bin/verify-exercises-in-docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env bash

# Synopsis:
# Verify that each exercise's example/exemplar solution passes the tests
# using the track's test runner Docker image.
# You can either verify all exercises or a single exercise.

# Example: verify all exercises in Docker
# bin/verify-exercises-in-docker

# Example: verify single exercise in Docker
# bin/verify-exercises-in-docker two-fer

# Example: verify all exercises against specified test runner
# bin/verify-exercises-in-docker -i my-local-image

set -e
shopt -s nullglob

die() {
echo "$*" >&2
exit 1
}

required_tool() {
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."
}

copy_example_or_exemplar_to_solution() {
local dir="${1}"
jq -r '[.files.solution, .files.exemplar // .files.example] | transpose | map(select(.[0] and .[1]))[][]' "${dir}/.meta/config.json" \
| while read -r dst; read -r src; do
cp "${dir}/${src}" "${dir}/${dst}"
done
}

run_tests() {
local slug="${1}" dir="${2}"
local -a docker_args

docker_args+=( --rm --network none )
docker_args+=( --mount "type=bind,src=${dir},dst=/solution" )
# /tmp needs to be a proper volume to run the compiled executable; tmpfs is not executable.
docker_args+=( --mount "type=volume,dst=/tmp" )

# /solution is used both as the location to read the code from and as a destination for the results.json file.
docker run "${docker_args[@]}" "${image}" "${slug}" /solution /solution
jq -e '.status == "pass"' "${dir}/results.json" >/dev/null 2>&1
}

verify_exercise() {
local dir slug tmpdir
dir="${1%/}"
slug="${dir##*/}"
tmpdir="$(mktemp -d -t "exercism-verify-${slug}-XXXXX")"

echo "Verifying ${slug} exercise..."
(
trap 'rm -rf "${tmpdir}"' EXIT # remove tempdir when subshell ends
cp -r "${dir}/." "${tmpdir}" || exit
copy_example_or_exemplar_to_solution "${tmpdir}"
run_tests "${slug}" "${tmpdir}" || { cat "${tmpdir}/results.json"; exit 1; }
)
}

verify_exercises() {
local -a exercises
local parent path
if (( $# )); then
for slug; do
for parent in concept practice; do
path="./exercises/${parent}/${slug}"
[[ -d "${path}" ]] && exercises+=( "${path}" )
done
done
else
exercises=( ./exercises/{concept,practice}/* )
fi
(( ${#exercises[@]} )) || die "No matching exercises found"

rc=0
for exercise_dir in "${exercises[@]}"; do
verify_exercise "${exercise_dir}" || rc=$?
done
return "$rc"
}


required_tool docker
required_tool jq

image=''
while getopts i: opt; do
case "${opt}" in
i) image="${OPTARG}" ;;
?) die "Unknown option: -$OPTARG" ;;
esac
done
shift "$((OPTIND - 1))"

if [[ -z "${image}" ]]; then
image="exercism/elixir-test-runner"
docker pull "${image}" ||
die "docker pull ${image} failed. Check the test runner docs at https://exercism.org/docs/building/tooling/test-runners for more information."
fi

verify_exercises "$@"
Loading