Skip to content

Commit 804b764

Browse files
Add script to add practice exercise (#192)
1 parent 11e9e18 commit 804b764

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

TRACK_README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ Another benefit is that you don't have to install track-specific dependencies (e
2323

2424
To test a single exercise, run `./bin/verify-exercises-in-docker <exercise-slug>`.
2525

26-
### Track linting
26+
### Linting
2727

2828
[`configlet`](https://exercism.org/docs/building/configlet) is an Exercism-wide tool for working with tracks. You can download it by running:
2929

3030
```shell
3131
$ ./bin/fetch-configlet
3232
```
3333

34-
Run its [`lint` command](https://exercism.org/docs/building/configlet/lint) to verify if all exercises have all the necessary files and if config files are correct:
34+
Run its [`lint` command](https://exercism.org/docs/building/configlet/lint) to verify if exercises have the required files and if config files are correct:
3535

3636
```shell
3737
$ ./bin/configlet lint
@@ -53,3 +53,17 @@ Basic linting finished successfully:
5353
- Required track docs are present
5454
- Required shared exercise docs are present
5555
```
56+
57+
## Adding exercises
58+
59+
New (practice) exercises can be added via:
60+
61+
```shell
62+
bin/add-practice-exercise <exercise-slug>
63+
```
64+
65+
Optionally, you can also specify the exercise's difficulty (via `-d`) and/or author's GitHub username (via `-a`):
66+
67+
```shell
68+
bin/add-practice-exercise -a foobar -d 3 <exercise-slug>
69+
```

bin/add-practice-exercise

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
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+
16+
set -euo pipefail
17+
scriptname=$0
18+
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)."
24+
exit 1
25+
}
26+
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."
32+
}
33+
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+
61+
slug="${1}"
62+
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+
70+
exercise_dir="exercises/practice/${slug}"
71+
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")
72+
73+
cat << NEXT_STEPS
74+
75+
Your next steps are:
76+
- Create the test suite in $(jq -r '.test' <<< "${files}")
77+
- The tests should be based on the canonical data at 'https://github.com/exercism/problem-specifications/blob/main/exercises/${slug}/canonical-data.json'
78+
- Any test cases you don't implement, mark them in 'exercises/practice/${slug}/.meta/tests.toml' with "include = false"
79+
- Create the example solution in $(jq -r '.example' <<< "${files}")
80+
- Verify the example solution passes the tests by running 'bin/verify-exercises ${slug}'
81+
- Create the stub solution in $(jq -r '.solution' <<< "${files}")
82+
- Update the 'difficulty' value for the exercise's entry in the 'config.json' file in the repo's root
83+
- Validate CI using 'bin/configlet lint' and 'bin/configlet fmt'
84+
NEXT_STEPS

0 commit comments

Comments
 (0)