Skip to content

Commit f2cb0b5

Browse files
Update add exercise script
1 parent 2ec4e48 commit f2cb0b5

1 file changed

Lines changed: 75 additions & 10 deletions

File tree

bin/add-exercise

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,75 @@
11
#!/usr/bin/env bash
22

3+
# Synopsis:
4+
# Scaffold the files for a new practice exercise.
5+
# After creating the exercise, follow the instructions in the output.
6+
7+
# Example:
8+
# bin/add-practice-exercise two-fer
9+
10+
# Example with difficulty:
11+
# bin/add-practice-exercise -d 5 two-fer
12+
13+
# Example with author and difficulty:
14+
# bin/add-practice-exercise -a foo -d 3 two-fer
15+
316
set -euo pipefail
17+
scriptname=$0
418

5-
if [ $# -ne 1 ]; then
6-
echo "Usage: bin/add-exercise <exercise-slug>"
19+
help_and_exit() {
20+
echo >&2 "Scaffold the files for a new practice exercise."
21+
echo >&2 "Usage: ${scriptname} [-h] [-a author] [-d difficulty] <exercise-slug>"
22+
echo >&2 "Where: author is the GitHub username of the exercise creator."
23+
echo >&2 "Where: difficulty is between 1 (easiest) to 10 (hardest)."
724
exit 1
8-
fi
25+
}
926

10-
command -v jq >/dev/null 2>&1 || {
11-
echo >&2 "jq is required but not installed. Please install it and make sure it's in your PATH."
12-
exit 1
27+
die() { echo >&2 "$*"; exit 1; }
28+
29+
required_tool() {
30+
command -v "${1}" >/dev/null 2>&1 ||
31+
die "${1} is required but not installed. Please install it and make sure it's in your PATH."
1332
}
1433

34+
require_files_template() {
35+
jq -e --arg key "${1}" '.files[$key] | length > 0' config.json > /dev/null ||
36+
die "The '.files.${1}' array in the 'config.json' file is empty. Please add at least one file. See https://exercism.org/docs/building/tracks/config-json#h-files for more information."
37+
}
38+
39+
required_tool jq
40+
41+
require_files_template "solution"
42+
require_files_template "test"
43+
require_files_template "example"
44+
45+
[[ -f ./bin/fetch-configlet ]] || die "Run this script from the repo's root directory."
46+
47+
author=''
48+
difficulty='1'
49+
while getopts :ha:d: opt; do
50+
case $opt in
51+
h) help_and_exit ;;
52+
a) author=$OPTARG ;;
53+
d) difficulty=$OPTARG ;;
54+
?) echo >&2 "Unknown option: -$OPTARG"; help_and_exit ;;
55+
esac
56+
done
57+
shift "$((OPTIND - 1))"
58+
59+
(( $# >= 1 )) || help_and_exit
60+
1561
slug="${1}"
16-
snake_name=$(echo "${slug}" | sed 's/-/_/g')
1762

18-
bin/fetch-configlet
19-
bin/configlet create --practice-exercise "${slug}"
20-
bin/reorder-exercises
63+
if [[ -z "${author}" ]]; then
64+
read -rp 'Your GitHub username: ' author
65+
fi
66+
67+
./bin/fetch-configlet
68+
./bin/configlet create --practice-exercise "${slug}" --author "${author}" --difficulty "${difficulty}"
69+
./bin/reorder-exercises
2170

2271
exercise_dir="exercises/practice/${slug}"
72+
snake_name=$(echo "${slug}" | sed 's/-/_/g')
2373
test_file=$(jq --arg dir "${exercise_dir}" -r '$dir + "/" + .files.test[0]' "exercises/practice/${slug}/.meta/config.json")
2474
cat <<EOT >> "${test_file}"
2575
pending :-
@@ -32,3 +82,18 @@ pending :-
3282
3383
:- end_tests(${snake_name}).
3484
EOT
85+
86+
files=$(jq -r --arg dir "${exercise_dir}" '.files | to_entries | map({key: .key, value: (.value | map("'"'"'" + $dir + "/" + . + "'"'"'") | join(" and "))}) | from_entries' "${exercise_dir}/.meta/config.json")
87+
88+
cat << NEXT_STEPS
89+
90+
Your next steps are:
91+
- Create the test suite in $(jq -r '.test' <<< "${files}")
92+
- The tests should be based on the canonical data at 'https://github.com/exercism/problem-specifications/blob/main/exercises/${slug}/canonical-data.json'
93+
- Any test cases you don't implement, mark them in 'exercises/practice/${slug}/.meta/tests.toml' with "include = false"
94+
- Create the example solution in $(jq -r '.example' <<< "${files}")
95+
- Verify the example solution passes the tests by running 'bin/verify-exercises ${slug}'
96+
- Create the stub solution in $(jq -r '.solution' <<< "${files}")
97+
- Update the 'difficulty' value for the exercise's entry in the 'config.json' file in the repo's root
98+
- Validate CI using 'bin/configlet lint' and 'bin/configlet fmt'
99+
NEXT_STEPS

0 commit comments

Comments
 (0)