From 5828116bb6d4e93cd3afcb4612e42d2ca56f44a2 Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Tue, 7 Mar 2023 12:12:50 +0100 Subject: [PATCH 001/129] :bug: Fix analyzer comments link (#434) --- building/tooling/analyzers/comments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tooling/analyzers/comments.md b/building/tooling/analyzers/comments.md index 92ebdfde..7d40b7fb 100644 --- a/building/tooling/analyzers/comments.md +++ b/building/tooling/analyzers/comments.md @@ -135,6 +135,6 @@ on type later. For simpler cases, it's more common to rely on `errors.New` or custom errors might be interesting. ``` -[git-website-copy]: https://github.com/exercism/website-copy/tree/main/automated-comments +[git-website-copy]: https://github.com/exercism/website-copy/tree/main/analyzer-comments [issue-ci-comments]: https://github.com/exercism/automated-mentoring-support/issues/51 [git-website-copy-label]: https://github.com/exercism/website-copy/pulls?q=is%3Aopen+is%3Apr+label%3Atype%2Fanalyzer-comments From 73835d5d918f3753240a50a7197b5a623cfd024e Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Fri, 10 Mar 2023 06:41:36 +0000 Subject: [PATCH 002/129] Provide an alternative to skips (#435) --- building/tracks/practice-exercises.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 04a7e27c..dc7b2644 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -571,7 +571,9 @@ public static class Isogram - The code should be as simple as possible. - Only use language features introduced by the exercise's prerequisites (and their prerequisites, and so on). - The tests file is shown to the student when doing in-browser coding and downloaded to the student's file system when using the CLI. -- Exercism favors Practice Exercises being completed via Test Driven Development. To achieve this, all but the first test should be skipped by default. How this is done differs between languages. +- Exercism favors Practice Exercises being completed via Test Driven Development. To achieve this, there are two options: + - The test runner must run the tests in the order defined in the file AND the test suite must bail on the first failure; or + - All but the first test should be skipped by default. - The relative paths to the test file(s) must be specified in the [`.meta/config.json` file's `"files.test"` key](./#filemetaconfigjson). #### Example From fac95087d00f81e72845547f105276392aaf1d7f Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Tue, 4 Apr 2023 18:15:57 +0100 Subject: [PATCH 003/129] Add Suggesting Exercise Improvements doc (#438) * Add Suggesting Exercise Improvements doc * Update community/suggesting-exercise-improvements.md Co-authored-by: BethanyG * Update community/suggesting-exercise-improvements.md Co-authored-by: BethanyG * Apply suggestions from code review Co-authored-by: FAP <459631+fapdash@users.noreply.github.com> Co-authored-by: BethanyG * Apply suggestions from code review Co-authored-by: Katrina Owen * Add to config.json * Update community/suggesting-exercise-improvements.md Co-authored-by: Isaac Good --------- Co-authored-by: BethanyG Co-authored-by: FAP <459631+fapdash@users.noreply.github.com> Co-authored-by: Katrina Owen Co-authored-by: Isaac Good --- community/config.json | 7 ++ community/suggesting-exercise-improvements.md | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 community/suggesting-exercise-improvements.md diff --git a/community/config.json b/community/config.json index 3267ad71..4eb2b529 100644 --- a/community/config.json +++ b/community/config.json @@ -27,6 +27,13 @@ "title": "Administrators", "blurb": "Learn about Exercism's Administrators" }, + { + "uuid": "4476a48e-3061-44c2-ba56-56896b1e0dc9", + "slug": "suggesting-exercise-improvements", + "path": "community/suggesting-exercise-improvements.md", + "title": "Suggesting Exercise Improvements", + "blurb": "What to consider before suggesting exercise improvements" + }, { "uuid": "7e11a8f5-e6f8-49f1-ab0c-81427fa12c5c", "slug": "being-a-good-community-member", diff --git a/community/suggesting-exercise-improvements.md b/community/suggesting-exercise-improvements.md new file mode 100644 index 00000000..3b916882 --- /dev/null +++ b/community/suggesting-exercise-improvements.md @@ -0,0 +1,69 @@ +# Suggesting Exercise Improvements + +So you've found something you'd like to improve about an exercise. +Firstly, thank you for caring, and for making the time to tell us! 💙 + +When changing an exercise, there are a few things to consider, and they're slightly different for Learning Exercises (those that teach topics in the Syllabus) and Practice Exercises (the rest). +We go into those differences below. + +However, firstly there are a couple of generic things to consider. + +### Changing an exercise has consequences + +There are quite a few consequences in changing an exercise: +1. Adding new tests may break existing solutions. + This has the consequence of outdating thousands of people's work, which is frustrating for them if the extra tests aren't highlighting any existing bugs. +2. We have to retest everyone's work, which has both an environmental and financial cost. +3. One or more maintainers have to take the time to review the changes, which means they're not able to make other improvements to the track during that time. + +For all these reasons, we are careful to only change exercises where there's a strong identifiable benefit. + +### Avoid trending towards entropy + +The aim of exercises' test suites is not to be comprehensive about all possible cases. +Our exercises are not production software, nor are they designed to imitate real-world use-cases. +They are designed to be toy-problems to help you gain **fluency in a programming language**. +We therefore deliberately avoid trying to cover every edge-case, forcing lots of input validation, or other such real-world concerns. +If your suggested improvement is to catch an edge-case or to check input-validation, it's unlikely to be accepted unless it makes a substantial difference to the exercise. + +## Learning Exercises + +Learning Exercises are designed with one goal: to teach concepts. +All changes to an exercise will first and foremost be considered against whether they improve the teaching of the concepts. + +Learning Exercises are (especially) not designed to be exhaustive in their tests. +They can also often be slightly contrived or obtuse to avoid using concepts a student has not yet learned, or so as not to overwhelm a student. + +If your suggested change could potentially distract from the learning of the concept, it will probably get rejected. +If the change makes the learning easier, it'll be strongly considered. +If it sits somewhere in between, it may be accepted, but is unlikely to be prioritised. + +## Practice Exercises + +The main thing to understand about Practice Exercises is that nearly all of them live in a [central repository](https://github.com/exercism/problem-specifications/) (known as "Problem Specifications"). +Making a change to an exercise therefore has knock-on effects over all tracks. +That means a good change is extra powerful as it'll help all languages. +But it also increases the burden of the change, as multiple cross-track maintainers need to agree to the change for it to be accepted, and then each language's maintainers need to pull the changes downstream into their tracks. + +Changes to exercises can also mean that the concepts they're linked to in syllabuses become muddier, or that exercises need extra features to solve, which changes where they become unlocked. +This also needs to be considered across tracks before changes are accepted. + +While we do have mechanisms for only some tracks to have certain test-cases, we tend to discourage it, as this forking can offer confusion to both maintainers and students. +So when proposing changes to Practice Exercises, please consider the bigger picture across tracks. + +## How to get your changes accepted + +Although there are a lot of reasons that we don't accept suggestions, a lot of the time people come up with great ideas that we do accept! + +We're also nearly always open to changes in wording that add clarity, especially in Learning Exercises. + +To give your suggestions the best shot of being approved, please write out your suggestions showing that you've considered the following: +- The tangible improvement that this change makes (ideally with code examples of what your change enables/stops). +- The impact on existing solutions, and why this is worth it. +- Any changes to the concepts taught or used. +- An understanding of why things might be the way they are. (Please read our post on [Chesterton's Fence](https://exercism.org/docs/community/being-a-good-community-member/chestertons-fence).) + +And please remember to express opinions as opinions and facts as facts. +That tends to lead to the most productive conversations. + +Thanks again for taking the time to make a suggestion and for reading this document! From 867765bf238b14f78de061a616219e56345a271f Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Tue, 4 Apr 2023 18:17:27 +0100 Subject: [PATCH 004/129] Move doc --- community/config.json | 2 +- community/{ => good-member}/suggesting-exercise-improvements.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename community/{ => good-member}/suggesting-exercise-improvements.md (100%) diff --git a/community/config.json b/community/config.json index 4eb2b529..6a9fb038 100644 --- a/community/config.json +++ b/community/config.json @@ -30,7 +30,7 @@ { "uuid": "4476a48e-3061-44c2-ba56-56896b1e0dc9", "slug": "suggesting-exercise-improvements", - "path": "community/suggesting-exercise-improvements.md", + "path": "community/good-member/suggesting-exercise-improvements.md", "title": "Suggesting Exercise Improvements", "blurb": "What to consider before suggesting exercise improvements" }, diff --git a/community/suggesting-exercise-improvements.md b/community/good-member/suggesting-exercise-improvements.md similarity index 100% rename from community/suggesting-exercise-improvements.md rename to community/good-member/suggesting-exercise-improvements.md From 709886f93d6fc04ef5c25c1adef9563158be72b6 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Tue, 4 Apr 2023 18:18:28 +0100 Subject: [PATCH 005/129] Reorder config file --- community/config.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/community/config.json b/community/config.json index 6a9fb038..1baf3f5a 100644 --- a/community/config.json +++ b/community/config.json @@ -27,13 +27,6 @@ "title": "Administrators", "blurb": "Learn about Exercism's Administrators" }, - { - "uuid": "4476a48e-3061-44c2-ba56-56896b1e0dc9", - "slug": "suggesting-exercise-improvements", - "path": "community/good-member/suggesting-exercise-improvements.md", - "title": "Suggesting Exercise Improvements", - "blurb": "What to consider before suggesting exercise improvements" - }, { "uuid": "7e11a8f5-e6f8-49f1-ab0c-81427fa12c5c", "slug": "being-a-good-community-member", @@ -41,6 +34,13 @@ "title": "Being a good community member", "blurb": "Learn how to be a great member of the Exercism community" }, + { + "uuid": "4476a48e-3061-44c2-ba56-56896b1e0dc9", + "slug": "suggesting-exercise-improvements", + "path": "community/good-member/suggesting-exercise-improvements.md", + "title": "Suggesting Exercise Improvements", + "blurb": "What to consider before suggesting exercise improvements" + }, { "uuid": "244e2482-a0c2-446c-822d-97172acc013f", "slug": "being-a-good-community-member/the-words-that-we-use", From 8c604214c4231b374d13e0bd8b2b109bc0d181f6 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Tue, 4 Apr 2023 18:22:15 +0100 Subject: [PATCH 006/129] Fix nesting --- community/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community/config.json b/community/config.json index 1baf3f5a..2807d880 100644 --- a/community/config.json +++ b/community/config.json @@ -36,7 +36,7 @@ }, { "uuid": "4476a48e-3061-44c2-ba56-56896b1e0dc9", - "slug": "suggesting-exercise-improvements", + "slug": "being-a-good-community-member/suggesting-exercise-improvements", "path": "community/good-member/suggesting-exercise-improvements.md", "title": "Suggesting Exercise Improvements", "blurb": "What to consider before suggesting exercise improvements" From b08c4abbd782793123032420fc6acd994cc56a46 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 14 Apr 2023 11:41:58 +0200 Subject: [PATCH 007/129] configlet: lint: fix name of `title` key for approaches/articles (#441) Fixes: #440 --- building/configlet/lint.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/building/configlet/lint.md b/building/configlet/lint.md index 8176f7ac..1280aab1 100644 --- a/building/configlet/lint.md +++ b/building/configlet/lint.md @@ -357,8 +357,8 @@ The `config.json` file should have the following checks: - The `"approaches[].slug"` value must be a kebab-case string² with length <= 255 - The `"approaches[].slug"` value must have a corresponding non-empty `/content.md` file - The `"approaches[].slug"` value must have a corresponding non-empty `/snippet.txt` file -- The `"approaches[].name"` key is required -- The `"approaches[].name"` value must be a Title Case string³ with length <= 255 +- The `"approaches[].title"` key is required +- The `"approaches[].title"` value must be a Title Case string³ with length <= 255 - The `"approaches[].blurb"` key is required - The `"approaches[].blurb"` value must be a non-blank string¹ with length <= 350 - The `"approaches[].authors"` key is required @@ -399,8 +399,8 @@ The `config.json` file should have the following checks: - The `"articles[].slug"` value must be a kebab-case string² with length <= 255 - The `"articles[].slug"` value must have a corresponding non-empty `/content.md` file - The `"articles[].slug"` value must have a corresponding non-empty `/snippet.md` file -- The `"articles[].name"` key is required -- The `"articles[].name"` value must be a Title Case string³ with length <= 255 +- The `"articles[].title"` key is required +- The `"articles[].title"` value must be a Title Case string³ with length <= 255 - The `"articles[].blurb"` key is required - The `"articles[].blurb"` value must be a non-blank string¹ with length <= 350 - The `"articles[].authors"` key is required From ec98b161f54f547462533fbd456e4218a784329b Mon Sep 17 00:00:00 2001 From: June <12543047+junedev@users.noreply.github.com> Date: Tue, 18 Apr 2023 08:59:03 +0200 Subject: [PATCH 008/129] update exercise story table (#439) --- building/tracks/stories/README.md | 404 +++++++++++++++--------------- 1 file changed, 203 insertions(+), 201 deletions(-) diff --git a/building/tracks/stories/README.md b/building/tracks/stories/README.md index ddcaf5b8..2f0bc4b6 100644 --- a/building/tracks/stories/README.md +++ b/building/tracks/stories/README.md @@ -5,204 +5,206 @@ Via the menu entries in this section you can browse through the different storie Additionally, the table below gives an overview which exercises are currently used to teach which concepts. The last column includes links to the respective implementations in the tracks. -| Exercise Slug | Concepts | Tracks | -| -------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **bird-watcher** | arrays, vectors, for-loops, increment-decrement | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/bird-watcher), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/bird-watcher), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/bird-watcher), [go](https://github.com/exercism/go/tree/main/exercises/concept/bird-watcher), [java](https://github.com/exercism/java/tree/main/exercises/concept/bird-watcher), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/bird-watcher) | -| **lucians-luscious-lasagna** | basics, basics-1, functions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/lucians-luscious-lasagna), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/lucians-luscious-lasagna), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/lucians-luscious-lasagna), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/lucians-luscious-lasagna), [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/lucians-luscious-lasagna), [kotlin](https://github.com/exercism/kotlin/tree/main/exercises/concept/lucians-luscious-lasagna) (wip), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/lucians-luscious-lasagna) | -| **annalyns-infiltration** | booleans, boolean-logic | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/annalyns-infiltration), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/annalyns-infiltration), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/annalyns-infiltration), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/annalyns-infiltration), [go](https://github.com/exercism/go/tree/main/exercises/concept/annalyns-infiltration), [java](https://github.com/exercism/java/tree/main/exercises/concept/annalyns-infiltration), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/annalyns-infiltration), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/annalyns-infiltration) (wip), [kotlin](https://github.com/exercism/kotlin/tree/main/exercises/concept/annalyns-infiltration) (wip) | -| **secure-munchester-united** | casting | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/secure-munchester-united) | -| **squeaky-clean** | chars, | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/squeaky-clean), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/squeaky-clean) (wip), [java](https://github.com/exercism/java/tree/main/exercises/concept/squeaky-clean) | -| **elons-toys** | classes, methods | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/elons-toys), [go](https://github.com/exercism/go/tree/main/exercises/concept/elons-toys) | -| **authentication-system** | constants | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/authentication-system) | -| **need-for-speed** | constructors, alias, structs | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/need-for-speed), [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/need-for-speed), [go](https://github.com/exercism/go/tree/main/exercises/concept/need-for-speed), [java](https://github.com/exercism/java/tree/main/exercises/concept/need-for-speed) | -| **booking-up-for-beauty** | datetimes, time | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/booking-up-for-beauty), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/booking-up-for-beauty), [go](https://github.com/exercism/go/tree/main/exercises/concept/booking-up-for-beauty) | -| **international-calling-connoisseur** | dictionaries, | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/international-calling-connoisseur), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/international-calling-connoisseur) (wip) | -| **logs-logs-logs** | enums, runes | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/logs-logs-logs), [go](https://github.com/exercism/go/tree/main/exercises/concept/logs-logs-logs) | -| **faceid-2** | equality | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/faceid-2) | -| **calculator-conundrum** | exceptions, exception-handling | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/calculator-conundrum), [java](https://github.com/exercism/java/tree/main/exercises/concept/calculator-conundrum) (wip) | -| **the-weather-in-deather** | ternary-operators | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/the-weather-in-deather) | -| **attack-of-the-trolls** | attributes | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/attack-of-the-trolls) | -| **interest-is-interesting** | do-while-loops, floating-point-numbers | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/interest-is-interesting), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/interest-is-interesting), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/interest-is-interesting), [go](https://github.com/exercism/go/tree/main/exercises/concept/interest-is-interesting) | -| **wizards-and-warriors** | inheritance | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/wizards-and-warriors), [java](https://github.com/exercism/java/tree/main/exercises/concept/wizards-and-warriors) | -| **hyper-optimized-telemetry** | integral-numbers | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyper-optimized-telemetry) | -| **remote-control-competition** | interfaces | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/remote-control-competition), [java](https://github.com/exercism/java/tree/main/exercises/concept/remote-control-competition) | -| **tracks-on-tracks-on-tracks** | generic-types, lists | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/tracks-on-tracks-on-tracks) | -| **wizards-and-warriors-2** | method-overloading | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/wizards-and-warriors-2) | -| **red-vs-blue-darwin-style** | namespaces | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/red-vs-blue-darwin-style) | -| **remote-control-cleanup** | nested-types | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/remote-control-cleanup) | -| **tim-from-marketing** | nullability | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/tim-from-marketing) | -| **cars-assemble** | if-statements, numbers, if-then-else-expressions, arithmetic-operators | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/cars-assemble), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/cars-assemble), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/cars-assemble), [go](https://github.com/exercism/go/tree/main/exercises/concept/cars-assemble), [java](https://github.com/exercism/java/tree/main/exercises/concept/cars-assemble) | -| **developer-privileges** | object-initializers | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/developer-privileges) | -| **hyperia-forex** | operator-overloading | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyperia-forex) | -| **hyperinflation-hits-hyperia** | overflow | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyperinflation-hits-hyperia) | -| **building-telemetry** | parameters | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/building-telemetry) | -| **weighing-machine** | properties | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/weighing-machine) | -| **roll-the-die** | randomness | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/roll-the-die) | -| **parsing-log-files** | regular-expressions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/parsing-log-files), [go](https://github.com/exercism/go/tree/main/exercises/concept/parsing-log-files) | -| **object-relational-mapping** | resource-cleanup | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/object-relational-mapping) | -| **orm-in-one-go** | resource-lifetime | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/orm-in-one-go) | -| **high-school-sweethearts** | string-formatting | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/high-school-sweethearts) | -| **log-levels** | strings, enums | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/log-levels), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/log-levels), [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/log-levels), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/log-levels), [java](https://github.com/exercism/java/tree/main/exercises/concept/log-levels), [python](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) (wip) | -| **land-grab-in-space** | structs | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/land-grab-in-space) | -| **football-match-reports** | switch-statements, switch-statement | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/football-match-reports), [java](https://github.com/exercism/java/tree/main/exercises/concept/football-match-reports) | -| **beauty-salon-goes-global** | time | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/beauty-salon-goes-global) | -| **phone-number-analysis** | tuples | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/phone-number-analysis) | -| **instruments-of-texas** | user-defined-exceptions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/instruments-of-texas) | -| **log-analysis** | extension-methods | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/log-analysis) | -| **strings** | strings, strings-basic | [cpp](https://github.com/exercism/cpp/tree/main/exercises/concept/strings) (wip), [dart](https://github.com/exercism/dart/tree/main/exercises/concept/strings) (wip) | -| **elyses-destructured-enchantments** | sequential-destructuring, array-destructuring | [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/elyses-destructured-enchantments), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-destructured-enchantments) | -| **date-parser** | , regular-expressions | [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/date-parser) (wip), [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/date-parser) | -| **socks-and-sexprs** | comments | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/socks-and-sexprs) | -| **key-comparison** | equality | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/key-comparison) | -| **pizza-pi** | integers | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/pizza-pi) | -| **leslies-lists** | lists | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/leslies-lists) | -| **pal-picker** | conditionals | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/pal-picker) | -| **lillys-lasagna** | functions | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lillys-lasagna) | -| **lillys-lasagna-leftovers** | lambda-list | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lillys-lasagna-leftovers) | -| **character-study** | characters | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/character-study) | -| **larrys-winning-checker** | arrays | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/larrys-winning-checker) | -| **high-scores** | hash-tables | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/high-scores) | -| **lucys-magnificent-mapper** | mapping | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lucys-magnificent-mapper) | -| **gigasecond-anniversary** | date-time | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/gigasecond-anniversary) | -| **logans-numeric-partition** | reducing | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/logans-numeric-partition) | -| **reporting-for-duty** | format-basics | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/reporting-for-duty) | -| **futures** | futures | [dart](https://github.com/exercism/dart/tree/main/exercises/concept/futures) (wip) | -| **numbers** | numbers-basic | [dart](https://github.com/exercism/dart/tree/main/exercises/concept/numbers) (wip) | -| **lasagna** | basics, functions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/lasagna), [go](https://github.com/exercism/go/tree/main/exercises/concept/lasagna), [java](https://github.com/exercism/java/tree/main/exercises/concept/lasagna), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lasagna), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/lasagna) (wip), [nim](https://github.com/exercism/nim/tree/main/exercises/concept/lasagna) (wip), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/lasagna), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/lasagna) | -| **pacman-rules** | booleans | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/pacman-rules) | -| **freelancer-rates** | integers, numbers | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/freelancer-rates), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/freelancer-rates), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/freelancer-rates) | -| **secrets** | anonymous-functions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/secrets) | -| **log-level** | cond | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/log-level) | -| **language-list** | lists | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/language-list) | -| **guessing-game** | multiple-clause-functions, pattern-matching | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/guessing-game), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/guessing-game) | -| **kitchen-calculator** | tuples | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/kitchen-calculator) | -| **high-school-sweetheart** | strings | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/high-school-sweetheart) | -| **bird-count** | recursion, arrays | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/bird-count), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/bird-count) | -| **high-score** | module-attributes-as-constants, dictionaries | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/high-score), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/high-score) (wip) | -| **city-office** | docs | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/city-office) | -| **german-sysadmin** | charlists | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/german-sysadmin) | -| **rpg-character-sheet** | io | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpg-character-sheet) | -| **name-badge** | nil, nothingness | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/name-badge), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/name-badge) (wip) | -| **take-a-number** | processes | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/take-a-number) | -| **wine-cellar** | keyword-lists | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/wine-cellar) | -| **dna-encoding** | bitstrings | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/dna-encoding) | -| **library-fees** | dates-and-time | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/library-fees) | -| **basketball-website** | access-behaviour | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/basketball-website) | -| **boutique-inventory** | enum, advanced-enumeration | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/boutique-inventory), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/boutique-inventory) | -| **file-sniffer** | binaries | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/file-sniffer) | -| **newsletter** | file | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/newsletter) | -| **chessboard** | ranges, range-iteration | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/chessboard), [go](https://github.com/exercism/go/tree/main/exercises/concept/chessboard) | -| **remote-control-car** | structs | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/remote-control-car) | -| **boutique-suggestions** | list-comprehensions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/boutique-suggestions) | -| **community-garden** | agent | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/community-garden) | -| **bread-and-potions** | protocols | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/bread-and-potions) | -| **captains-log** | erlang-libraries | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/captains-log) | -| **rpn-calculator** | errors, vec-stack | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/rpn-calculator) | -| **stack-underflow** | exceptions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/stack-underflow) | -| **rpn-calculator-output** | try-rescue-else-after | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator-output) (wip) | -| **rpn-calculator-inspection** | links | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator-inspection) | -| **lucas-numbers** | streams | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/lucas-numbers) | -| **mensch-aergere-dich-nicht** | | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/mensch-aergere-dich-nicht) (deprecated) | -| **new-passport** | with | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/new-passport) | -| **top-secret** | ast | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/top-secret) | -| **dancing-dots** | behaviours | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/dancing-dots) | -| **take-a-number-deluxe** | genserver | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/take-a-number-deluxe) | -| **bettys-bike-shop** | basics-2 | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/bettys-bike-shop) | -| **bandwagoner** | records | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/bandwagoner), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/bandwagoner) | -| **valentines-day** | custom-types, discriminated-unions | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/valentines-day), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/valentines-day) | -| **tisbury-treasure-hunt** | tuples | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/tisbury-treasure-hunt), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/tisbury-treasure-hunt), [python](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | -| **role-playing-game** | maybe, option | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/role-playing-game), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/role-playing-game) | -| **go** | result | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/go) | -| **ticket-please** | pattern-matching | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/ticket-please) | -| **luigis-luscious-lasagna** | let | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/luigis-luscious-lasagna) | -| **top-scorers** | dict | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/top-scorers) | -| **pizza-pricing** | recursion | [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/pizza-pricing) | -| **party-robot** | string-formatting | [go](https://github.com/exercism/go/tree/main/exercises/concept/party-robot) | -| **weather-forecast** | comments | [go](https://github.com/exercism/go/tree/main/exercises/concept/weather-forecast) | -| **savings-account** | constants, loops | [go](https://github.com/exercism/go/tree/main/exercises/concept/savings-account) (wip), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/savings-account) | -| **blackjack** | conditionals-switch, conditionals-if | [go](https://github.com/exercism/go/tree/main/exercises/concept/blackjack), [java](https://github.com/exercism/java/tree/main/exercises/concept/blackjack) | -| **vehicle-purchase** | comparison, conditionals, booleans | [go](https://github.com/exercism/go/tree/main/exercises/concept/vehicle-purchase), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/vehicle-purchase), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/vehicle-purchase) (wip), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/vehicle-purchase) | -| **gross-store** | maps | [go](https://github.com/exercism/go/tree/main/exercises/concept/gross-store) | -| **lasagna-master** | functions, function-overloading | [go](https://github.com/exercism/go/tree/main/exercises/concept/lasagna-master), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lasagna-master), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/lasagna-master) | -| **deep-thought** | errors | [go](https://github.com/exercism/go/tree/main/exercises/concept/deep-thought) (wip) | -| **card-tricks** | slices | [go](https://github.com/exercism/go/tree/main/exercises/concept/card-tricks) | -| **sorting-room** | type-assertion | [go](https://github.com/exercism/go/tree/main/exercises/concept/sorting-room) | -| **the-farm** | interfaces | [go](https://github.com/exercism/go/tree/main/exercises/concept/the-farm) | -| **census** | zero-values | [go](https://github.com/exercism/go/tree/main/exercises/concept/census) | -| **welcome-to-tech-palace** | strings | [go](https://github.com/exercism/go/tree/main/exercises/concept/welcome-to-tech-palace) | -| **election-day** | pointers | [go](https://github.com/exercism/go/tree/main/exercises/concept/election-day) | -| **expenses** | first-class-functions | [go](https://github.com/exercism/go/tree/main/exercises/concept/expenses) | -| **animal-magic** | randomness | [go](https://github.com/exercism/go/tree/main/exercises/concept/animal-magic) | -| **meteorology** | stringers | [go](https://github.com/exercism/go/tree/main/exercises/concept/meteorology) | -| **temperature** | numbers | [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/temperature) | -| **karls-languages** | lists | [java](https://github.com/exercism/java/tree/main/exercises/concept/karls-languages) | -| **elons-toy-car** | classes | [java](https://github.com/exercism/java/tree/main/exercises/concept/elons-toy-car) | -| **salary-calculator** | ternary-operators | [java](https://github.com/exercism/java/tree/main/exercises/concept/salary-calculator) | -| **poetry-club-door-policy** | strings | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/poetry-club-door-policy) | -| **elyses-enchantments** | arrays, vectors | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-enchantments), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/elyses-enchantments) (wip) | -| **mixed-juices** | while-loops | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/mixed-juices) | -| **lucky-numbers** | type-conversion | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lucky-numbers) | -| **elyses-analytic-enchantments** | array-analysis, predicates | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-analytic-enchantments), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/elyses-analytic-enchantments) (wip) | -| **elyses-looping-enchantments** | array-loops | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-looping-enchantments) | -| **nullability** | | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/nullability) (deprecated) | -| **amusement-park** | null-undefined, instance-variables | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/amusement-park), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/amusement-park) | -| **pizza-order** | recursion | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/pizza-order) (wip) | -| **coordinate-transformation** | closures | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/coordinate-transformation) | -| **fruit-picker** | arrow-functions | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/fruit-picker) | -| **translation-service** | promises | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/translation-service) | -| **high-score-board** | objects, dictionaries | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/high-score-board), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/high-score-board) | -| **ozans-playlist** | sets | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/ozans-playlist) | -| **factory-sensors** | errors | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/factory-sensors) | -| **elyses-transformative-enchantments** | array-transformations | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-transformative-enchantments) | -| **custom-signs** | conditionals-ternary, characters | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/custom-signs), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/custom-signs) | -| **windowing-system** | classes, methods | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/windowing-system), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/windowing-system) | -| **documented-lasagna** | docstrings | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/documented-lasagna) (wip) | -| **tech-palace** | strings | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/tech-palace) (wip) | -| **annalyns-infiltration2** | randomness | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/annalyns-infiltration2) (wip) | -| **dnd-char** | kwdef | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/dnd-char) (wip) | -| **leap** | arithmetic-operators | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/leap) (wip) | -| **exercism-matrix** | matrices-introduction | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/exercism-matrix) (wip) | -| **encounters** | multiple-dispatch | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/encounters) (wip) | -| **emoji-times** | unicode-identifiers | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/emoji-times) (wip) | -| **stage-heralding** | regex | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/stage-heralding) (wip) | -| **fibonacci-iterator** | iterator-protocol | [julia](https://github.com/exercism/julia/tree/main/exercises/concept/fibonacci-iterator) (wip) | -| **booleans** | booleans | [purescript](https://github.com/exercism/purescript/tree/main/exercises/concept/booleans) (wip) | -| **guidos-gorgeous-lasagna** | basics | [python](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna) | -| **ghost-gobble-arcade-game** | bools | [python](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game) | -| **currency-exchange** | numbers | [python](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | -| **meltdown-mitigation** | conditionals | [python](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | -| **black-jack** | comparisons | [python](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | -| **little-sisters-essay** | string-methods | [python](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | -| **little-sisters-vocab** | strings | [python](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | -| **pretty-leaflet** | string-formatting | [python](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) (wip) | -| **card-games** | lists | [python](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | -| **chaitanas-colossal-coaster** | list-methods | [python](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | -| **making-the-grade** | loops | [python](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | -| **inventory-management** | dicts | [python](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | -| **cater-waiter** | sets | [python](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | -| **ellens-alien-game** | classes | [python](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | -| **plane-tickets** | generators | [python](https://github.com/exercism/python/tree/main/exercises/concept/plane-tickets) (wip) | -| **restaurant-rozalynn** | none | [python](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) (wip) | -| **Locomotive Engineer** | unpacking, and multiple assignment | [python](https://github.com/exercism/python/tree/main/exercises/concept/locomotive-engineer) | -| **amusement-park-improvements** | booleans | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/amusement-park-improvements) | -| **log-line-parser** | strings | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/log-line-parser) | -| **assembly-line** | numbers, integers | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/assembly-line), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/assembly-line) | -| **boutique-inventory-improvements** | ostruct | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/boutique-inventory-improvements) | -| **moviegoer** | ternary-operator | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/moviegoer) | -| **simple-calculator** | exceptions | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/simple-calculator) | -| **magazine-cutout** | entry-api | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/magazine-cutout) | -| **semi-structured-logs** | enums | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/semi-structured-logs) | -| **resistor-color** | external-crates | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/resistor-color) | -| **health-statistics** | methods | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/health-statistics) | -| **low-power-embedded-game** | tuples | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/low-power-embedded-game) | -| **short-fibonacci** | vec-macro | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/short-fibonacci) | -| **csv-builder** | string-vs-str | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/csv-builder) (wip) | -| **basics** | basics | [scala](https://github.com/exercism/scala/tree/main/exercises/concept/basics) (wip), [x86-64-assembly](https://github.com/exercism/x86-64-assembly/tree/main/exercises/concept/basics) (wip) | -| **bomb-defuser** | capturing | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/bomb-defuser) | -| **log-lines** | enumerations | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/log-lines) | -| **master-mixologist** | loops | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/master-mixologist) | -| **magician-in-training** | arrays | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/magician-in-training) | -| **pizza-slices** | optionals | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/pizza-slices) | -| **poetry-club** | importing | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/poetry-club) | -| **santas-helper** | tuples | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/santas-helper) | -| **secret-agent** | escaping-functions | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/secret-agent) | +(The table below is currently auto-generated every now and then with [this script](https://github.com/junedev/exercism-story-list-generator). No need to update manually.) + +| Exercise Slug | Concepts | Tracks | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **lasagna** | basics, variables, functions, integer-introduction, comments, constants, type-annotations | [abap](https://github.com/exercism/abap/tree/main/exercises/concept/lasagna) (wip), [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/lasagna), [go](https://github.com/exercism/go/tree/main/exercises/concept/lasagna), [java](https://github.com/exercism/java/tree/main/exercises/concept/lasagna), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lasagna), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/lasagna), [julia](https://github.com/exercism/julia/tree/main/exercises/concept/lasagna) (wip), [nim](https://github.com/exercism/nim/tree/main/exercises/concept/lasagna) (wip), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/lasagna), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/lasagna), [unison](https://github.com/exercism/unison/tree/main/exercises/concept/lasagna) (wip) | +| **bird-watcher** | arrays, for-loops, foreach-loops, vectors, increment-decrement | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/bird-watcher), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/bird-watcher), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/bird-watcher), [go](https://github.com/exercism/go/tree/main/exercises/concept/bird-watcher), [java](https://github.com/exercism/java/tree/main/exercises/concept/bird-watcher), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/bird-watcher) | +| **lucians-luscious-lasagna** | basics, basics-1, functions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/lucians-luscious-lasagna), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/lucians-luscious-lasagna), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/lucians-luscious-lasagna), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/lucians-luscious-lasagna), [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/lucians-luscious-lasagna), [kotlin](https://github.com/exercism/kotlin/tree/main/exercises/concept/lucians-luscious-lasagna) (wip), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/lucians-luscious-lasagna) | +| **annalyns-infiltration** | booleans | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/annalyns-infiltration), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/annalyns-infiltration), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/annalyns-infiltration), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/annalyns-infiltration), [go](https://github.com/exercism/go/tree/main/exercises/concept/annalyns-infiltration), [java](https://github.com/exercism/java/tree/main/exercises/concept/annalyns-infiltration), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/annalyns-infiltration), [kotlin](https://github.com/exercism/kotlin/tree/main/exercises/concept/annalyns-infiltration) (wip) | +| **secure-munchester-united** | casting | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/secure-munchester-united) | +| **squeaky-clean** | chars, string-builder | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/squeaky-clean), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/squeaky-clean) (wip), [java](https://github.com/exercism/java/tree/main/exercises/concept/squeaky-clean) | +| **elons-toys** | classes, methods | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/elons-toys), [go](https://github.com/exercism/go/tree/main/exercises/concept/elons-toys) | +| **authentication-system** | constants, defensive-copying, readonly-collections | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/authentication-system) | +| **need-for-speed** | constructors, alias, import, structs | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/need-for-speed), [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/need-for-speed), [go](https://github.com/exercism/go/tree/main/exercises/concept/need-for-speed), [java](https://github.com/exercism/java/tree/main/exercises/concept/need-for-speed) | +| **booking-up-for-beauty** | datetimes, time | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/booking-up-for-beauty), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/booking-up-for-beauty), [go](https://github.com/exercism/go/tree/main/exercises/concept/booking-up-for-beauty) | +| **international-calling-connoisseur** | dictionaries | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/international-calling-connoisseur), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/international-calling-connoisseur) (wip) | +| **logs-logs-logs** | enums, runes | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/logs-logs-logs), [go](https://github.com/exercism/go/tree/main/exercises/concept/logs-logs-logs) | +| **faceid-2** | equality, sets | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/faceid-2) | +| **calculator-conundrum** | exceptions, exception-handling | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/calculator-conundrum), [java](https://github.com/exercism/java/tree/main/exercises/concept/calculator-conundrum) (wip) | +| **the-weather-in-deather** | ternary-operators, expression-bodied-members, switch-expressions, throw-expressions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/the-weather-in-deather) | +| **attack-of-the-trolls** | attributes, flag-enums | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/attack-of-the-trolls) | +| **interest-is-interesting** | do-while-loops, floating-point-numbers, while-loops | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/interest-is-interesting), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/interest-is-interesting), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/interest-is-interesting), [go](https://github.com/exercism/go/tree/main/exercises/concept/interest-is-interesting) | +| **wizards-and-warriors** | inheritance | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/wizards-and-warriors), [java](https://github.com/exercism/java/tree/main/exercises/concept/wizards-and-warriors) | +| **hyper-optimized-telemetry** | integral-numbers | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyper-optimized-telemetry) | +| **remote-control-competition** | interfaces, ordering | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/remote-control-competition), [java](https://github.com/exercism/java/tree/main/exercises/concept/remote-control-competition) | +| **tracks-on-tracks-on-tracks** | generic-types, lists | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [elm](https://github.com/exercism/elm/tree/main/exercises/concept/tracks-on-tracks-on-tracks), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/tracks-on-tracks-on-tracks) | +| **wizards-and-warriors-2** | method-overloading, named-arguments, optional-parameters | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/wizards-and-warriors-2) | +| **red-vs-blue-darwin-style** | namespaces | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/red-vs-blue-darwin-style) | +| **remote-control-cleanup** | nested-types | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/remote-control-cleanup) | +| **tim-from-marketing** | nullability | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/tim-from-marketing) | +| **cars-assemble** | if-statements, numbers, conditionals, if-then-else-expressions, arithmetic-operators | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/cars-assemble), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/cars-assemble), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/cars-assemble), [go](https://github.com/exercism/go/tree/main/exercises/concept/cars-assemble), [java](https://github.com/exercism/java/tree/main/exercises/concept/cars-assemble) | +| **developer-privileges** | object-initializers | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/developer-privileges) | +| **hyperia-forex** | operator-overloading | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyperia-forex) | +| **hyperinflation-hits-hyperia** | overflow | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/hyperinflation-hits-hyperia) | +| **building-telemetry** | parameters | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/building-telemetry) | +| **weighing-machine** | properties | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/weighing-machine) | +| **roll-the-die** | randomness | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/roll-the-die) | +| **parsing-log-files** | regular-expressions | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/parsing-log-files), [go](https://github.com/exercism/go/tree/main/exercises/concept/parsing-log-files) | +| **object-relational-mapping** | resource-cleanup | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/object-relational-mapping) | +| **orm-in-one-go** | resource-lifetime | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/orm-in-one-go) | +| **high-school-sweethearts** | string-formatting, verbatim-strings | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/high-school-sweethearts) | +| **log-levels** | strings, enums | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/log-levels), [cpp](https://github.com/exercism/cpp/tree/main/exercises/concept/log-levels) (wip), [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/log-levels), [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/log-levels), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/log-levels), [java](https://github.com/exercism/java/tree/main/exercises/concept/log-levels), [python](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) (wip) | +| **land-grab-in-space** | structs | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/land-grab-in-space) | +| **football-match-reports** | switch-statements, switch-statement | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/football-match-reports), [java](https://github.com/exercism/java/tree/main/exercises/concept/football-match-reports) | +| **beauty-salon-goes-global** | time, timezone | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/beauty-salon-goes-global) | +| **phone-number-analysis** | tuples | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/phone-number-analysis) | +| **instruments-of-texas** | user-defined-exceptions, exception-filtering | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/instruments-of-texas) | +| **log-analysis** | extension-methods | [csharp](https://github.com/exercism/csharp/tree/main/exercises/concept/log-analysis) | +| **elyses-destructured-enchantments** | sequential-destructuring, array-destructuring, rest-and-spread | [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/elyses-destructured-enchantments), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-destructured-enchantments) | +| **date-parser** | | [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/date-parser) (wip) | +| **coordinate-transformation** | closures | [clojure](https://github.com/exercism/clojure/tree/main/exercises/concept/coordinate-transformation) (wip), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/coordinate-transformation) | +| **socks-and-sexprs** | comments, expressions, cons, symbols | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/socks-and-sexprs) | +| **key-comparison** | equality | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/key-comparison) | +| **pizza-pi** | integers, floating-point-numbers, arithmetic | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/pizza-pi) | +| **leslies-lists** | lists | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/leslies-lists) | +| **pal-picker** | conditionals, truthy-and-falsy | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/pal-picker) | +| **lillys-lasagna** | functions | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lillys-lasagna) | +| **lillys-lasagna-leftovers** | lambda-list, keyword-parameters, optional-parameters, rest-parameters | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lillys-lasagna-leftovers) | +| **character-study** | characters | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/character-study) | +| **larrys-winning-checker** | arrays, vectors | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/larrys-winning-checker) | +| **high-scores** | hash-tables | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/high-scores) | +| **lucys-magnificent-mapper** | mapping, filtering | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/lucys-magnificent-mapper) | +| **gigasecond-anniversary** | date-time, multiple-values | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/gigasecond-anniversary) | +| **logans-numeric-partition** | reducing | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/logans-numeric-partition) | +| **reporting-for-duty** | format-basics | [common-lisp](https://github.com/exercism/common-lisp/tree/main/exercises/concept/reporting-for-duty) | +| **futures** | futures | [dart](https://github.com/exercism/dart/tree/main/exercises/concept/futures) (wip) | +| **numbers** | numbers-basic, type-conversion | [dart](https://github.com/exercism/dart/tree/main/exercises/concept/numbers) (wip) | +| **strings** | strings-basic | [dart](https://github.com/exercism/dart/tree/main/exercises/concept/strings) (wip) | +| **pacman-rules** | booleans | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/pacman-rules), [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/pacman-rules) (wip), [unison](https://github.com/exercism/unison/tree/main/exercises/concept/pacman-rules) (wip) | +| **freelancer-rates** | integers, floating-point-numbers, numbers, arithmetic-operators | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/freelancer-rates), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/freelancer-rates), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/freelancer-rates) | +| **secrets** | anonymous-functions, bit-manipulation | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/secrets) | +| **log-level** | cond, atoms | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/log-level) | +| **language-list** | lists | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/language-list) | +| **guessing-game** | multiple-clause-functions, guards, default-arguments, pattern-matching, pattern-matching-literals | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/guessing-game), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/guessing-game), [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/guessing-game) | +| **kitchen-calculator** | tuples, pattern-matching | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/kitchen-calculator) | +| **high-school-sweetheart** | strings, pipe-operator | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/high-school-sweetheart) | +| **bird-count** | recursion, arrays, enumeration | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/bird-count), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/bird-count), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/bird-count) | +| **high-score** | module-attributes-as-constants, maps | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/high-score) | +| **city-office** | docs, typespecs | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/city-office) | +| **german-sysadmin** | charlists, case | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/german-sysadmin) | +| **rpg-character-sheet** | io | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpg-character-sheet) | +| **name-badge** | nil, if | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/name-badge) | +| **take-a-number** | processes, pids | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/take-a-number) | +| **wine-cellar** | keyword-lists | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/wine-cellar) | +| **dna-encoding** | bitstrings, tail-call-recursion | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/dna-encoding) | +| **library-fees** | dates-and-time | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/library-fees) | +| **basketball-website** | access-behaviour | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/basketball-website) | +| **boutique-inventory** | enum, advanced-enumeration | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/boutique-inventory), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/boutique-inventory) | +| **file-sniffer** | binaries | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/file-sniffer) | +| **newsletter** | file | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/newsletter) | +| **chessboard** | ranges, range-iteration, type-definitions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/chessboard), [go](https://github.com/exercism/go/tree/main/exercises/concept/chessboard) | +| **remote-control-car** | structs, functions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/remote-control-car), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/remote-control-car) | +| **boutique-suggestions** | list-comprehensions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/boutique-suggestions) | +| **community-garden** | agent | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/community-garden) | +| **bread-and-potions** | protocols | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/bread-and-potions) | +| **captains-log** | erlang-libraries, randomness | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/captains-log) | +| **rpn-calculator** | errors, try-rescue, vec-stack | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/rpn-calculator) | +| **stack-underflow** | exceptions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/stack-underflow) | +| **rpn-calculator-output** | try-rescue-else-after, dynamic-dispatch | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator-output) (wip) | +| **rpn-calculator-inspection** | links, tasks | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/rpn-calculator-inspection) | +| **lucas-numbers** | streams | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/lucas-numbers) | +| **new-passport** | with | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/new-passport) | +| **top-secret** | ast | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/top-secret) | +| **dancing-dots** | behaviours, use | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/dancing-dots) | +| **take-a-number-deluxe** | genserver | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/take-a-number-deluxe) | +| **log-parser** | regular-expressions | [elixir](https://github.com/exercism/elixir/tree/main/exercises/concept/log-parser) | +| **bettys-bike-shop** | basics-2 | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/bettys-bike-shop) | +| **bandwagoner** | records | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/bandwagoner), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/bandwagoner) | +| **valentines-day** | custom-types, discriminated-unions, algebraic-data-types | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/valentines-day), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/valentines-day), [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/valentines-day) | +| **tisbury-treasure-hunt** | tuples | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/tisbury-treasure-hunt), [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/tisbury-treasure-hunt), [python](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | +| **role-playing-game** | maybe, option | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/role-playing-game), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/role-playing-game) | +| **go** | result | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/go) | +| **ticket-please** | pattern-matching | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/ticket-please) | +| **marios-marvellous-lasagna** | let | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/marios-marvellous-lasagna) | +| **top-scorers** | dict | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/top-scorers) | +| **paolas-prestigious-pizza** | parsing | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/paolas-prestigious-pizza) | +| **monster-attack** | partial-application-composition | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/monster-attack) | +| **blorkemon-cards** | comparison | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/blorkemon-cards) | +| **gotta-snatch-em-all** | set | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/gotta-snatch-em-all) | +| **treasure-chest** | generics | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/treasure-chest) | +| **secure-treasure-chest** | opaque-types | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/secure-treasure-chest) | +| **treasure-factory** | phantom-types | [elm](https://github.com/exercism/elm/tree/main/exercises/concept/treasure-factory) | +| **pizza-pricing** | recursion | [fsharp](https://github.com/exercism/fsharp/tree/main/exercises/concept/pizza-pricing) | +| **party-robot** | string-formatting, packages | [go](https://github.com/exercism/go/tree/main/exercises/concept/party-robot) | +| **weather-forecast** | comments | [go](https://github.com/exercism/go/tree/main/exercises/concept/weather-forecast) | +| **savings-account** | constants, loops, modules | [go](https://github.com/exercism/go/tree/main/exercises/concept/savings-account) (wip), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/savings-account) | +| **blackjack** | conditionals-switch, conditionals-if | [go](https://github.com/exercism/go/tree/main/exercises/concept/blackjack), [java](https://github.com/exercism/java/tree/main/exercises/concept/blackjack) | +| **vehicle-purchase** | comparison, conditionals-if, conditionals, compare, booleans, conditionals-guard, conditionals-switch, ternary-operator | [go](https://github.com/exercism/go/tree/main/exercises/concept/vehicle-purchase), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/vehicle-purchase), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/vehicle-purchase), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/vehicle-purchase) | +| **gross-store** | maps | [go](https://github.com/exercism/go/tree/main/exercises/concept/gross-store) | +| **lasagna-master** | functions, function-overloading, multiple-return-values, default-parameters, variadic-parameters, inout-parameters, nested-functions | [go](https://github.com/exercism/go/tree/main/exercises/concept/lasagna-master), [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lasagna-master), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/lasagna-master) | +| **card-tricks** | slices, variadic-functions | [go](https://github.com/exercism/go/tree/main/exercises/concept/card-tricks) | +| **sorting-room** | type-assertion, type-conversion | [go](https://github.com/exercism/go/tree/main/exercises/concept/sorting-room) | +| **airport-robot** | interfaces | [go](https://github.com/exercism/go/tree/main/exercises/concept/airport-robot) | +| **the-farm** | errors | [go](https://github.com/exercism/go/tree/main/exercises/concept/the-farm) | +| **census** | zero-values | [go](https://github.com/exercism/go/tree/main/exercises/concept/census) | +| **welcome-to-tech-palace** | strings, strings-package | [go](https://github.com/exercism/go/tree/main/exercises/concept/welcome-to-tech-palace) | +| **election-day** | pointers | [go](https://github.com/exercism/go/tree/main/exercises/concept/election-day) | +| **expenses** | first-class-functions | [go](https://github.com/exercism/go/tree/main/exercises/concept/expenses) | +| **animal-magic** | randomness | [go](https://github.com/exercism/go/tree/main/exercises/concept/animal-magic) | +| **meteorology** | stringers | [go](https://github.com/exercism/go/tree/main/exercises/concept/meteorology) | +| **temperature** | numbers | [haskell](https://github.com/exercism/haskell/tree/main/exercises/concept/temperature) | +| **karls-languages** | lists, generic-types | [java](https://github.com/exercism/java/tree/main/exercises/concept/karls-languages) | +| **elons-toy-car** | classes | [java](https://github.com/exercism/java/tree/main/exercises/concept/elons-toy-car) | +| **salary-calculator** | ternary-operators | [java](https://github.com/exercism/java/tree/main/exercises/concept/salary-calculator) | +| **poetry-club-door-policy** | strings | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/poetry-club-door-policy) | +| **elyses-enchantments** | arrays | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-enchantments) | +| **mixed-juices** | while-loops, conditionals-switch | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/mixed-juices) | +| **lucky-numbers** | type-conversion | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/lucky-numbers) | +| **elyses-analytic-enchantments** | arrow-functions, array-analysis | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-analytic-enchantments) | +| **elyses-looping-enchantments** | array-loops | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-looping-enchantments) | +| **amusement-park** | null-undefined, instance-variables, nil | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/amusement-park), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/amusement-park) | +| **pizza-order** | recursion | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/pizza-order) (wip) | +| **fruit-picker** | callbacks | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/fruit-picker) | +| **translation-service** | promises | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/translation-service) | +| **high-score-board** | objects, dictionaries | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/high-score-board), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/high-score-board), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/high-score-board) | +| **ozans-playlist** | sets | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/ozans-playlist) | +| **factory-sensors** | errors, inheritance | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/factory-sensors) | +| **elyses-transformative-enchantments** | array-transformations | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/elyses-transformative-enchantments) | +| **custom-signs** | conditionals-ternary, template-strings, characters | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/custom-signs), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/custom-signs) | +| **windowing-system** | classes, methods, self, structs-and-classes, value-and-reference-types | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/windowing-system), [swift](https://github.com/exercism/swift/tree/main/exercises/concept/windowing-system) | +| **regular-chatbot** | regular-expressions | [javascript](https://github.com/exercism/javascript/tree/main/exercises/concept/regular-chatbot), [jq](https://github.com/exercism/jq/tree/main/exercises/concept/regular-chatbot) | +| **shopping** | basics | [jq](https://github.com/exercism/jq/tree/main/exercises/concept/shopping) | +| **assembly-line** | numbers, floating-point-numbers, conditionals, integers | [jq](https://github.com/exercism/jq/tree/main/exercises/concept/assembly-line), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/assembly-line), [rust](https://github.com/exercism/rust/tree/main/exercises/concept/assembly-line) | +| **log-line-parser** | strings | [jq](https://github.com/exercism/jq/tree/main/exercises/concept/log-line-parser), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/log-line-parser) | +| **grade-stats** | reduce | [jq](https://github.com/exercism/jq/tree/main/exercises/concept/grade-stats) | +| **recursive-functions** | recursion | [jq](https://github.com/exercism/jq/tree/main/exercises/concept/recursive-functions) | +| **booleans** | booleans | [purescript](https://github.com/exercism/purescript/tree/main/exercises/concept/booleans) (wip) | +| **guidos-gorgeous-lasagna** | basics | [python](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna) | +| **ghost-gobble-arcade-game** | bools | [python](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game) | +| **currency-exchange** | numbers | [python](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | +| **meltdown-mitigation** | conditionals | [python](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | +| **black-jack** | comparisons | [python](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | +| **little-sisters-essay** | string-methods | [python](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | +| **little-sisters-vocab** | strings | [python](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | +| **pretty-leaflet** | string-formatting | [python](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) (wip) | +| **card-games** | lists | [python](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | +| **chaitanas-colossal-coaster** | list-methods | [python](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | +| **making-the-grade** | loops | [python](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | +| **inventory-management** | dicts | [python](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | +| **locomotive-engineer** | unpacking-and-multiple-assignment, multiple-assignment-and-decomposition | [python](https://github.com/exercism/python/tree/main/exercises/concept/locomotive-engineer), [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/locomotive-engineer) | +| **cater-waiter** | sets | [python](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | +| **ellens-alien-game** | classes | [python](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | +| **plane-tickets** | generators | [python](https://github.com/exercism/python/tree/main/exercises/concept/plane-tickets) (wip) | +| **restaurant-rozalynn** | none | [python](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) (wip) | +| **amusement-park-improvements** | booleans | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/amusement-park-improvements) | +| **boutique-inventory-improvements** | ostruct | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/boutique-inventory-improvements) | +| **moviegoer** | ternary-operator | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/moviegoer) | +| **simple-calculator** | exceptions | [ruby](https://github.com/exercism/ruby/tree/main/exercises/concept/simple-calculator) | +| **magazine-cutout** | entry-api | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/magazine-cutout) | +| **semi-structured-logs** | enums | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/semi-structured-logs) | +| **resistor-color** | external-crates | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/resistor-color) | +| **health-statistics** | methods, structs | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/health-statistics) | +| **low-power-embedded-game** | tuples, destructuring | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/low-power-embedded-game) | +| **short-fibonacci** | vec-macro | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/short-fibonacci) | +| **csv-builder** | string-vs-str | [rust](https://github.com/exercism/rust/tree/main/exercises/concept/csv-builder) (wip) | +| **basics** | basics | [scala](https://github.com/exercism/scala/tree/main/exercises/concept/basics) (wip), [x86-64-assembly](https://github.com/exercism/x86-64-assembly/tree/main/exercises/concept/basics) (wip) | +| **bomb-defuser** | capturing, closures, shorthand-arguments, trailing-closures | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/bomb-defuser) | +| **log-lines** | enumerations | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/log-lines) | +| **master-mixologist** | loops, for-loops, while-loops, repeat-while, control-transfer | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/master-mixologist) | +| **magician-in-training** | arrays | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/magician-in-training) | +| **pizza-slices** | optionals | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/pizza-slices) | +| **poetry-club** | importing, string-components, string-indexing | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/poetry-club) | +| **santas-helper** | tuples | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/santas-helper) | +| **secret-agent** | escaping-functions, higher-order-functions | [swift](https://github.com/exercism/swift/tree/main/exercises/concept/secret-agent) | From 038ecc86bcaea803eb6356860aa0d78386804273 Mon Sep 17 00:00:00 2001 From: Ethan Hansen <1ethanhansen@protonmail.com> Date: Tue, 25 Apr 2023 04:21:29 -0400 Subject: [PATCH 009/129] fix: add missing info about docs/config.json (#429) * fix: add missing info about docs/config.json * Apply suggestions from code review Co-authored-by: Kim Hallberg * Apply suggestions from code review * Update building/tracks/docs.md Co-authored-by: Kim Hallberg * Update docs.md * Update building/tracks/docs.md Co-authored-by: Erik Schierboom * Update building/tracks/docs.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> * Update building/tracks/docs.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --------- Co-authored-by: Jeremy Walker Co-authored-by: Kim Hallberg Co-authored-by: Erik Schierboom Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- building/tracks/docs.md | 53 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/building/tracks/docs.md b/building/tracks/docs.md index 01d64c65..4f994e12 100644 --- a/building/tracks/docs.md +++ b/building/tracks/docs.md @@ -10,7 +10,8 @@ docs ├── SNIPPET.txt (required) ├── REPRESENTER_NORMALIZATIONS.md (optional) ├── RESOURCES.md (required) -└── TESTS.md (required) +├── TESTS.md (required) +└── config.json (required) ``` --- @@ -317,3 +318,53 @@ dotnet add package [package-name] ```` Check [this page](https://exercism.org/docs/tracks/fsharp/tests) to see what this looks like when rendered. + +## File: `config.json` + +**Purpose:** Tell the website what pages are available. + +**Displayed on:** (not displayed) + +### Example + +```json +{ + "docs": [ + { + "uuid": "410d95ba-6294-4f40-af4a-6bb052977a3e", + "slug": "installation", + "path": "docs/INSTALLATION.md", + "title": "Installing F# locally", + "blurb": "Learn how to install F# locally to solve Exercism's exercises on your own machine" + }, + { + "uuid": "e0862775-d757-4d21-826f-b7cc2640ae8f", + "slug": "learning", + "path": "docs/LEARNING.md", + "title": "How to learn F#", + "blurb": "An overview of how to get started from scratch with F#" + }, + { + "uuid": "bca11b18-be81-468a-a34e-f5e6ac16bfd6", + "slug": "tests", + "path": "docs/TESTS.md", + "title": "Testing on the F# track", + "blurb": "Learn how to test your F# exercises on Exercism" + }, + { + "uuid": "39a578fe-e4ae-479e-b648-733882e244ef", + "slug": "resources", + "path": "docs/RESOURCES.md", + "title": "Useful F# resources", + "blurb": "A collection of useful resources to help you master F#" + }, + { + "uuid": "edfbb0fa-a950-4384-899f-6c6c7a440dca", + "slug": "representer-normalizations", + "path": "docs/REPRESENTER_NORMALIZATIONS.md", + "title": "F# representer normalizations", + "blurb": "An overview of the normalizations the F# representer applies to solutions" + } + ] +} +``` From c9b1c13d253f000416ca8cf4b71fd829895e5757 Mon Sep 17 00:00:00 2001 From: Safwan Samsudeen <62411302+safwansamsudeen@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:53:00 +0530 Subject: [PATCH 010/129] General correction of language, style, and wrong links (#436) * correction of language style and wrong links * revert changes based on review * change based on ee7's review * change url based on ee7's review * Update pull-request-guide on erik's review Co-authored-by: Erik Schierboom --------- Co-authored-by: Erik Schierboom --- building/configlet/lint.md | 30 ++++++++++--------- .../github/contributors-pull-request-guide.md | 15 +++++----- building/github/gha-best-practices.md | 24 +++++++++------ .../github/maintainers-pull-request-guide.md | 2 +- building/tooling/analyzers/docker.md | 2 +- .../tooling/analyzers/feedback-guidelines.md | 4 +-- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/building/configlet/lint.md b/building/configlet/lint.md index 1280aab1..7bd07595 100644 --- a/building/configlet/lint.md +++ b/building/configlet/lint.md @@ -526,24 +526,26 @@ The `config.json` file should have the following checks: ## Glossary -1. Non-blank string: a string that contains at least one non-whitespace character. -2. kebab-case string: a string that contains only characters in the range `[a-z0-9]`, optionally separated by dashes (e.g. "two-fer"). It must match the regular expression: `^[a-z0-9]+(-[a-z0-9]+)*$` -3. Title Case string: a non-blank string that follows the below guidelines (from the Chicago Manual of Style, see https://en.wikipedia.org/wiki/Title_case): - > - Capitalize the first and last words of titles and subtitles. - > - Capitalize "major" words (nouns, pronouns, verbs, adjectives, adverbs, and some conjunctions). - > - Lowercase the conjunctions _and_, _but_, _for_, _or_, and _nor_. - > - Lowercase the articles _the_, _a_, and _an_. - > - Lowercase prepositions, regardless of length, except when they are stressed, are used adverbially or adjectivally, or are used as conjunctions. - > - Lowercase the words _to_ and _as_. - > - Lowercase the second part of Latin species names. -4. Sentence Case string: a non-blank string that follows the below guidelines (see https://en.wikipedia.org/wiki/Letter_case#Sentence_case): - > - Capitalize the first word of the sentence, as well as proper nouns and other words as required by a more specific rule. -5. Valid `"files"` pattern: A non-blank string¹ that specifies a location of a file used in an exercise, relative to the exercise's directory. A pattern may use one of the following placeholders: +1. **Non-blank string**: a string that contains at least one non-whitespace character. +2. **kebab-case string**: a string that contains only characters in the range `[a-z0-9]`, optionally separated by dashes (e.g. "two-fer"). It must match the regular expression: `^[a-z0-9]+(-[a-z0-9]+)*$` +3. **Title Case string**: a non-blank string that follows the below [guidelines from the Chicago Manual of Style](https://en.wikipedia.org/wiki/Title_case#Chicago_Manual_of_Style): + - Capitalize the first and last words of titles and subtitles. + - Capitalize "major" words (nouns, pronouns, verbs, adjectives, adverbs, and some conjunctions). + - Lowercase the conjunctions _and_, _but_, _for_, _or_, and _nor_. + - Lowercase the articles _the_, _a_, and _an_. + - Lowercase prepositions, regardless of length, except when they are stressed, are used adverbially or adjectivally, or are used as conjunctions. + - Lowercase the words _to_ and _as_. + - Lowercase the second part of Latin species names. +4. **Sentence Case string**: a non-blank string that follows the below [guidelines](https://en.wikipedia.org/wiki/Letter_case#Sentence_case): + - Capitalize the first word of the sentence, as well as proper nouns and other words as required by a more specific rule. +5. **Valid `files` pattern**: A non-blank string¹ that specifies a location of a file used in an exercise, relative to the exercise's directory. A pattern may use one of the following placeholders: + - `%{kebab_slug}`: the `kebab-case` exercise slug (e.g. `bit-manipulation`) - `%{snake_slug}`: the `snake_case` exercise slug (e.g. `bit_manipulation`) - `%{camel_slug}`: the `camelCase` exercise slug (e.g. `bitManipulation`) - `%{pascal_slug}`: the `PascalCase` exercise slug (e.g. `BitManipulation`) -6. Unique version 4 UUID string: A string that satisfies all of these conditions: +6. **Unique version 4 UUID string**: A string that satisfies all of these conditions: + - It only exists once in the track-level `config.json` file of a given Exercism track - It does not exist in the track-level `config.json` file of any other Exercism track - It does not exist in any `canonical-data.json` file in https://github.com/exercism/problem-specifications diff --git a/building/github/contributors-pull-request-guide.md b/building/github/contributors-pull-request-guide.md index fd3075a6..b1512eb3 100644 --- a/building/github/contributors-pull-request-guide.md +++ b/building/github/contributors-pull-request-guide.md @@ -1,33 +1,34 @@ # The Contributors' Guide to Pull Requests Thank you for wanting to contribute to Exercism! -Before creating a pull request, please read the [how to make a great Pull Request][how-to-make-a-great-pr] guide which aims to help you with the non-technical side of pull requests so that you have a great experience and help our maintaining team at the same time. +Before creating a pull request, please read the [how to make a great pull request][how-to-make-a-great-pr] guide which aims to help you with the non-technical side of pull requests so that you have a great experience and help our maintaining team at the same time. This documents contains some additional, Exercism-specific guidelines for different types of pull requests. ## Exercise Pull Requests Creating a pull request for a Concept Exercise or Practice Exercise can be daunting with the many rules around these types of exercises. -For this reason, it can take a maintainer 2 to 3 hours to review and can result in dozens of comments. - +For this reason, it can take a maintainer two to three hours to review and can result in dozens of comments. To help you, there'll be one primary reviewer commenting on your pull request. Don't be discouraged by the number of review comments; it is perfectly normal for an exercise to go through several rewrites before arriving at something both you and the primary reviewer are happy with. +## Recommendations + Here are some recommendations to help streamline the process even more. -### Recommendation: read the documentation +### Read the documentation Before creating a pull request for an exercise, be sure to read the [concept exercise documentation][concept-exercises] respectively [practice exercise documentation][practice-exercises]. Reading the documentation beforehand helps immensely, as reviews will have less comments and your pull request will be merged much sooner. -### Recommendation: examine existing exercises (if any) +### Examine existing exercises (if any) If the track has any existing exercises, take the time to study them a bit to discover what they look like and how they're structured. ## Practice Exercise Pull Requests -### Recommendation: consider whether the change actually belongs in problems-specifications +### Consider whether the change actually belongs in problems-specifications Some of the contents of a Practice Exercise (such as its introduction) comes from its (shared) metadata as defined in the [problems-specifications repo][problem-specifications]. If you're intending to update such content, consider whether the change might benefit other tracks too. @@ -35,7 +36,7 @@ If so, please open a pull request to the exercise's metadata in the [problems-sp ## General recommendations -### Recommendation: refrain from doing unsolicited reviews +### Refrain from doing unsolicited reviews All pull request reviews are done by one (or more) maintainers of the track, as they are responsible for signing off all changes to the repository. Maintainers doing the review also helps to avoid conflicting feedback for the pull request author. diff --git a/building/github/gha-best-practices.md b/building/github/gha-best-practices.md index fbba15ae..84b895f1 100644 --- a/building/github/gha-best-practices.md +++ b/building/github/gha-best-practices.md @@ -3,15 +3,21 @@ This _working_ document serves as a collection of best practices for making use of GitHub Actions. If you have any suggestions or additions, [please open a pull request on GitHub!](https://github.com/exercism/docs/edit/main/building/github/gha-best-practices.md) -- [Collection of Best Practices](#collection-of-best-practices) - - [Set timeouts for workflows](#set-timeouts-for-workflows) - - [Consider if (third-party) actions are really needed](#consider-if-third-party-actions-are-really-needed) - - [Limit scope of workflow token](#limit-scope-of-workflow-token) - - [Pin actions to SHAs](#pin-actions-to-shas) - - [Consider setting up a concurrency strategy](#consider-setting-up-a-concurrency-strategy) - - [Consider which triggers are really needed](#consider-which-triggers-are-really-needed) - - [Read the "Security hardening for GitHub Actions" guide](#read-the-security-hardening-for-github-actions-guide) -- [Workflow Checklist](#workflow-checklist) +- [GitHub Actions: Best Practices](#github-actions-best-practices) + - [Collection of Best Practices](#collection-of-best-practices) + - [Set timeouts for workflows](#set-timeouts-for-workflows) + - [Example](#example) + - [Consider if (third-party) actions are really needed](#consider-if-third-party-actions-are-really-needed) + - [Limit scope of workflow token](#limit-scope-of-workflow-token) + - [Example](#example-1) + - [Pin actions to SHAs](#pin-actions-to-shas) + - [Finding the commit SHA](#finding-the-commit-sha) + - [Example](#example-2) + - [Consider setting up a concurrency strategy](#consider-setting-up-a-concurrency-strategy) + - [Example](#example-3) + - [Consider which triggers are really needed](#consider-which-triggers-are-really-needed) + - [Read the "Security hardening for GitHub Actions" guide](#read-the-security-hardening-for-github-actions-guide) + - [Workflow Checklist](#workflow-checklist) ## Collection of Best Practices diff --git a/building/github/maintainers-pull-request-guide.md b/building/github/maintainers-pull-request-guide.md index 4e4c8a1c..0a29651b 100644 --- a/building/github/maintainers-pull-request-guide.md +++ b/building/github/maintainers-pull-request-guide.md @@ -6,7 +6,7 @@ This document contains some Exercism-specific pull request review guidelines. ## Reviewing Exercise Pull Requests Reviewing a pull request for a Concept Exercise or Practice Exercise can be daunting with the many rules around these types of exercise. -For this reason, a first-pass review by a maintainer often take 2 to 3 hours and results in dozens of comments. +For this reason, a first-pass review by a maintainer often takes two to three hours and results in dozens of comments. For Concept Exercises, there are also files with similar goals/contents (e.g. the exercise and concept introduction), where focusing on getting one of those perfect first is essential before branching out too far. To help streamline this workflow, we've developed the following recommendations. diff --git a/building/tooling/analyzers/docker.md b/building/tooling/analyzers/docker.md index c6872b83..71a357a0 100644 --- a/building/tooling/analyzers/docker.md +++ b/building/tooling/analyzers/docker.md @@ -2,7 +2,7 @@ Our Analyzers are deployed as Docker images. -Please read the [general Tooling docker information](/docs/building/tooling/analyzers/docker) to familiarize yourself with how these work. +Please read the [general tooling Docker information](/docs/building/tooling/docker) to familiarize yourself with how these work. When we run the Analyzer's container we execute a `run.sh` script. To ensure this works properly the following rules must be following: diff --git a/building/tooling/analyzers/feedback-guidelines.md b/building/tooling/analyzers/feedback-guidelines.md index bb9dd2df..232af7d7 100644 --- a/building/tooling/analyzers/feedback-guidelines.md +++ b/building/tooling/analyzers/feedback-guidelines.md @@ -4,8 +4,8 @@ _NOTE: This spec is currently being updated._ The goal of a language track on Exercism is to give people a way to achieve a high level of [fluency](/docs/using/product/fluency) at a low level of proficiency. We're aiming for fluency -in the syntax, idioms, and the standard library of the language. You can read -more about the [goal of exercism here](https://github.com/exercism/docs/blob/master/about/goal-of-exercism.md). +in the syntax, idioms, and the standard library of the language. Read +more about the [goal of Exercism here](https://github.com/exercism/docs/blob/master/about/goal-of-exercism.md). ## Definitions From 9f5240a8740567235799da12a513d39e19b4f100 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Mon, 8 May 2023 20:09:50 +0200 Subject: [PATCH 011/129] Replace links to kytrinyx.com (#443) This website is not mine anymore. Also replace link to Overkill, since RubyConf PT took down their youtube channel, as well as linking to a more recent version of the other talk (with the name changed). --- using/faqs.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/using/faqs.md b/using/faqs.md index 6cf35bad..1c7748ae 100644 --- a/using/faqs.md +++ b/using/faqs.md @@ -2,7 +2,7 @@ ## Who is behind Exercism? -Exercism's two co-founders are [Katrina Owen][katrina-owen] and [Jeremy Walker][jeremy-walker]. +Exercism's two co-founders are [Katrina Owen][kytrinyx] and [Jeremy Walker][jeremy-walker]. Exercism was originally founded by [Katrina][kytrinyx], a polyglot developer and winner of the "Ruby Hero" award who accidentally became a developer while pursuing a degree in molecular biology. She began nitpicking code back in 2006 while volunteering at JavaRanch, and got hooked. @@ -26,7 +26,7 @@ The way Katrina came to think about each exercise is that they are small, trivia However, when it comes to solving an exercise, the devil is often in the details. In other words, that simple exercise is suddenly more challenging once you think about the finer details. -To learn more about what it means for the devil to be in the details, check out the [Overkill][overkill] and [Succession][succession] talks by Katrina. +To learn more about what it means for the devil to be in the details, check out the [Here Be Dragons][here-be-dragons] and [One Small Step][one-small-step] talks by Katrina. ## The Basics @@ -192,8 +192,7 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [interactive-walkthrough]: https://exercism.org/cli-walkthrough [jeremy-walker]: https://ihid.info [kaido]: https://kaido.org -[katrina-owen]: http://www.kytrinyx.com/ -[kytrinyx]: http://www.kytrinyx.com/ +[kytrinyx]: https://exercism.github.io/kytrinyx/ [languages]: https://github.com/search?q=topic%3Aexercism-track+org%3Aexercism&type=Repositories [learning-mode]: /docs/building/product/unlocking-exercises#h-learning-mode-vs-practice-mode @@ -201,10 +200,10 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [new-cli-issue]: https://github.com/exercism/CLI/issues/new [new-language-request]: https://github.com/exercism/generic-track/blob/main/README.md [opening-an-issue]: #opening-an-issue -[overkill]: http://www.kytrinyx.com/talks/overkill +[here-be-dragons]: https://www.youtube.com/watch?v=QAUHYzC9kFM [personal-settings]: https://exercism.org/my/settings/preferences/edit [settings]: https://exercism.org/my/settings -[succession]: http://www.kytrinyx.com/talks/succession +[one-small-step]: https://www.youtube.com/watch?v=JXCJL4IJUhQ [switching-modes]: /docs/building/product/unlocking-exercises#h-switching-modes [unlocking-exercises]: /docs/building/product/unlocking-exercises [upgrade-cli]: https://github.com/exercism/website-copy/blob/main/pages/cli_v1_to_v2.md From 51f1439e7f26c2176435193f049cd2379ce14f74 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Tue, 4 Jul 2023 14:45:35 +0100 Subject: [PATCH 012/129] Remove outdated documentation (#446) As discussed on Discord this documentation was no longer relevant. --- building/tooling/analyzers/interface.md | 149 ------------------------ 1 file changed, 149 deletions(-) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index e769b693..a8996441 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -80,152 +80,3 @@ However, in the future we may choose to add emojis or indicators to other types, The contents of `stdout` and `stderr` from each run will be persisted into files that can be viewed later. You may write an `analysis.out` file that contains debugging information you want to later view. - ---- - -**NOTE: Everything below this line is currently changing.** - -# LEGACY: Comments - -Exercism is responsible for the display and communication of comments. The analyzer's job is purely to provide functional comments. Please follow these guidelines: - -- Comments should be actionable. The user should understand the action they need to undertake. -- While friendly, they should not try and pretend to be a human and should not contain greetings, etc. -- The solution should not act like the start of a discussion. - -- A good comment would be "This solution may be improved by doing XYZ". -- A bad comment would be "Hello. Have you thought about doing XYZ?". - -# LEGACY: Feedback guidelines - -The goal of a language track on Exercism is to give people a way to achieve a -high level of [fluency](/docs/using/product/fluency) at a low level of proficiency. We're aiming for fluency -in the syntax, idioms, and the standard library of the language. You can read -more about the [goal of exercism here](https://github.com/exercism/docs/blob/main/about/goal-of-exercism.md). - -## Definitions - -In the following paragraphs, keywords such as **MUST**, **SHOULD**, **MAY** -are to be interpreted as in [RFC2119](https://www.ietf.org/rfc/rfc2119.txt); -given that we recognize the following four output states and their restrictions: - -- `approve`: **MUST** be an approvable solution, **MAY** be with comment. -- `disapprove`: **MUST** be with comment -- `refer_to_mentor`: **MAY** be with comment - -### Why is it not ... (e.g. **MUST**)? - -Per [RFC2119](https://www.ietf.org/rfc/rfc2119.txt), if **MUST** is used, it is -a guarantee that the rule is always so, and does not need to be guarded for. For -example, **MUST** be without comment means that the website could crash if an -analyzer sends a comment anyway. **SHOULD** indicates any consumer of the output -must still guard against unwanted behavior. - -### Approvability - -A solution is approvable if it either matches the (set of) optimal solution(s) -of that exercise, or if it shows understanding of the teaching goals of that -exercise and every _core_ exercise that came before. This means that an -analyzer **MAY** approve a solution but still provide a set of comments as tips -relating to the exercise or further improvements. - -### Idiomatic rules / Language features / Stylistic choices - -In this document, **de facto** is defined as follows: - -- **de facto**: describes practices that exist in reality, even if not - officially recognized by laws. -- **de facto standard**: a custom or convention that has achieved a dominant - position by public acceptance or market forces. Unofficial customs that are - widely accepted. - -In other words, if a nearly all developers (non-hobbyists) who write code in a -certain language have established certain rules, these rules are a **de facto -standard** and become idiomatic use. Example: **Ruby** uses 2 space indentation. - -Some rules are language features, even if they are not documented well. These -language features are part of "idiomatic rules" and not stylistic choices. -Example: **Ruby**'s MRI treats variables named `_` differently. - -Finally there are rules that are pure preferences, even though they might be -adopted by large bodies such as organizations and corporations. These rules -are usually part of _competing_ standards. Exercism does not favor one over -another. Example: **TypeScript** has a linter `tslint` (or `eslint` + plugin) -which is maintained by a company that is not Microsoft. It competes with other -linters such as `xo`. Most of the rules are not language features or idiomatic -rules, and therefore stylistic choices. - -## Conditions and outcome - -For feedback generated by automated mentoring: - -- it **SHOULD** `disapprove` when someone isn't using one of the - subjects the exercise is supposed to teach, -- each `disapprove` **MUST** have at least one comment, -- each `disapprove` **SHOULD** steer a student towards an `approve`, -- the analyzer **SHOULD** aim to teach a pathway, which means a student - **SHOULD NOT** get a `refer_to_mentor` in a newer iteration, after a - `disapprove`, given they follow that pathway. -- a `refer_to_mentor` **MAY** have one or more comments which will be given to - the mentor, -- the same comment **MUST** not be added twice in one analysis. Adding the same - comment with different parameters attached to it is not considered a duplicate. -- comments **SHOULD** be ordered by importance (the first comment being the most - important, and the last comment being the least important). - -It is currently undecided as to whether there is a minimum or maximum amount of -:speech_balloon: comments. However, each comment **SHOULD** be aiding the -student towards an approvable solution. Our recommendation is currently to aim -for one to three comments per iteration. - -## Approvability - -Given the above, and to re-iterate that we focus on language fluency: - -### Incorrect Naming or Casing - -In general, if it's a _language feature_ that will be caught by a compiler or -parser or based on official rules (which means there is a dependency on -correctness in tools), you should `disapprove`, preferably linking -to the official rules. - -> - :-1: disapprove if there are official guidelines on naming conventions -> - :speech_balloon: leave a comment if there is something noteworthy -> - :no*bell: if its a \_stylistic* preference, **and** there are _competing -> standards_, do not remark at all. Since there are competing standards, -> they're all preferences. -> - :speech*balloon: if it's a \_stylistic* preference, **and** there is _one -> clear standard_, comment on it. These rules enforce idiomatic code. -> - :question: If it's a _stylistic_ preference, **and** there is no clear -> standard, but most to all non-hobbyist have adopted the same style, this -> might be idiomatic. Comment at your discretion. - -#### Examples - -- **Ruby** has a language feature where `_` is treated differently, - - :-1: if a student uses `_` for a variable name, but then uses it. -- **Ruby** recognizes `constants` only if they start with a **C**apital Letter, - - :-1: if a student uses `snake_case` for a `class` name -- **Ruby** has _de facto_ standards on `cAsInG` and `name-ing`, - - :speech_balloon: you **SHOULD** guide students that `snake_case` is to be - expected by most IDEs and highlighting on exercism in code blocks. -- **JavaScript** IDEs highlight variables which are not used, except for those - prefixed with an underscore (`_`). - - :speech*balloon: note that this behavior exist so it might help them to use - a different naming strategy. They might think that prefixing with `*`means `private`, which is not the case in JavaScript. -- **TypeScript** has a _de facto_ standard lint tool provided by Palantir, - - :no_bell: If a student does not follow these rules as the lint tool is not - official. In fact, there are multiple linters out there, with mutually - exclusive rules. -- **Go** has very strict rules around naming and other linting. - - :-1: if the student does not follow these (e.g. has not applied `golint`) -- **Go** has very strict rules around formatting. - - :-1: if the student does not follow these (e.g. has not applied `gofmt`) - -### Badly formatted code - -The same rules apply as above. In general, if `linting` and a specific format is -not part of the official language, and/or not integral to the language: - -- :no_bell: **SHOULD NOT** disapprove on it -- :speech_balloon: You **MAY** guide students towards tools for auto-formatting From c8fcecf4b9cef9e48bb312f039d611bbd6ff30bb Mon Sep 17 00:00:00 2001 From: Aron Demeter <66035744+dem4ron@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:05:43 +0200 Subject: [PATCH 013/129] Update add-initial-exercises.md (#447) https://forum.exercism.org/t/small-fix-to-website-documentation-for-adding-exercises/6390 --- building/tracks/new/add-initial-exercises.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index 5bfb87d1..937f92af 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -10,7 +10,7 @@ There are two types of exercises you can implement: - Practice Exercises: they are designed to practice learned concepts. Check the [documentation](/docs/building/tracks/practice-exercises) for more information. In general, we recommend new tracks start with implementing Practice Exercises, as they're easier to implement and require less thinking upfront. -Concept Exercises can always be added later, once you're track is up-and-running properly. +Concept Exercises can always be added later, once your track is up and running properly. ## Choose exercises From 56120d3511aeafc7e247c3602165ccc59030f28b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 18 Jul 2023 10:48:54 +0200 Subject: [PATCH 014/129] Update average run time (#430) * Update average run time * Update building/configlet/lint.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --------- Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- building/configlet/lint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/configlet/lint.md b/building/configlet/lint.md index 7bd07595..4df60cc5 100644 --- a/building/configlet/lint.md +++ b/building/configlet/lint.md @@ -108,7 +108,7 @@ The `config.json` file should have the following checks: - If the track is `d` or `plsql`, the `"files.solution"` and `"files.test"` files _can_ overlap - The `"files.example` and `"files.exemplar"` files _can_ overlap - The `"test_runner.average_run_time"` key is required if `"status.test_runner"` is equal to `true` -- The `"test_runner.average_run_time"` value must be a floating-point number > 0 with one decimal point of precision +- The `"test_runner.average_run_time"` value must be an integer > 0 - The `"approaches.snippet_extension"` key is required if the track has one or more approaches - The `"approaches.snippet_extension"` value must be a non-blank string¹ - The `"exercises"` key is required From 75528d960471545032fb7888ccc11b265107e709 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 18 Jul 2023 13:30:59 +0200 Subject: [PATCH 015/129] Add re-testing solutions documentation (#448) * Add re-testing solutions documentation * Update heading * Update building/tracks/README.md Co-authored-by: Jeremy Walker * Update building/tracks/README.md Co-authored-by: Jeremy Walker * Update building/tracks/README.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> * Update building/tracks/README.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> * Update building/tracks/README.md Co-authored-by: Jeremy Walker * Add missing word * Improve * Emphasize --------- Co-authored-by: Jeremy Walker Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- building/tracks/README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/building/tracks/README.md b/building/tracks/README.md index 2d477cfe..7b94c7c8 100644 --- a/building/tracks/README.md +++ b/building/tracks/README.md @@ -92,5 +92,33 @@ csharp | ├── debug.md | ├── help.md | └── tests.md -└── [config.json](/docs/building/tracks) +└── config.json + +## Maintenance + +### Avoiding triggering unnecessary test runs + +When you merge a track PR that touches an exercise, it triggers the latest published iteration of students' solutions to be re-tested. +For popular exercises, this is a _very_ expensive operation (70,000 test runs for Python Hello World as an extreme!). + +**We encourage you to try and avoid doing this unnecessarily.** + +Solutions **will not** be retested if the merged commit either: + +- only touches `.docs` or `.meta` files, or other files that users don't interact with +- or contains `[no important files changed]` in the commit body. + +Solutions **will** be re-tested if the merged commit both: + +- lacks `[no important files changed]` in the commit body +- and touches one of the following files for an exercise (as specified in its `.meta/config.json` file): + - test files + - editor files + - invalidator files + +Some examples: + +- [Python#3423](https://github.com/exercism/python/pull/3423): Only touches docs so no tests were run +- [Python#3437](https://github.com/exercism/python/commit/29a64a4889f94bafbd0062d7fc5052858523b25c): Was merged with `[no important files changed]` added, so no tests were run +- [Csharp#2138](https://github.com/exercism/csharp/pull/2138): Whitespace was removed from tests. The keyword was **not** added. Tests were re-run unnecessarily. From dfe837bb4515ff0ffb42baf68bf7f55c8d1c89a8 Mon Sep 17 00:00:00 2001 From: Tim Austin Date: Sat, 29 Jul 2023 05:35:08 -0600 Subject: [PATCH 016/129] Add concept map documentation (#449) --- building/config.json | 7 +++++ building/tracks/README.md | 3 +++ building/tracks/concept-map.md | 48 ++++++++++++++++++++++++++++++++++ building/tracks/concepts.md | 4 ++- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 building/tracks/concept-map.md diff --git a/building/config.json b/building/config.json index 9302a3e9..266c1ceb 100644 --- a/building/config.json +++ b/building/config.json @@ -520,6 +520,13 @@ "title": "Concepts", "blurb": "" }, + { + "uuid": "0807db01-ce0e-4ac4-817b-7fe0a2366d01", + "slug": "tracks/concept-map", + "path": "building/tracks/concept-map.md", + "title": "Concept Map", + "blurb": "" + }, { "uuid": "2a29f722-e70d-4679-81f8-bd6727fa9013", "slug": "tracks/config-json", diff --git a/building/tracks/README.md b/building/tracks/README.md index 7b94c7c8..ac2d64fc 100644 --- a/building/tracks/README.md +++ b/building/tracks/README.md @@ -11,6 +11,9 @@ The track's configuration and metadata are specified in the `config.json` file. All concept and practices exercises of a track involve _concepts_. These concepts are separate entities by themselves. Check the [documentation](/docs/building/tracks/concepts) for more information. The concepts taught in the track's concept exercises form a _syllabus_. +The _syllabus_ is shown to students as a _concept map_. +Check the [_concept map_ documentation](/docs/building/tracks/concept-map) to learn about building out the concept map for a _syllabus_. + For more information on how to design a syllabus, check the [syllabus documentation](/docs/building/tracks/syllabus). ## Exercises diff --git a/building/tracks/concept-map.md b/building/tracks/concept-map.md new file mode 100644 index 00000000..e67a4cc6 --- /dev/null +++ b/building/tracks/concept-map.md @@ -0,0 +1,48 @@ +# Concept Map + +The concept map is one of the main methods to illustrate the progression that a student takes through the concept exercises. + +## Design Goals + +- Provide a meaningful concept map of ideas from simple to complex ideas. +- Provide a pathway towards fluency in a language. +- Illustrate the progression through the course contents. + +## Structure + +- Nodes + - Each concept is represented by a node on the concept map. + - Provide a quick reference with their style to indicate the degree of progression. + - Mastered: Concepts whose learning exercise and all associate practice exercise is complete. + - Complete: Concepts whose learning exercise is complete, but one or more practice exercise is not complete. + - Available: Concepts whose learning exercise has not been completed. + - Locked: Concepts whose learning exercise requires one or more pre-requisite exercise to be completed. +- Paths + - Provide a relation of one concept to the next. + - Provide a relation indicating the building blocks of a concept back to the root. + +## Configuration + +The configuration of the concept map is determined by details of the [`config.json`](/docs/building/tracks/config-json) associated to a track. +Specifically the specification of the concept exercises determines the shape and layout of the concept map. + +> You can view the code used to compute the concept map specification on GitHub: [Exercism/website: determine_concept_map_layout.rb][github-concept-code] + +A concept exercise specifies its prerequites and also the concept it teaches on completion. +If we translate this to the terms of a [graph][wikipedia-graph]: +- A concept represents a _vertex_ (also known as a _node_) +- A concept exercise determines one or more _directed edges_ between two _vertices_ (_nodes_) + +It is important to note, not all edges that could be specified from the `config.json` appear in the result -- only the edges from the preceeding level are selected. + +```mermaid +flowchart TD + Basics --> Integers + Integers --> Strings + Basics --Edge Removed--> Strings + + linkStyle 2 stroke-width:2px,fill:none,stroke:red; +``` + +[github-concept-code]: https://github.com/exercism/website/blob/main/app/commands/track/determine_concept_map_layout.rb +[wikipedia-graph]: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics) diff --git a/building/tracks/concepts.md b/building/tracks/concepts.md index 6dcaa01b..830620e5 100644 --- a/building/tracks/concepts.md +++ b/building/tracks/concepts.md @@ -1,6 +1,8 @@ # Concepts -Concepts are the things that a programmer would need to understand to be fluent in a language. Concepts are taught by Concept Exercises and are used as prerequisites for Concept- _and_ Practice Exercises. +Concepts are the things that a programmer would need to understand to be fluent in a language. +Concepts are taught by Concept Exercises and are used as prerequisites for Concept- _and_ Practice Exercises. +Concepts are placed on to a [_concept map_](/docs/building/tracks/concept-map) when displayed to the student. ## Metadata From 954ef4d8778a75ec7898872ab4a33b0179924d9b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Mon, 31 Jul 2023 13:23:51 +0200 Subject: [PATCH 017/129] Make `online_editor.highlightjs_language` optional (#450) --- building/configlet/lint.md | 13 +++++++++---- building/tracks/config-json.md | 2 +- building/tracks/new/prepare-for-launch.md | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/building/configlet/lint.md b/building/configlet/lint.md index 4df60cc5..2bb9ce98 100644 --- a/building/configlet/lint.md +++ b/building/configlet/lint.md @@ -76,7 +76,7 @@ The `config.json` file should have the following checks: - The `"online_editor.indent_style"` value must be the string `space` or `tab` - The `"online_editor.indent_size"` key is required - The `"online_editor.indent_size"` value must be an integer >= 0 and <= 8 -- The `"online_editor.highlightjs_language"` key is required +- The `"online_editor.highlightjs_language"` key is optional - The `"online_editor.highlightjs_language"` value must be a non-blank string¹ - The `"files"` key is optional - The `"files"` value must be an object @@ -528,7 +528,7 @@ The `config.json` file should have the following checks: 1. **Non-blank string**: a string that contains at least one non-whitespace character. 2. **kebab-case string**: a string that contains only characters in the range `[a-z0-9]`, optionally separated by dashes (e.g. "two-fer"). It must match the regular expression: `^[a-z0-9]+(-[a-z0-9]+)*$` -3. **Title Case string**: a non-blank string that follows the below [guidelines from the Chicago Manual of Style](https://en.wikipedia.org/wiki/Title_case#Chicago_Manual_of_Style): +3. **Title Case string**: a non-blank string that follows the below [guidelines from the Chicago Manual of Style](https://en.wikipedia.org/wiki/Title_case#Chicago_Manual_of_Style): - Capitalize the first and last words of titles and subtitles. - Capitalize "major" words (nouns, pronouns, verbs, adjectives, adverbs, and some conjunctions). - Lowercase the conjunctions _and_, _but_, _for_, _or_, and _nor_. @@ -539,23 +539,28 @@ The `config.json` file should have the following checks: 4. **Sentence Case string**: a non-blank string that follows the below [guidelines](https://en.wikipedia.org/wiki/Letter_case#Sentence_case): - Capitalize the first word of the sentence, as well as proper nouns and other words as required by a more specific rule. 5. **Valid `files` pattern**: A non-blank string¹ that specifies a location of a file used in an exercise, relative to the exercise's directory. A pattern may use one of the following placeholders: - + - `%{kebab_slug}`: the `kebab-case` exercise slug (e.g. `bit-manipulation`) - `%{snake_slug}`: the `snake_case` exercise slug (e.g. `bit_manipulation`) - `%{camel_slug}`: the `camelCase` exercise slug (e.g. `bitManipulation`) - `%{pascal_slug}`: the `PascalCase` exercise slug (e.g. `BitManipulation`) + 6. **Unique version 4 UUID string**: A string that satisfies all of these conditions: - + - It only exists once in the track-level `config.json` file of a given Exercism track - It does not exist in the track-level `config.json` file of any other Exercism track - It does not exist in any `canonical-data.json` file in https://github.com/exercism/problem-specifications - It does not exist anywhere else on Exercism - It matches the regular expression: + ``` ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ ``` + For example, the below UUID has the correct form: + ``` d334ffe3-657e-4725-a950-290b284b6d9f ``` + You can run `configlet uuid` to generate a suitable UUID. diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index 6d62b666..5e03f66f 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -14,7 +14,7 @@ The following top-level properties contain general track metadata: - `online_editor`: an object describing settings used for the online editor: (required) - `indent_style`: either `"space"` or `"tab"` (required) - `indent_size`: the indentation size as an integer (e.g. `4`) (required) - - `highlightjs_language`: the language identifier for Highlight.js (see the [full list of identifiers](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)) (required) + - `highlightjs_language`: the language identifier for Highlight.js (see the [full list of identifiers](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)) (optional) - `status`: an object describing which v3 features should be enabled: (required) - `concept_exercises`: a `boolean` value indicating if [Concept Exercises](/docs/building/tracks/concept-exercises) have been built (required). When `true` the Exercism website interface changes to indicate that concept exercises are available for the track. - `test_runner`: a `boolean` value indicating if a [test runner](/docs/building/tooling/test-runners) has been implemented (required). When `true` we put submitted solutions through our testing infrastructure and show the results on the website. The website also allows students to initiate a test run from within the online editor. diff --git a/building/tracks/new/prepare-for-launch.md b/building/tracks/new/prepare-for-launch.md index 1520c26d..d23e0521 100644 --- a/building/tracks/new/prepare-for-launch.md +++ b/building/tracks/new/prepare-for-launch.md @@ -51,6 +51,6 @@ The following properties should be updated: - `online_editor`: an object describing settings used for the online editor: - `indent_style`: either `"space"` or `"tab"` - `indent_size`: the indentation size as an integer (e.g. `4`) - - `highlightjs_language`: the language identifier for Highlight.js (see the [full list of identifiers](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)) + - `highlightjs_language`: the language identifier for Highlight.js (see the [full list of identifiers](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)). If there is no Highlight.js support, this field can be omitted. - `key_features`: the language's key features, which succinctly describe its most important features. For more information, check the [key features documentation](/docs/building/tracks/config-json#h-key-features). - `tags`: define the tags that apply to this track, which allows it be searched for on the website. For more information and the list of supported tags, check the [tags documentation](/docs/building/tracks/config-json#h-tags). From 88fadf76d772f1e2cdd5f41758d4d985ebdf1ae8 Mon Sep 17 00:00:00 2001 From: Adrien LUDWIG <42720099+Adrien-LUDWIG@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:18:35 +0000 Subject: [PATCH 018/129] Fix typos (#451) --- community/maintainers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/community/maintainers.md b/community/maintainers.md index d2853114..a6ea6786 100644 --- a/community/maintainers.md +++ b/community/maintainers.md @@ -10,7 +10,7 @@ If you want to one day become a Maintainer (yay!!), then please read on! But remember, most people who help build Exercism are not Maintainers - they are [Contributors](/docs/community/contributors). **There is no barrier to contributing to Exercism** and every bit of help is gratefully received. -We aim to have at least active two Maintainers for each Track, and at least one active maintainer for all the parts of the platform. +We aim to have at least two active Maintainers for each Track, and at least one active maintainer for all the parts of the platform. _Note: the sections below generally discuss Track Maintainers. The process for becoming a Maintainer of different parts of the ecosystem varies._ @@ -38,7 +38,7 @@ Through this Maintainers control what code goes into their repositories. ## Rewards -The main incentives for being an Exercism Maintainer tend to be intrinsic: e.g. the fun of building something meaningful and useful with other like-minded people, +The main incentives for being an Exercism Maintainer tend to be intrinsic: e.g. the fun of building something meaningful and useful with other like-minded people. There are also career benefits such as improving your personal network, developing both technical and soft skills, and having a meaningful out-of-work entry on your resume. We also provide some extra benefits: From 07b6fdc68890236ae0e5abb616304ed70141a96a Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 16 Aug 2023 21:28:57 +0200 Subject: [PATCH 019/129] Use `assets.exercism.org` subdomain (#452) As part of moving various parts of our image hosting behind a CDN, this commit updates the involved urls we automatically found in this repository. --- building/tracks/icons.md | 668 +++++++++++++++++++-------------------- 1 file changed, 334 insertions(+), 334 deletions(-) diff --git a/building/tracks/icons.md b/building/tracks/icons.md index 8da71cd8..2f4cda31 100644 --- a/building/tracks/icons.md +++ b/building/tracks/icons.md @@ -9,345 +9,345 @@ There are two types of icons relevant to tracks: | Name | Icon | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| accumulate | accumulate | -| acronym | acronym | -| affine-cipher | affine-cipher | -| all-your-base | all-your-base | -| allergies | allergies | -| alphametics | alphametics | -| amusement-park | amusement-park | -| amusement-park-improvements | amusement-park-improvements | -| anagram | anagram | -| anagrams | anagrams | -| annalyns-infiltration | annalyns-infiltration | -| annalyns-infiltration2 | annalyns-infiltration2 | -| armstrong-numbers | armstrong-numbers | -| array-loops | array-loops | -| assembly-line | assembly-line | -| atbash-cipher | atbash-cipher | -| attack-of-the-trolls | attack-of-the-trolls | -| authentication-system | authentication-system | -| bandwagoner | bandwagoner | -| bank-account | bank-account | -| basics | basics | -| basketball-website | basketball-website | -| beauty-salon-goes-global | beauty-salon-goes-global | -| beer-song | beer-song | -| bettys-bike-shop | bettys-bike-shop | -| binary | binary | -| binary-search | binary-search | -| binary-search-tree | binary-search-tree | -| bird-count | bird-count | -| bird-watcher | bird-watcher | -| blackjack | blackjack | -| bob | bob | -| bomb-defuser | bomb-defuser | -| book-store | book-store | -| booking-up-for-beauty | booking-up-for-beauty | -| booleans | booleans | -| boutique-inventory | boutique-inventory | -| boutique-inventory-improvements | boutique-inventory-improvements | -| boutique-suggestions | boutique-suggestions | -| bowling | bowling | -| bracket-push | bracket-push | -| bread-and-potions | bread-and-potions | -| building-telemetry | building-telemetry | -| calculator-conundrum | calculator-conundrum | -| captains-log | captains-log | -| car-purchase | car-purchase | -| card-tricks | card-tricks | -| cars-assemble | cars-assemble | -| census | census | -| change | change | -| character-study | character-study | -| chessboard | chessboard | -| circular-buffer | circular-buffer | -| city-office | city-office | -| clock | clock | -| collatz-conjecture | collatz-conjecture | -| community-garden | community-garden | -| complex-numbers | complex-numbers | -| connect | connect | -| coordinate-transformation | coordinate-transformation | -| crypto-square | crypto-square | -| csv-builder | csv-builder | -| custom-set | custom-set | -| custom-signs | custom-signs | -| darts | darts | -| date-parser | date-parser | -| decimal | decimal | -| developer-privileges | developer-privileges | -| diamond | diamond | -| die | die | -| difference-of-squares | difference-of-squares | -| diffie-hellman | diffie-hellman | -| dna-encoding | dna-encoding | -| dnd-character | dnd-character | -| documented-lasagna | documented-lasagna | -| dominoes | dominoes | -| dot-dsl | dot-dsl | -| doubly-linked-list | doubly-linked-list | -| dungeons-and-dragons | dungeons-and-dragons | -| eight-queens | eight-queens | -| election-day | election-day | -| elons-toys | elons-toys | -| elyses-analytic-enchantments | elyses-analytic-enchantments | -| elyses-enchantments | elyses-enchantments | -| elyses-transformative-enchantments | elyses-transformative-enchantments | -| emoji-times | emoji-times | -| equilateral-triangle | equilateral-triangle | -| error-handling | error-handling | -| errors | errors | -| etl | etl | -| exercism-matrix | exercism-matrix | -| faceid-2 | faceid-2 | -| factory-sensors | factory-sensors | -| fibonacci-iterator | fibonacci-iterator | -| file-sniffer | file-sniffer | -| fizz-buzz | fizz-buzz | -| fizzy | fizzy | -| flatten-array | flatten-array | -| food-chain | food-chain | -| football-match-reports | football-match-reports | -| forth | forth | -| freelancer-rates | freelancer-rates | -| fruit-picker | fruit-picker | -| futures | futures | -| german-sysadmin | german-sysadmin | -| gigasecond | gigasecond | -| go-counting | go-counting | -| grade-school | grade-school | -| grains | grains | -| grep | grep | -| gross-store | gross-store | -| guessing-game | guessing-game | -| hamming | hamming | -| hangman | hangman | -| health-statistics | health-statistics | -| hello-world | hello-world | -| hexadecimal | hexadecimal | -| high-school-sweethearts | high-school-sweethearts | -| high-score | high-score | -| high-score-board | high-score-board | -| high-scores | high-scores | -| house | house | -| hyper-optimized-telemetry | hyper-optimized-telemetry | -| hyperia-forex | hyperia-forex | -| hyperinflation-hits-hyperia | hyperinflation-hits-hyperia | -| instruments-of-texas | instruments-of-texas | -| interest-is-interesting | interest-is-interesting | -| international-calling-connoisseur | international-calling-connoisseur | -| isbn-verifier | isbn-verifier | -| isogram | isogram | -| kindergarten-garden | kindergarten-garden | -| kitchen-calculator | kitchen-calculator | -| knapsack | knapsack | -| land-grab-in-space | land-grab-in-space | -| language-list | language-list | -| largest-series-product | largest-series-product | -| larrys-winning-checker | larrys-winning-checker | -| lasagna | lasagna | -| lasagna-master | lasagna-master | -| leap | leap | -| ledger | ledger | -| lens-person | lens-person | -| library-fees | library-fees | -| linked-list | linked-list | -| list-ops | list-ops | -| log-levels | log-levels | -| logs-logs-logs | logs-logs-logs | -| low-power-embedded-game | low-power-embedded-game | -| lucky-numbers | lucky-numbers | -| luhn | luhn | -| macros | macros | -| magazine-cutout | magazine-cutout | -| magician-in-training | magician-in-training | -| maps | maps | -| markdown | markdown | -| mask-credit-card | mask-credit-card | -| master-mixologist | master-mixologist | -| matching-brackets | matching-brackets | -| matrix | matrix | -| meetup | meetup | -| mensch-aergere-dich-nicht | mensch-aergere-dich-nicht | -| micro-blog | micro-blog | -| microwave | microwave | -| minesweeper | minesweeper | -| minima | minima | -| missing-number | missing-number | -| mixed-juices | mixed-juices | -| movie-goer | movie-goer | -| moviegoer | moviegoer | -| name-badge | name-badge | -| need-for-speed | need-for-speed | -| new-passport | new-passport | -| newsletter | newsletter | -| nth-prime | nth-prime | -| nucleotide-codons | nucleotide-codons | -| nucleotide-count | nucleotide-count | -| nullability | nullability | -| numbers | numbers | -| object-relational-mapping | object-relational-mapping | -| ocr-numbers | ocr-numbers | -| octal | octal | -| ordinal-numbers | ordinal-numbers | -| orm-in-one-go | orm-in-one-go | -| ozans-playlist | ozans-playlist | -| paasio | paasio | -| pacman-rules | pacman-rules | -| pal-picker | pal-picker | -| palindrome-products | palindrome-products | -| pangram | pangram | -| parallel-letter-frequency | parallel-letter-frequency | -| parsing-log-files | parsing-log-files | -| party-robot | party-robot | -| pascals-triangle | pascals-triangle | -| perfect-numbers | perfect-numbers | -| phone-number | phone-number | -| phone-number-analysis | phone-number-analysis | -| pig-latin | pig-latin | -| pizza-order | pizza-order | -| pizza-pricing | pizza-pricing | -| pizza-slices | pizza-slices | -| poetry-club | poetry-club | -| poetry-club-door-policy | poetry-club-door-policy | -| point-mutations | point-mutations | -| poker | poker | -| pov | pov | -| prime-factors | prime-factors | -| prime-number | prime-number | -| promises | promises | -| protein-translation | protein-translation | -| proverb | proverb | -| pythagorean-triplet | pythagorean-triplet | -| queen-attack | queen-attack | -| rail-fence-cipher | rail-fence-cipher | -| raindrops | raindrops | -| rational-numbers | rational-numbers | -| react | react | -| rectangles | rectangles | -| red-vs-blue-darwin-style | red-vs-blue-darwin-style | -| remote-control-cleanup | remote-control-cleanup | -| remote-control-competition | remote-control-competition | -| resistor-color | resistor-color | -| resistor-color-duo | resistor-color-duo | -| resistor-color-trio | resistor-color-trio | -| rest-api | rest-api | -| reverse-string | reverse-string | -| rle | rle | -| rna-transcription | rna-transcription | -| robot-name | robot-name | -| robot-simulator | robot-simulator | -| role-playing-game | role-playing-game | -| roll-the-die | roll-the-die | -| roman-numerals | roman-numerals | -| rotational-cipher | rotational-cipher | -| rpn-calculator | rpn-calculator | -| run-length-encoding | run-length-encoding | -| saddle-points | saddle-points | -| salary-calculator | salary-calculator | -| santas-helper | santas-helper | -| satellite | satellite | -| savings-account | savings-account | -| say | say | -| scale-generator | scale-generator | -| scrabble-score | scrabble-score | -| secret-agent | secret-agent | -| secret-handshake | secret-handshake | -| secrets | secrets | -| secure-munchester-united | secure-munchester-united | -| semi-structured-logs | semi-structured-logs | -| series | series | -| sgf-parsing | sgf-parsing | -| short-fibonacci | short-fibonacci | -| sieve | sieve | -| simple-calculator | simple-calculator | -| simple-cipher | simple-cipher | -| simple-linked-list | simple-linked-list | -| slices | slices | -| socks-and-sexprs | socks-and-sexprs | -| sorting-room | sorting-room | -| space-age | space-age | -| spiral-matrix | spiral-matrix | -| square-root | square-root | -| squeaky-clean | squeaky-clean | -| stage-heralding | stage-heralding | -| strain | strain | -| strings | strings | -| strings-package | strings-package | -| sublist | sublist | -| sum-of-multiples | sum-of-multiples | -| take-a-number | take-a-number | -| the-farm | the-farm | -| the-weather-in-deather | the-weather-in-deather | -| tim-from-marketing | tim-from-marketing | -| time | time | -| tisbury-treasure-hunt | tisbury-treasure-hunt | -| top-secret | top-secret | -| tournament | tournament | -| tracks-on-tracks-on-tracks | tracks-on-tracks-on-tracks | -| translation-service | translation-service | -| transpose | transpose | -| tree-building | tree-building | -| triangle | triangle | -| trinary | trinary | -| twelve-days | twelve-days | -| two_product_production_decision | two_product_production_decision | -| two-bucket | two-bucket | -| two-fer | two-fer | -| valentines-day | valentines-day | -| variable-length-quantity | variable-length-quantity | -| vehicle-purchase | vehicle-purchase | -| weather-forecast | weather-forecast | -| weighing-machine | weighing-machine | -| welcome-to-tech-palace | welcome-to-tech-palace | -| windowing-system | windowing-system | -| wine-cellar | wine-cellar | -| wizards-and-warriors | wizards-and-warriors | -| wizards-and-warriors-2 | wizards-and-warriors-2 | -| word-count | word-count | -| word-search | word-search | -| wordy | wordy | -| xorcism | xorcism | -| yacht | yacht | -| zebra-puzzle | zebra-puzzle | -| zero-value | zero-value | +| accumulate | accumulate | +| acronym | acronym | +| affine-cipher | affine-cipher | +| all-your-base | all-your-base | +| allergies | allergies | +| alphametics | alphametics | +| amusement-park | amusement-park | +| amusement-park-improvements | amusement-park-improvements | +| anagram | anagram | +| anagrams | anagrams | +| annalyns-infiltration | annalyns-infiltration | +| annalyns-infiltration2 | annalyns-infiltration2 | +| armstrong-numbers | armstrong-numbers | +| array-loops | array-loops | +| assembly-line | assembly-line | +| atbash-cipher | atbash-cipher | +| attack-of-the-trolls | attack-of-the-trolls | +| authentication-system | authentication-system | +| bandwagoner | bandwagoner | +| bank-account | bank-account | +| basics | basics | +| basketball-website | basketball-website | +| beauty-salon-goes-global | beauty-salon-goes-global | +| beer-song | beer-song | +| bettys-bike-shop | bettys-bike-shop | +| binary | binary | +| binary-search | binary-search | +| binary-search-tree | binary-search-tree | +| bird-count | bird-count | +| bird-watcher | bird-watcher | +| blackjack | blackjack | +| bob | bob | +| bomb-defuser | bomb-defuser | +| book-store | book-store | +| booking-up-for-beauty | booking-up-for-beauty | +| booleans | booleans | +| boutique-inventory | boutique-inventory | +| boutique-inventory-improvements | boutique-inventory-improvements | +| boutique-suggestions | boutique-suggestions | +| bowling | bowling | +| bracket-push | bracket-push | +| bread-and-potions | bread-and-potions | +| building-telemetry | building-telemetry | +| calculator-conundrum | calculator-conundrum | +| captains-log | captains-log | +| car-purchase | car-purchase | +| card-tricks | card-tricks | +| cars-assemble | cars-assemble | +| census | census | +| change | change | +| character-study | character-study | +| chessboard | chessboard | +| circular-buffer | circular-buffer | +| city-office | city-office | +| clock | clock | +| collatz-conjecture | collatz-conjecture | +| community-garden | community-garden | +| complex-numbers | complex-numbers | +| connect | connect | +| coordinate-transformation | coordinate-transformation | +| crypto-square | crypto-square | +| csv-builder | csv-builder | +| custom-set | custom-set | +| custom-signs | custom-signs | +| darts | darts | +| date-parser | date-parser | +| decimal | decimal | +| developer-privileges | developer-privileges | +| diamond | diamond | +| die | die | +| difference-of-squares | difference-of-squares | +| diffie-hellman | diffie-hellman | +| dna-encoding | dna-encoding | +| dnd-character | dnd-character | +| documented-lasagna | documented-lasagna | +| dominoes | dominoes | +| dot-dsl | dot-dsl | +| doubly-linked-list | doubly-linked-list | +| dungeons-and-dragons | dungeons-and-dragons | +| eight-queens | eight-queens | +| election-day | election-day | +| elons-toys | elons-toys | +| elyses-analytic-enchantments | elyses-analytic-enchantments | +| elyses-enchantments | elyses-enchantments | +| elyses-transformative-enchantments | elyses-transformative-enchantments | +| emoji-times | emoji-times | +| equilateral-triangle | equilateral-triangle | +| error-handling | error-handling | +| errors | errors | +| etl | etl | +| exercism-matrix | exercism-matrix | +| faceid-2 | faceid-2 | +| factory-sensors | factory-sensors | +| fibonacci-iterator | fibonacci-iterator | +| file-sniffer | file-sniffer | +| fizz-buzz | fizz-buzz | +| fizzy | fizzy | +| flatten-array | flatten-array | +| food-chain | food-chain | +| football-match-reports | football-match-reports | +| forth | forth | +| freelancer-rates | freelancer-rates | +| fruit-picker | fruit-picker | +| futures | futures | +| german-sysadmin | german-sysadmin | +| gigasecond | gigasecond | +| go-counting | go-counting | +| grade-school | grade-school | +| grains | grains | +| grep | grep | +| gross-store | gross-store | +| guessing-game | guessing-game | +| hamming | hamming | +| hangman | hangman | +| health-statistics | health-statistics | +| hello-world | hello-world | +| hexadecimal | hexadecimal | +| high-school-sweethearts | high-school-sweethearts | +| high-score | high-score | +| high-score-board | high-score-board | +| high-scores | high-scores | +| house | house | +| hyper-optimized-telemetry | hyper-optimized-telemetry | +| hyperia-forex | hyperia-forex | +| hyperinflation-hits-hyperia | hyperinflation-hits-hyperia | +| instruments-of-texas | instruments-of-texas | +| interest-is-interesting | interest-is-interesting | +| international-calling-connoisseur | international-calling-connoisseur | +| isbn-verifier | isbn-verifier | +| isogram | isogram | +| kindergarten-garden | kindergarten-garden | +| kitchen-calculator | kitchen-calculator | +| knapsack | knapsack | +| land-grab-in-space | land-grab-in-space | +| language-list | language-list | +| largest-series-product | largest-series-product | +| larrys-winning-checker | larrys-winning-checker | +| lasagna | lasagna | +| lasagna-master | lasagna-master | +| leap | leap | +| ledger | ledger | +| lens-person | lens-person | +| library-fees | library-fees | +| linked-list | linked-list | +| list-ops | list-ops | +| log-levels | log-levels | +| logs-logs-logs | logs-logs-logs | +| low-power-embedded-game | low-power-embedded-game | +| lucky-numbers | lucky-numbers | +| luhn | luhn | +| macros | macros | +| magazine-cutout | magazine-cutout | +| magician-in-training | magician-in-training | +| maps | maps | +| markdown | markdown | +| mask-credit-card | mask-credit-card | +| master-mixologist | master-mixologist | +| matching-brackets | matching-brackets | +| matrix | matrix | +| meetup | meetup | +| mensch-aergere-dich-nicht | mensch-aergere-dich-nicht | +| micro-blog | micro-blog | +| microwave | microwave | +| minesweeper | minesweeper | +| minima | minima | +| missing-number | missing-number | +| mixed-juices | mixed-juices | +| movie-goer | movie-goer | +| moviegoer | moviegoer | +| name-badge | name-badge | +| need-for-speed | need-for-speed | +| new-passport | new-passport | +| newsletter | newsletter | +| nth-prime | nth-prime | +| nucleotide-codons | nucleotide-codons | +| nucleotide-count | nucleotide-count | +| nullability | nullability | +| numbers | numbers | +| object-relational-mapping | object-relational-mapping | +| ocr-numbers | ocr-numbers | +| octal | octal | +| ordinal-numbers | ordinal-numbers | +| orm-in-one-go | orm-in-one-go | +| ozans-playlist | ozans-playlist | +| paasio | paasio | +| pacman-rules | pacman-rules | +| pal-picker | pal-picker | +| palindrome-products | palindrome-products | +| pangram | pangram | +| parallel-letter-frequency | parallel-letter-frequency | +| parsing-log-files | parsing-log-files | +| party-robot | party-robot | +| pascals-triangle | pascals-triangle | +| perfect-numbers | perfect-numbers | +| phone-number | phone-number | +| phone-number-analysis | phone-number-analysis | +| pig-latin | pig-latin | +| pizza-order | pizza-order | +| pizza-pricing | pizza-pricing | +| pizza-slices | pizza-slices | +| poetry-club | poetry-club | +| poetry-club-door-policy | poetry-club-door-policy | +| point-mutations | point-mutations | +| poker | poker | +| pov | pov | +| prime-factors | prime-factors | +| prime-number | prime-number | +| promises | promises | +| protein-translation | protein-translation | +| proverb | proverb | +| pythagorean-triplet | pythagorean-triplet | +| queen-attack | queen-attack | +| rail-fence-cipher | rail-fence-cipher | +| raindrops | raindrops | +| rational-numbers | rational-numbers | +| react | react | +| rectangles | rectangles | +| red-vs-blue-darwin-style | red-vs-blue-darwin-style | +| remote-control-cleanup | remote-control-cleanup | +| remote-control-competition | remote-control-competition | +| resistor-color | resistor-color | +| resistor-color-duo | resistor-color-duo | +| resistor-color-trio | resistor-color-trio | +| rest-api | rest-api | +| reverse-string | reverse-string | +| rle | rle | +| rna-transcription | rna-transcription | +| robot-name | robot-name | +| robot-simulator | robot-simulator | +| role-playing-game | role-playing-game | +| roll-the-die | roll-the-die | +| roman-numerals | roman-numerals | +| rotational-cipher | rotational-cipher | +| rpn-calculator | rpn-calculator | +| run-length-encoding | run-length-encoding | +| saddle-points | saddle-points | +| salary-calculator | salary-calculator | +| santas-helper | santas-helper | +| satellite | satellite | +| savings-account | savings-account | +| say | say | +| scale-generator | scale-generator | +| scrabble-score | scrabble-score | +| secret-agent | secret-agent | +| secret-handshake | secret-handshake | +| secrets | secrets | +| secure-munchester-united | secure-munchester-united | +| semi-structured-logs | semi-structured-logs | +| series | series | +| sgf-parsing | sgf-parsing | +| short-fibonacci | short-fibonacci | +| sieve | sieve | +| simple-calculator | simple-calculator | +| simple-cipher | simple-cipher | +| simple-linked-list | simple-linked-list | +| slices | slices | +| socks-and-sexprs | socks-and-sexprs | +| sorting-room | sorting-room | +| space-age | space-age | +| spiral-matrix | spiral-matrix | +| square-root | square-root | +| squeaky-clean | squeaky-clean | +| stage-heralding | stage-heralding | +| strain | strain | +| strings | strings | +| strings-package | strings-package | +| sublist | sublist | +| sum-of-multiples | sum-of-multiples | +| take-a-number | take-a-number | +| the-farm | the-farm | +| the-weather-in-deather | the-weather-in-deather | +| tim-from-marketing | tim-from-marketing | +| time | time | +| tisbury-treasure-hunt | tisbury-treasure-hunt | +| top-secret | top-secret | +| tournament | tournament | +| tracks-on-tracks-on-tracks | tracks-on-tracks-on-tracks | +| translation-service | translation-service | +| transpose | transpose | +| tree-building | tree-building | +| triangle | triangle | +| trinary | trinary | +| twelve-days | twelve-days | +| two_product_production_decision | two_product_production_decision | +| two-bucket | two-bucket | +| two-fer | two-fer | +| valentines-day | valentines-day | +| variable-length-quantity | variable-length-quantity | +| vehicle-purchase | vehicle-purchase | +| weather-forecast | weather-forecast | +| weighing-machine | weighing-machine | +| welcome-to-tech-palace | welcome-to-tech-palace | +| windowing-system | windowing-system | +| wine-cellar | wine-cellar | +| wizards-and-warriors | wizards-and-warriors | +| wizards-and-warriors-2 | wizards-and-warriors-2 | +| word-count | word-count | +| word-search | word-search | +| wordy | wordy | +| xorcism | xorcism | +| yacht | yacht | +| zebra-puzzle | zebra-puzzle | +| zero-value | zero-value | ## Key Feature Icons | Name | Icon | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| community | community | -| concurrency | concurrency | -| cross-platform | cross-platform | -| documentation | documentation | -| dynamically-typed | dynamically-typed | -| easy | easy | -| embeddable | embeddable | -| evolving | evolving | -| expressive | expressive | -| extensible | extensible | -| fast | fast | -| fun | fun | -| functional | functional | -| garbage-collected | garbage-collected | -| general-purpose | general-purpose | -| homoiconic | homoiconic | -| immutable | immutable | -| interactive | interactive | -| interop | interop | -| multi-paradigm | multi-paradigm | -| portable | portable | -| powerful | powerful | -| productive | productive | -| safe | safe | -| scientific | scientific | -| small | small | -| stable | stable | -| statically-typed | statically-typed | -| tooling | tooling | -| web | web | -| widely-used | widely-used | +| community | community | +| concurrency | concurrency | +| cross-platform | cross-platform | +| documentation | documentation | +| dynamically-typed | dynamically-typed | +| easy | easy | +| embeddable | embeddable | +| evolving | evolving | +| expressive | expressive | +| extensible | extensible | +| fast | fast | +| fun | fun | +| functional | functional | +| garbage-collected | garbage-collected | +| general-purpose | general-purpose | +| homoiconic | homoiconic | +| immutable | immutable | +| interactive | interactive | +| interop | interop | +| multi-paradigm | multi-paradigm | +| portable | portable | +| powerful | powerful | +| productive | productive | +| safe | safe | +| scientific | scientific | +| small | small | +| stable | stable | +| statically-typed | statically-typed | +| tooling | tooling | +| web | web | +| widely-used | widely-used | [exercise-icons]: /docs/building/tracks/icons#h-exercise-icons [key-feature-icons]: /docs/building/tracks/icons#h-key-feature-icons From c6c6a0d7d0ba5dba39a0a8c2093e46aa8f6f38c2 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 29 Aug 2023 15:16:36 +0200 Subject: [PATCH 020/129] Fix anchors for GHA docs (#453) --- building/github/gha-best-practices.md | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/building/github/gha-best-practices.md b/building/github/gha-best-practices.md index 84b895f1..8f298931 100644 --- a/building/github/gha-best-practices.md +++ b/building/github/gha-best-practices.md @@ -3,21 +3,21 @@ This _working_ document serves as a collection of best practices for making use of GitHub Actions. If you have any suggestions or additions, [please open a pull request on GitHub!](https://github.com/exercism/docs/edit/main/building/github/gha-best-practices.md) -- [GitHub Actions: Best Practices](#github-actions-best-practices) - - [Collection of Best Practices](#collection-of-best-practices) - - [Set timeouts for workflows](#set-timeouts-for-workflows) - - [Example](#example) - - [Consider if (third-party) actions are really needed](#consider-if-third-party-actions-are-really-needed) - - [Limit scope of workflow token](#limit-scope-of-workflow-token) - - [Example](#example-1) - - [Pin actions to SHAs](#pin-actions-to-shas) - - [Finding the commit SHA](#finding-the-commit-sha) - - [Example](#example-2) - - [Consider setting up a concurrency strategy](#consider-setting-up-a-concurrency-strategy) - - [Example](#example-3) - - [Consider which triggers are really needed](#consider-which-triggers-are-really-needed) - - [Read the "Security hardening for GitHub Actions" guide](#read-the-security-hardening-for-github-actions-guide) - - [Workflow Checklist](#workflow-checklist) +- [GitHub Actions: Best Practices](#h-github-actions-best-practices) + - [Collection of Best Practices](#h-collection-of-best-practices) + - [Set timeouts for workflows](#h-set-timeouts-for-workflows) + - [Example](#h-example) + - [Consider if (third-party) actions are really needed](#h-consider-if-third-party-actions-are-really-needed) + - [Limit scope of workflow token](#h-limit-scope-of-workflow-token) + - [Example](#h-example-1) + - [Pin actions to SHAs](#h-pin-actions-to-shas) + - [Finding the commit SHA](#h-finding-the-commit-sha) + - [Example](#h-example-2) + - [Consider setting up a concurrency strategy](#h-consider-setting-up-a-concurrency-strategy) + - [Example](#h-example-3) + - [Consider which triggers are really needed](#h-consider-which-triggers-are-really-needed) + - [Read the "Security hardening for GitHub Actions" guide](#h-read-the-security-hardening-for-github-actions-guide) + - [Workflow Checklist](#h-workflow-checklist) ## Collection of Best Practices From 42666150477ea5d2c2e85dc5a3cec067d713e9f8 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 29 Aug 2023 15:25:15 +0200 Subject: [PATCH 021/129] Add section to test runner versions (#454) --- building/github/gha-best-practices.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/building/github/gha-best-practices.md b/building/github/gha-best-practices.md index 8f298931..46439233 100644 --- a/building/github/gha-best-practices.md +++ b/building/github/gha-best-practices.md @@ -13,8 +13,10 @@ If you have any suggestions or additions, [please open a pull request on GitHub! - [Pin actions to SHAs](#h-pin-actions-to-shas) - [Finding the commit SHA](#h-finding-the-commit-sha) - [Example](#h-example-2) - - [Consider setting up a concurrency strategy](#h-consider-setting-up-a-concurrency-strategy) + - [Pin test runners to version](#h-pin-test-runners-to-version) - [Example](#h-example-3) + - [Consider setting up a concurrency strategy](#h-consider-setting-up-a-concurrency-strategy) + - [Example](#h-example-4) - [Consider which triggers are really needed](#h-consider-which-triggers-are-really-needed) - [Read the "Security hardening for GitHub Actions" guide](#h-read-the-security-hardening-for-github-actions-guide) - [Workflow Checklist](#h-workflow-checklist) @@ -103,6 +105,27 @@ You'll then be redirected to the release details page, which lists the full comm uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 ``` +### Pin test runners to version + +Most workflows will run on [GitHub's supported runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). +When using one of these runners, use a specific version instead of the latest version. + +This ensures that the workflow will always run on the same runner, which makes your build _stable_. + +#### Example + +Use: + +```yaml +runs-on: ubuntu-22.04 +``` + +instead of: + +```yaml +runs-on: ubuntu-latest +``` + ### Consider setting up a concurrency strategy It's often not necessary or useful to run CI on intermediate commits if a newer commit has been pushed in the meantime. From cbb4653bc933acc2c2cfe70cfa43a3aaf024c3b3 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Mon, 4 Sep 2023 14:05:22 +0200 Subject: [PATCH 022/129] Fix self-link in code-of-conduct.md (#456) * Fix self-link in code-of-conduct.md * Link to history instead of file Co-authored-by: Erik Schierboom --------- Co-authored-by: Erik Schierboom --- using/legal/code-of-conduct.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/using/legal/code-of-conduct.md b/using/legal/code-of-conduct.md index 268651e6..6ab0c3f7 100644 --- a/using/legal/code-of-conduct.md +++ b/using/legal/code-of-conduct.md @@ -77,6 +77,6 @@ If you say something that is found offensive, and you are called out on it, try ## History -This policy was initially adopted from the Front-end London Slack community and has been modified since. A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md). +This policy was initially adopted from the Front-end London Slack community and has been modified since. A version history can be seen on [GitHub](https://github.com/exercism/docs/commits/main/using/legal/code-of-conduct.md). _This policy is a "living" document, and subject to refinement and expansion in the future. This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Forum, Slack, Twitter, email) and any other Exercism entity or event._ From cb2e535c7a3acbad3f46f2ceb9b04c7b3140ba49 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Tue, 19 Sep 2023 18:43:31 +0200 Subject: [PATCH 023/129] Fix incorrectly ported emoji on building/tooling/analyzers/feedback-guidelines (#458) * Fix incorrectly ported emoji on building/tooling/analyzers/feedback-guidelines * Update building/tooling/analyzers/feedback-guidelines.md --------- Co-authored-by: Jeremy Walker --- .../tooling/analyzers/feedback-guidelines.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/building/tooling/analyzers/feedback-guidelines.md b/building/tooling/analyzers/feedback-guidelines.md index 232af7d7..3cf64932 100644 --- a/building/tooling/analyzers/feedback-guidelines.md +++ b/building/tooling/analyzers/feedback-guidelines.md @@ -78,7 +78,7 @@ For feedback generated by automated mentoring: important, and the last comment being the least important). It is currently undecided as to whether there is a minimum or maximum amount of -:speech_balloon: comments. However, each comment **SHOULD** be aiding the +💬 comments. However, each comment **SHOULD** be aiding the student towards an approvable solution. Our recommendation is currently to aim for one to three comments per iteration. @@ -93,46 +93,47 @@ parser or based on official rules (which means there is a dependency on correctness in tools), you should `disapprove`, preferably linking to the official rules. -> - :-1: disapprove if there are official guidelines on naming conventions -> - :speech_balloon: leave a comment if there is something noteworthy -> - :no*bell: if its a \_stylistic* preference, **and** there are _competing -> standards_, do not remark at all. Since there are competing standards, -> they're all preferences. -> - :speech*balloon: if it's a \_stylistic* preference, **and** there is _one -> clear standard_, comment on it. These rules enforce idiomatic code. -> - :question: If it's a _stylistic_ preference, **and** there is no clear -> standard, but most to all non-hobbyist have adopted the same style, this -> might be idiomatic. Comment at your discretion. +- 👎 disapprove if there are official guidelines on naming conventions +- 💬 leave a comment if there is something noteworthy +- 🔕 if its a _stylistic_ preference, **and** there are _competing + standards_, do not remark at all. Since there are competing standards, + they're all preferences. +- 💬 if it's a _stylistic_ preference, **and** there is _one + clear standard_, comment on it. These rules enforce idiomatic code. +- 🤔 If it's a _stylistic_ preference, **and** there is no clear + standard, but most to all non-hobbyist have adopted the same style, this + might be idiomatic. Comment at your discretion. #### Examples - **Ruby** has a language feature where `_` is treated differently, - - :-1: if a student uses `_` for a variable name, but then uses it. + - 👎 if a student uses `_` for a variable name, but then uses it. - **Ruby** recognizes `constants` only if they start with a **C**apital Letter, - - :-1: if a student uses `snake_case` for a `class` name + - 👎 if a student uses `snake_case` for a `class` name - **Ruby** has _de facto_ standards on `cAsInG` and `name-ing`, - - :speech_balloon: you **SHOULD** guide students that `snake_case` is to be + - 💬 you **SHOULD** guide students that `snake_case` is to be expected by most IDEs and highlighting on exercism in code blocks. - **JavaScript** IDEs highlight variables which are not used, except for those prefixed with an underscore (`_`). - - :speech*balloon: note that this behavior exist so it might help them to use - a different naming strategy. They might think that prefixing with `*`means `private`, which is not the case in JavaScript. + - 💬 note that this behavior exist so it might help them to use + a different naming strategy. They might think that prefixing with `*` means + `private`, which is not the case in JavaScript. - **TypeScript** has a _de facto_ standard lint tool provided by Palantir, - - :no_bell: If a student does not follow these rules as the lint tool is not + - 🔕 If a student does not follow these rules as the lint tool is not official. In fact, there are multiple linters out there, with mutually exclusive rules. - **Go** has very strict rules around naming and other linting. - - :-1: if the student does not follow these (e.g. has not applied `golint`) + - 👎 if the student does not follow these (e.g. has not applied `golint`) - **Go** has very strict rules around formatting. - - :-1: if the student does not follow these (e.g. has not applied `gofmt`) + - 👎 if the student does not follow these (e.g. has not applied `gofmt`) ### Badly formatted code The same rules apply as above. In general, if `linting` and a specific format is not part of the official language, and/or not integral to the language: -- :no_bell: **SHOULD NOT** disapprove on it -- :speech_balloon: You **MAY** guide students towards tools for auto-formatting +- 🔕 **SHOULD NOT** disapprove on it +- 💬 You **MAY** guide students towards tools for auto-formatting # Running guidelines From b7e0016f7cb3c50f0cae850cac613f40ba2ddd3d Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 20 Sep 2023 10:23:46 +0200 Subject: [PATCH 024/129] Add allow run time information (#459) --- building/tooling/analyzers/interface.md | 5 +++++ building/tooling/representers/interface.md | 5 +++++ building/tooling/test-runners/interface.md | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index a8996441..fb3dc773 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -11,6 +11,11 @@ All interactions with the Exercism website are handled automatically. Analyzers - A path to an output directory (with a trailing slash). This directory is writable. - The script must write an `analysis.json` file to the output directory. +### Allowed run time + +The analyzer gets 100% machine resources for a 20 second window per solution. +After 20 seconds, the process is halted and reports a time-out. + ## Output format The `analysis.json` file should be structured as followed: diff --git a/building/tooling/representers/interface.md b/building/tooling/representers/interface.md index 9166d8d3..ff3824a3 100644 --- a/building/tooling/representers/interface.md +++ b/building/tooling/representers/interface.md @@ -13,6 +13,11 @@ All interactions with the Exercism website are handled automatically. Represente - The script must write a `representation.json` file to the output directory. - The script must write a `mapping.json` file to the output directory. +### Allowed run time + +The representer gets 100% machine resources for a 20 second window per solution. +After 20 seconds, the process is halted and reports a time-out. + ## Output format ### representation.txt diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index e985755a..23ac40ac 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -13,6 +13,11 @@ All interactions with the Exercism website are handled automatically and are not - The script must write a `results.json` file to the output directory. - The runner must exit with an exit code of 0 if it has run successfully, regardless of the status of the tests. +### Allowed run time + +The test runner gets 100% machine resources for a 20 second window per solution. +After 20 seconds, the process is halted and reports a time-out. + ## Output format The following fields are supported in `results.json` files: From 413cd2a3fe10268c07f0b5ce67a77a326dc237e3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 20 Sep 2023 13:13:55 +0200 Subject: [PATCH 025/129] Update timeout values in Docker document (#461) --- building/tooling/docker.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/tooling/docker.md b/building/tooling/docker.md index be96c165..dc13b59d 100644 --- a/building/tooling/docker.md +++ b/building/tooling/docker.md @@ -33,9 +33,9 @@ If the file is larger than this, the tooling run will be killed with a 460 error ## Configuration -Each solution gets 100% machine resources for a ten second window. +Each solution gets 100% machine resources for a twenty second window. -After 10 seconds, the process is halted and reports as a time-out. +After 20 seconds, the process is halted and reports as a time-out. Configuration can be set in the [`tools.json` file](https://github.com/exercism/tooling-invoker/blob/main/tools.json) in the Tooling Invoker repository. From 1628f0bfaa9ab6688da71f5d1620800ca3e7c38b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 20 Sep 2023 13:14:19 +0200 Subject: [PATCH 026/129] Rework analyzer docs (#460) * Add backticks to analysis types * Rework feedback guidelines --- building/config.json | 7 - building/tooling/analyzers/README.md | 1 + building/tooling/analyzers/comments.md | 79 +++++----- .../tooling/analyzers/feedback-guidelines.md | 144 ------------------ building/tooling/analyzers/interface.md | 12 +- 5 files changed, 49 insertions(+), 194 deletions(-) delete mode 100644 building/tooling/analyzers/feedback-guidelines.md diff --git a/building/config.json b/building/config.json index 266c1ceb..34a7efa5 100644 --- a/building/config.json +++ b/building/config.json @@ -689,13 +689,6 @@ "title": "Analyzers", "blurb": "" }, - { - "uuid": "48775c5f-b449-4c91-b792-b81a8ef4ec04", - "slug": "tooling/analyzers/feedback-guidelines", - "path": "building/tooling/analyzers/feedback-guidelines.md", - "title": "Feedback Guidelines", - "blurb": "" - }, { "uuid": "787e4243-b97a-4b55-9038-7667e5c3788a", "slug": "tooling/analyzers/comments", diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index b9a566fa..d1775b38 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -18,3 +18,4 @@ You can use the following documents to learn more about building an analyzer: - [Creating a Analyzer from scratch](/docs/building/tooling/analyzers/creating-from-scratch) - [The Analyzer interface.](/docs/building/tooling/analyzers/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/analyzers/docker) +- [Writing Analyzer comments](/docs/building/tooling/analyzers/comments) diff --git a/building/tooling/analyzers/comments.md b/building/tooling/analyzers/comments.md index 7d40b7fb..9ffdaf7c 100644 --- a/building/tooling/analyzers/comments.md +++ b/building/tooling/analyzers/comments.md @@ -1,36 +1,18 @@ -**NOTE: This spec is currently being updated.** +# Writing Analyzer Comments ---- +This document provides information and guidelines on how to write comments produced by the analyzer. -# Comment Copy Guidelines +## Contents -This document sets guidelines for the comments produced by the analyzer. The -[Feedback guidelines](/docs/building/tooling/analyzers/feedback-guidelines) talk about _when_ to comment and -_what_ to comment. This document talks mostly about _how_ to comment. +The contents for analyzer comments are stored as Markdown document in the [exercism/website-copy][git-website-copy] repo. +Comments contain a pointer-string, formatted as `..`, that links to a specific Markdown document in the `website-copy` repo. -## Working directory for contents +For example, `ruby.two-fer.string-interpolation` refers to https://github.com/exercism/website-copy/blob/main/analyzer-comments/ruby/two-fer/string_interpolation.md. -Comments for the analyzer live in the [`exercism/website-copy`][git-website-copy] -repository. Changes should be made via a PR on this repository, only changing -the comments for one analyzer at a time. These will get the -[`type/analyzer-comments` label][git-website-copy-label]. - -### Generic comments - -Some comments are language specific and not exercise specific. These should be -extracted into a generic comments file, in order to not duplicate commentary, -which would lead to either outdated commentary or shotgun surgery when a change -is made. - -## CI - -Because the comments don't live in the same repository as the analyzer, each -analyzer should have CI that checks if the comments used in that specific -analyzer (those that can become output), are comments on the `main` branch, on -the [`exercism/website-copy`][git-website-copy] repository. - -At the moment of writing, [this issue][issue-ci-comments] tracks the status of any -generalization of this CI, if any. +```exercism/note +If a comment is language specific and _not_ exercise specific, replace `` with `general`. +E.g. `ruby.general.string-explicit_return` +``` ## Wording @@ -49,23 +31,34 @@ generalization of this CI, if any. understand x", because even though something was said, the person doesn't necessarily understand it. -### Guidance +## Guidance + +- **Aim for fluency, not proficiency**: the goal of a language track on Exercism is to give people a way to achieve a high level of [fluency](/docs/using/product/fluency) at a low level of proficiency. + We're aiming for fluency in the syntax, idioms, and the standard library of the language. +- **Suggest idiomatic code** when possible, where idiomatic is code that would be written by nearly all developers (non-hobbyists) who write code in that language. + If a non-idiomatic suggestion is made, mention this and explain why the suggestion might still be useful. +- **Name the difference** between what the person is doing, and what is 'idiomatic' in the language. +- **Use the proper terms** and nomenclature, so people can recognize those concepts elsewhere, and also can research them by themselves. +- **Don't give the solution** as general advice for all Exercism mentoring. + Learning sticks when people discover the answer for themselves. That's a tremendously exhilarating experience, and the emotional kick makes it memorable. + However, if the discovery **doesn't** trigger a dopamine hit, it makes total sense to show what it looks like. + For example, we might choose to provide a small improvement on an approved solution will be a lot less exciting than giving someone a learning point on a solution that is being disapproved and therefore may warrant an example rather than a link. +- **Order comments by importance**, the first comment being the most important, and the last comment being the least important. +- **Keep the number of comments manageable**. + Aim for one to three comments per iteration. +- **Don't add the same comment twice** in one analysis. + Adding the same comment with different parameters attached to it is _not_ considered a duplicate. +- **Consider only commenting on formatting** if formatting or linting is integral to the language. + When possible, guide students towards tools for auto-formatting and/or link to any official style guide. + +### First few exercises For the first few exercises of a track, the following is _extra_ important: -- **Name the difference** between what the person is doing, and what is - 'idiomatic' in the language. -- **Use the proper terms** and nomenclature, so people can recognize those - concepts elsewhere, and also can research them by themselves. - **Keep it relatively short**, avoiding a wall of text or overwhelming them with advice. If they have a great experience in the first exercise, they'll come back and you'll have many more opportunities to provide feedback on all of the things that you noticed. -- **Don't give the solution** as general advice for all Exercism mentoring. - Learning sticks when people discover the answer for themselves. That's a - tremendously exhilarating experience, and the emotional kick makes it - memorable. However, if the discovery **doesn't** trigger a dopamine hit, it - makes total sense to show what it looks like. For example, we might choose to provide a small improvement on an approved solution will be a lot less exciting than giving someone a learning point on a solution that is being disapproved and therefore may warrant an example rather than a link. - **Don't overly explain a concept**: don't go in depth about the underlying mechanisms of compilers and things. In this case it's more about this being one of the first exercises in the language track, and at this stage feedback @@ -135,6 +128,16 @@ on type later. For simpler cases, it's more common to rely on `errors.New` or custom errors might be interesting. ``` +## CI + +Because the comments don't live in the same repository as the analyzer, each +analyzer should have CI that checks if the comments used in that specific +analyzer (those that can become output), are comments on the `main` branch, on +the [`exercism/website-copy`][git-website-copy] repository. + +At the moment of writing, [this issue][issue-ci-comments] tracks the status of any +generalization of this CI, if any. + [git-website-copy]: https://github.com/exercism/website-copy/tree/main/analyzer-comments [issue-ci-comments]: https://github.com/exercism/automated-mentoring-support/issues/51 [git-website-copy-label]: https://github.com/exercism/website-copy/pulls?q=is%3Aopen+is%3Apr+label%3Atype%2Fanalyzer-comments diff --git a/building/tooling/analyzers/feedback-guidelines.md b/building/tooling/analyzers/feedback-guidelines.md deleted file mode 100644 index 3cf64932..00000000 --- a/building/tooling/analyzers/feedback-guidelines.md +++ /dev/null @@ -1,144 +0,0 @@ -_NOTE: This spec is currently being updated._ - -# Feedback guidelines - -The goal of a language track on Exercism is to give people a way to achieve a -high level of [fluency](/docs/using/product/fluency) at a low level of proficiency. We're aiming for fluency -in the syntax, idioms, and the standard library of the language. Read -more about the [goal of Exercism here](https://github.com/exercism/docs/blob/master/about/goal-of-exercism.md). - -## Definitions - -In the following paragraphs, keywords such as **MUST**, **SHOULD**, **MAY** -are to be interpreted as in [RFC2119](https://www.ietf.org/rfc/rfc2119.txt); -given that we recognize the following four output states and their restrictions: - -- `approve`: **MUST** be an approvable solution, **MAY** be with comment. -- `disapprove`: **MUST** be with comment -- `refer_to_mentor`: **MAY** be with comment - -### Why is it not ... (e.g. **MUST**)? - -Per [RFC2119](https://www.ietf.org/rfc/rfc2119.txt), if **MUST** is used, it is -a guarantee that the rule is always so, and does not need to be guarded for. For -example, **MUST** be without comment means that the website could crash if an -analyzer sends a comment anyway. **SHOULD** indicates any consumer of the output -must still guard against unwanted behavior. - -### Approvability - -A solution is approvable if it either matches the (set of) optimal solution(s) -of that exercise, or if it shows understanding of the teaching goals of that -exercise and every _core_ exercise that came before. This means that an -analyzer **MAY** approve a solution but still provide a set of comments as tips -relating to the exercise or further improvements. - -### Idiomatic rules / Language features / Stylistic choices - -In this document, **de facto** is defined as follows: - -- **de facto**: describes practices that exist in reality, even if not - officially recognized by laws. -- **de facto standard**: a custom or convention that has achieved a dominant - position by public acceptance or market forces. Unofficial customs that are - widely accepted. - -In other words, if a nearly all developers (non-hobbyists) who write code in a -certain language have established certain rules, these rules are a **de facto -standard** and become idiomatic use. Example: **Ruby** uses 2 space indentation. - -Some rules are language features, even if they are not documented well. These -language features are part of "idiomatic rules" and not stylistic choices. -Example: **Ruby**'s MRI treats variables named `_` differently. - -Finally there are rules that are pure preferences, even though they might be -adopted by large bodies such as organizations and corporations. These rules -are usually part of _competing_ standards. Exercism does not favor one over -another. Example: **TypeScript** has a linter `tslint` (or `eslint` + plugin) -which is maintained by a company that is not Microsoft. It competes with other -linters such as `xo`. Most of the rules are not language features or idiomatic -rules, and therefore stylistic choices. - -## Conditions and outcome - -For feedback generated by automated mentoring: - -- it **SHOULD** `disapprove` when someone isn't using one of the - subjects the exercise is supposed to teach, -- each `disapprove` **MUST** have at least one comment, -- each `disapprove` **SHOULD** steer a student towards an `approve`, -- the analyzer **SHOULD** aim to teach a pathway, which means a student - **SHOULD NOT** get a `refer_to_mentor` in a newer iteration, after a - `disapprove`, given they follow that pathway. -- a `refer_to_mentor` **MAY** have one or more comments which will be given to - the mentor, -- the same comment **MUST** not be added twice in one analysis. Adding the same - comment with different parameters attached to it is not considered a duplicate. -- comments **SHOULD** be ordered by importance (the first comment being the most - important, and the last comment being the least important). - -It is currently undecided as to whether there is a minimum or maximum amount of -💬 comments. However, each comment **SHOULD** be aiding the -student towards an approvable solution. Our recommendation is currently to aim -for one to three comments per iteration. - -## Approvability - -Given the above, and to re-iterate that we focus on language fluency: - -### Incorrect Naming or Casing - -In general, if it's a _language feature_ that will be caught by a compiler or -parser or based on official rules (which means there is a dependency on -correctness in tools), you should `disapprove`, preferably linking -to the official rules. - -- 👎 disapprove if there are official guidelines on naming conventions -- 💬 leave a comment if there is something noteworthy -- 🔕 if its a _stylistic_ preference, **and** there are _competing - standards_, do not remark at all. Since there are competing standards, - they're all preferences. -- 💬 if it's a _stylistic_ preference, **and** there is _one - clear standard_, comment on it. These rules enforce idiomatic code. -- 🤔 If it's a _stylistic_ preference, **and** there is no clear - standard, but most to all non-hobbyist have adopted the same style, this - might be idiomatic. Comment at your discretion. - -#### Examples - -- **Ruby** has a language feature where `_` is treated differently, - - 👎 if a student uses `_` for a variable name, but then uses it. -- **Ruby** recognizes `constants` only if they start with a **C**apital Letter, - - 👎 if a student uses `snake_case` for a `class` name -- **Ruby** has _de facto_ standards on `cAsInG` and `name-ing`, - - 💬 you **SHOULD** guide students that `snake_case` is to be - expected by most IDEs and highlighting on exercism in code blocks. -- **JavaScript** IDEs highlight variables which are not used, except for those - prefixed with an underscore (`_`). - - 💬 note that this behavior exist so it might help them to use - a different naming strategy. They might think that prefixing with `*` means - `private`, which is not the case in JavaScript. -- **TypeScript** has a _de facto_ standard lint tool provided by Palantir, - - 🔕 If a student does not follow these rules as the lint tool is not - official. In fact, there are multiple linters out there, with mutually - exclusive rules. -- **Go** has very strict rules around naming and other linting. - - 👎 if the student does not follow these (e.g. has not applied `golint`) -- **Go** has very strict rules around formatting. - - 👎 if the student does not follow these (e.g. has not applied `gofmt`) - -### Badly formatted code - -The same rules apply as above. In general, if `linting` and a specific format is -not part of the official language, and/or not integral to the language: - -- 🔕 **SHOULD NOT** disapprove on it -- 💬 You **MAY** guide students towards tools for auto-formatting - -# Running guidelines - -Each solution gets 100% machine resources for a ten second window. - -## Maximum runtime - -After 10 seconds, the process is halted and reports as a time-out. diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index fb3dc773..d8643202 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -44,15 +44,14 @@ The `analysis.json` file should be structured as followed: ### `summary` (optional) -The summary field is a text (not markdown) field that summarizes the output. +The `summary` field is a text (not markdown) field that summarizes the output. It might say something like "Your solution is nearly there - there's just two small changes you can make." or "The code works great, but there's a little bit of linting that needs doing.". This summary is rendered on the website above the comments. ### `comments` -Comments are keys into `website-copy/automated-comments/`, e.g. [`ruby.general.explicit_return -> automated-comments/ruby/general/explicit_return.md`](https://github.com/exercism/website-copy/blob/47af5b309ac263629ca5c52904046f81e0cc8def/automated-comments/ruby/general/explicit_return.md). - -Then can be structured either as single pointer strings (e.g. the last example above) or using a JSON Object to specify the follow keys: +The `comments` field is an array of comments that link to Markdown documents in the [`exercism/website-copy`][website-copy-repo] (see [Writing Analyzer comments][writing-analyzer-comments] for more information). +Each value in the array is either a pointer-string or a JSON object with the following format: #### `comment` @@ -77,7 +76,7 @@ The following `type`s are valid: Comments without a type field default to `informative `. -Currently in the website, we soft-block on essential comments, encourage students to complete actionable comments before marking as complete on Practice Exercises (but not Concept Exercises), but don't suggest any action on `informative` or `celebratory`. +Currently in the website, we soft-block on `essential` comments, encourage students to complete `actionable` comments before marking as complete on Practice Exercises (but not Concept Exercises), but don't suggest any action on `informative` or `celebratory`. However, in the future we may choose to add emojis or indicators to other types, or group them separately. ## Debugging @@ -85,3 +84,6 @@ However, in the future we may choose to add emojis or indicators to other types, The contents of `stdout` and `stderr` from each run will be persisted into files that can be viewed later. You may write an `analysis.out` file that contains debugging information you want to later view. + +[website-copy-repo]: https://github.com/exercise/website-copy +[writing-analyzer-comments]: /docs/building/tooling/analyzers/comments From 397366d94025ccc9587a6dfe653a3a587e953e37 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 21 Sep 2023 15:40:37 +0200 Subject: [PATCH 027/129] Clarify `error` status (#462) * Clarify `error` status * Update building/tooling/test-runners/interface.md Co-authored-by: Jeremy Walker * Update building/tooling/test-runners/interface.md Co-authored-by: Jeremy Walker * Update building/tooling/test-runners/interface.md Co-authored-by: Jeremy Walker * Update building/tooling/test-runners/interface.md --------- Co-authored-by: Jeremy Walker --- building/tooling/test-runners/interface.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index 23ac40ac..c49b391d 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -46,7 +46,11 @@ The following overall statuses are valid: - `pass`: All tests passed - `fail`: At least one test has the status `fail` or `error` -- `error`: No test was executed correctly (this usually means e.g. a compile error, or a syntax error) +- `error`: No test was executed (this usually means a compile error or a syntax error) + +The `error` status should _only_ be used if **none of the tests were run**. +For compiled languages this is generally a result of the code not being able to compile. +For interpreted languages, this is generally the result of a syntax error that stops the file from parsing. #### Message From 0b174ce1d776663dcaee9c5247003a3dcbdbd7d7 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 25 Sep 2023 20:18:15 +0100 Subject: [PATCH 028/129] Add a doc on writing a good support request (#464) * Add a doc on writing a good support request * Apply suggestions from code review Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --------- Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- community/config.json | 7 +++ .../good-member/writing-support-requests.md | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 community/good-member/writing-support-requests.md diff --git a/community/config.json b/community/config.json index 2807d880..2b6e8860 100644 --- a/community/config.json +++ b/community/config.json @@ -61,5 +61,12 @@ "path": "community/good-member/pull-requests.md", "title": "Pull Requests", "blurb": "How to make a great Pull Request" + }, + { + "uuid": "d8aff5ac-fa35-40e2-8acd-d2693f9b30b6", + "slug": "being-a-good-community-member/writing-support-requests", + "path": "community/good-member/writing-support-requests.md", + "title": "How to write a good support request", + "blurb": "Want help? Help yourself by learning how to write a good support request!" } ] diff --git a/community/good-member/writing-support-requests.md b/community/good-member/writing-support-requests.md new file mode 100644 index 00000000..0769f76a --- /dev/null +++ b/community/good-member/writing-support-requests.md @@ -0,0 +1,57 @@ +# How to write a good support request + +Want help with an Exercism? +Help boost your chances of getting a good response by learning how to write a good support request and encouraging our community to support you. + +Here are some key tips. + +## Use code blocks, not images + +When you want to share code (either that you've written, or instructions), use a code block, not an image. + +As developers, we're much quicker at reading code than having to zoom into image to see things. +And many of our users use screen readers, which cannot read out images. +So if you want to want to be a good community citizen, encourage our volunteers to help you, and look like a pro, share your code as text not an image! + +For example, you can type the following: + +```` +``` +for number in range(10): + total += number; +``` +```` + +That will get rendered like this: + +``` +for number in range(10): + total += number; +``` + +You can even get syntax highlighting by adding the language to the code block opening: + +```` +```python +for number in range(10): + total += number; +``` +```` + +which turns into: + +```python +for number in range(10): + total += number; +``` + +## Use inclusive language + +Exercism is made up of people of different genders. +If you turn up in our Discord and say things like "Hey boys", you're immediately excluding half of our community. +That means they're less likely to help you, but it means that the rest of the community are less likely to help you too, as we value inclusivity at Exercism. + +So if you want to get help, and you want to be a nice person, don't use gender-specific language. +Try "Hey folks", "Hey everyone" or go really bold and choose "Hey fellow Exercists!" + +For more on this read our article on [the words that we use](./the-words-that-we-use). From db0fdd1386b1ab42602047f3765ba55ba89d80cc Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 29 Sep 2023 10:36:50 +0200 Subject: [PATCH 029/129] Document tests.toml (#465) --- building/tracks/practice-exercises.md | 53 ++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index dc7b2644..a5518917 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -64,6 +64,7 @@ These files are _not_ presented to the student, but used to define metadata of t - `.meta/config.json`: contains meta information on the exercise (required) - `.meta/design.md`: describe the design of the exercise (optional) +- `.meta/tests.toml`: contains information on what tests are implemented (optional) ### Approach files @@ -113,8 +114,9 @@ exercises | ├── instructions.md | └── hints.md ├── .meta - | ├── config.json + | ├── config.json | ├── design.md + | ├── tests.toml | └── Example.cs (example implementation) ├── Isogram.cs (stub implementation) └── IsogramTests.cs (tests) @@ -312,6 +314,54 @@ Note that: - The order of authors and contributors is not significant and has no meaning. - `language_versions` is a free-form string that tracks are free to use and interpret as they like. +### File: .meta/tests.toml + +**Purpose:** Contains information on what tests are implemented. + +**Presence:** Optional + +This file contains information on which tests are being implemented, provided the exercise has any tests defined in its `canonical-data.json` file within the [problem-specifications repo][problem-specifications-exercises]. + +It exists to help maintainers keep track of which tests are implemented, and to (optionally) document why a certain test isn't implemented. +It can also be used to detect unimplemented tests. + +The [configlet][configlet] tool handles updating/syncing of this file with the data in the [problem-specifications repo][problem-specifications-exercises]. +When syncing, configlet will, for each unimplemented test, ask whether to include that test or not. + +#### Example + +```toml +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[3e5c30a8-87e2-4845-a815-a49671ade970] +description = "empty strand" + +[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec] +description = "can count one nucleotide in single-character input" + +[eca0d565-ed8c-43e7-9033-6cefbf5115b5] +description = "strand with repeated nucleotide" + +[40a45eac-c83f-4740-901a-20b22d15a39f] +description = "strand with multiple nucleotides" + +[b4c47851-ee9e-4b0a-be70-a86e343bd851] +description = "strand with invalid nucleotides" +include = false +comment = "error handling omitted on purpose" +``` + +--- + ### File: `.approaches/introduction.md` **Purpose:** Introduction to the most common approaches for the exercise @@ -656,3 +706,4 @@ If not, please [open an issue in the website-icons repository][website-icons-iss [exercise-icons]: /docs/building/tracks/icons#h-exercise-icons [website-icons-issues]: https://github.com/exercism/website-icons/issues [problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises +[configlet]: /docs/building/configlet From 5b76c282cd4bee22303d7d3880b608c752c22d90 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 29 Sep 2023 10:43:38 +0200 Subject: [PATCH 030/129] Link to configlet sync (#466) * Document tests.toml * Link to configlet sync --- building/tracks/practice-exercises.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index a5518917..109d5e85 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -325,7 +325,7 @@ This file contains information on which tests are being implemented, provided th It exists to help maintainers keep track of which tests are implemented, and to (optionally) document why a certain test isn't implemented. It can also be used to detect unimplemented tests. -The [configlet][configlet] tool handles updating/syncing of this file with the data in the [problem-specifications repo][problem-specifications-exercises]. +The [configlet][configlet] tool handles updating/syncing of this file with the data in the [problem-specifications repo][problem-specifications-exercises] via the [configlet sync][configlet-sync] command. When syncing, configlet will, for each unimplemented test, ask whether to include that test or not. #### Example @@ -707,3 +707,4 @@ If not, please [open an issue in the website-icons repository][website-icons-iss [website-icons-issues]: https://github.com/exercism/website-icons/issues [problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises [configlet]: /docs/building/configlet +[configlet-sync]: /docs/building/configlet/sync From 839596f45ecd8e258bbf1f76a12ac46b20facbe7 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 6 Oct 2023 15:45:45 +0200 Subject: [PATCH 031/129] Add guidance (#469) --- building/config.json | 7 +++++++ building/tooling/analyzers/README.md | 1 + building/tooling/analyzers/guidance.md | 26 +++++++++++++++++++++++++ building/tooling/analyzers/interface.md | 5 +++++ 4 files changed, 39 insertions(+) create mode 100644 building/tooling/analyzers/guidance.md diff --git a/building/config.json b/building/config.json index 34a7efa5..e6668d4c 100644 --- a/building/config.json +++ b/building/config.json @@ -710,6 +710,13 @@ "title": "Docker", "blurb": "" }, + { + "uuid": "8b527b08-52f4-4380-9d04-ebd866a09ad4", + "slug": "tooling/analyzers/guidance", + "path": "building/tooling/analyzers/guidance.md", + "title": "Guidance", + "blurb": "" + }, { "uuid": "70960db8-c1cb-4d1b-93bc-749b72fe3ee7", "slug": "tooling/analyzers/interface", diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index d1775b38..b0060dbf 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -19,3 +19,4 @@ You can use the following documents to learn more about building an analyzer: - [The Analyzer interface.](/docs/building/tooling/analyzers/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/analyzers/docker) - [Writing Analyzer comments](/docs/building/tooling/analyzers/comments) +- [Guidance for building an Analyzer](/docs/building/tooling/analyzers/guidance) diff --git a/building/tooling/analyzers/guidance.md b/building/tooling/analyzers/guidance.md new file mode 100644 index 00000000..04594ce0 --- /dev/null +++ b/building/tooling/analyzers/guidance.md @@ -0,0 +1,26 @@ +# Analyzer Guidance + +This document provides guidance when implementing an analyzer. + +## General + +- Analyze _all_ submitted files, except tests/invalidator/editor/example/exemplar files +- Don't aim for 100% correctness, which will be near impossible to achieve. + It's okay if the analyzer can't find all issues or is unable to infer certain tags. + +## Compilation + +If your language requires a compilation step, consider these points: + +- Compiling the solution will usually allow for higher fidelity. + For example, the definitive type of any value is only known _after_ compilation. + Do be aware of the trade-offs (e.g. slower to run and more memory usage). +- Consider in-memory compilation (if possible), to improve performance. + +## Testing + +- Have an extensive test suite +- Use golden tests to verify the behavior of the analyzer. + These tests should use the Docker image that will be deployed to verify the analyzer. +- Consider having tests for each approach. + You want the analyzer to work well for these solutions, and it will help assigning tags to the approaches later on. diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index d8643202..4b52e45c 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -85,5 +85,10 @@ The contents of `stdout` and `stderr` from each run will be persisted into files You may write an `analysis.out` file that contains debugging information you want to later view. +## Further reading + +Before building an analyzer, please read our [Analyzer Guidance][analyzer-guidance]. + [website-copy-repo]: https://github.com/exercise/website-copy [writing-analyzer-comments]: /docs/building/tooling/analyzers/comments +[analyzer-guidance]: /docs/building/tooling/analyzers/guidance From 451f33c509e172f4101e417be1f48d2b08649e04 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 6 Oct 2023 19:04:22 +0200 Subject: [PATCH 032/129] Document `tags` field (#468) --- building/config.json | 7 + building/configlet/lint.md | 36 ++++ building/tooling/analyzers/README.md | 1 + building/tooling/analyzers/interface.md | 23 +++ building/tooling/analyzers/tags.md | 251 ++++++++++++++++++++++++ building/tracks/concept-exercises.md | 4 + building/tracks/config-json.md | 9 +- building/tracks/practice-exercises.md | 4 + 8 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 building/tooling/analyzers/tags.md diff --git a/building/config.json b/building/config.json index e6668d4c..1b7472d7 100644 --- a/building/config.json +++ b/building/config.json @@ -724,6 +724,13 @@ "title": "The Analyzer Interface", "blurb": "" }, + { + "uuid": "4dec2835-6dc0-493b-81aa-d93cf1d6036c", + "slug": "tooling/analyzers/tags", + "path": "building/tooling/analyzers/tags.md", + "title": "Tagging solutions", + "blurb": "" + }, { "uuid": "fd89f1aa-e73b-46f0-9759-da5cc9969069", "slug": "tooling/representers", diff --git a/building/configlet/lint.md b/building/configlet/lint.md index 2bb9ce98..9d40393e 100644 --- a/building/configlet/lint.md +++ b/building/configlet/lint.md @@ -185,6 +185,19 @@ The `config.json` file should have the following checks: - The `"concepts[].slug"` value must be a kebab-case string² with length <= 255 - The `"concepts[].name"` key is required - The `"concepts[].name"` value must be a Title Case string³ with length <= 255 +- The `"concepts[].tags"` key is optional +- The `"concepts[].tags.all"` key is optional, unless `"concepts[].tags.any"` is empty +- The `"concepts[].tags.all"` value must be an array +- The `"concepts[].tags.all"` values must be non-blank tag⁷ strings with length <= 255 +- The `"concepts[].tags.all"` values must not have duplicates +- The `"concepts[].tags.any"` key is optional, unless `"concepts[].tags.all"` is empty +- The `"concepts[].tags.any"` value must be an array +- The `"concepts[].tags.any"` values must be non-blank tag⁷ strings with length <= 255 +- The `"concepts[].tags.any"` values must not have duplicates +- The `"concepts[].tags.not"` key is optional +- The `"concepts[].tags.not"` value must be an array +- The `"concepts[].tags.not"` values must be non-blank tag⁷ strings with length <= 255 +- The `"concepts[].tags.not"` values must not have duplicates - Each `"concepts"` value must have a `concept//about.md` file. Linting rules for this file are specified below. - Each `"concepts"` value must have a `concept//introduction.md` file. Linting rules for this file are specified below. - Each `"concepts"` value must have a `concept//links.json` file. Linting rules for this file are specified below. @@ -372,6 +385,19 @@ The `config.json` file should have the following checks: - The `"approaches[].contributors"` values must not have duplicates - The `"approaches[].contributors"` values are treated case-insensitively - Users can only be listed in either the `"approaches[].authors"` or `"approaches[].contributors"` array (no overlap) +- The `"approaches[].tags"` key is optional +- The `"approaches[].tags.all"` key is optional, unless `"approaches[].tags.any"` is empty +- The `"approaches[].tags.all"` value must be an array +- The `"approaches[].tags.all"` values must be non-blank tag⁷ strings with length <= 255 +- The `"approaches[].tags.all"` values must not have duplicates +- The `"approaches[].tags.any"` key is optional, unless `"approaches[].tags.all"` is empty +- The `"approaches[].tags.any"` value must be an array +- The `"approaches[].tags.any"` values must be non-blank tag⁷ strings with length <= 255 +- The `"approaches[].tags.any"` values must not have duplicates +- The `"approaches[].tags.not"` key is optional +- The `"approaches[].tags.not"` value must be an array +- The `"approaches[].tags.not"` values must be non-blank tag⁷ strings with length <= 255 +- The `"approaches[].tags.not"` values must not have duplicates ### Rule: exercises/{concept|practice}/<slug>/.approaches/<approach-slug>/content.md is valid @@ -564,3 +590,13 @@ The `config.json` file should have the following checks: ``` You can run `configlet uuid` to generate a suitable UUID. + +7. **Valid analyzer `tag`**: A non-blank string¹ that is formatted as `:`. + The `` value must be one of: + +- `paradigm` (e.g. `paradigm:functional`) +- `technique` (e.g. `technique:recursion`) +- `construct` (e.g. `construct:bitwise-and`) +- `uses` (e.g. `uses:DateTime.add_seconds`) + +The `` value must a non-blank string¹. diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index b0060dbf..22069087 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -19,4 +19,5 @@ You can use the following documents to learn more about building an analyzer: - [The Analyzer interface.](/docs/building/tooling/analyzers/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/analyzers/docker) - [Writing Analyzer comments](/docs/building/tooling/analyzers/comments) +- [Tagging solutions](/docs/building/tooling/analyzers/tags) - [Guidance for building an Analyzer](/docs/building/tooling/analyzers/guidance) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index 4b52e45c..5c01995d 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -38,6 +38,12 @@ The `analysis.json` file should be structured as followed: "comment": "ruby.general.some_unparameterised_message" }, "ruby.general.some_unparameterised_message" + ], + "tags": [ + "construct:list", + "paradigm:functional", + "technique:higher-order-functions", + "uses:List.unfold" ] } ``` @@ -79,6 +85,22 @@ Comments without a type field default to `informative `. Currently in the website, we soft-block on `essential` comments, encourage students to complete `actionable` comments before marking as complete on Practice Exercises (but not Concept Exercises), but don't suggest any action on `informative` or `celebratory`. However, in the future we may choose to add emojis or indicators to other types, or group them separately. +### `tags` (optional) + +The `tags` field is an array of strings. +Each tag is formatted as: `":"`. + +Some examples being: + +- `"paradigm:functional"` +- `"technique:recursion"` +- `"construct:bitwise-and"` +- `"uses:DateTime.add_seconds"` + +Tags can be used to identify what constructs/techniques/paradigms a solution uses. + +For more information, see [Tagging solutions][tagging-solutions]. + ## Debugging The contents of `stdout` and `stderr` from each run will be persisted into files that can be viewed later. @@ -91,4 +113,5 @@ Before building an analyzer, please read our [Analyzer Guidance][analyzer-guidan [website-copy-repo]: https://github.com/exercise/website-copy [writing-analyzer-comments]: /docs/building/tooling/analyzers/comments +[tagging-solutions]: /docs/building/tooling/analyzers/tags [analyzer-guidance]: /docs/building/tooling/analyzers/guidance diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md new file mode 100644 index 00000000..c61e8edc --- /dev/null +++ b/building/tooling/analyzers/tags.md @@ -0,0 +1,251 @@ +# Analyzer Tags + +Analyzer tags are used to identify _how_ a solution was solved. + +These tags can then be used to: + +- Link a solution to approaches +- Link a solution to concepts +- Search for solutions with certain tags + +## Format + +A tag is a string that is formatted as: `":"`. + +We support four different categories, going from highest to lowest level: + +| Category | Description | Specificity | | Examples | +| ----------- | ----------------------------------------------- | ---------------------------------------- | ------------------------------------- | -------- | +| `paradigm` | A [programming paradigm][programming-paradigms] | Very high-level, not track-specific | `imperative`, `functional` | +| `technique` | A technique being used | High-level, most won't be track-specific | `recursion`, `immutability` | +| `construct` | A language construct | Many won't be track-specific | `bitwise-and`, `for-loop` | +| `uses` | Language-specific usage, like types or methods | Low-level, track-specific | `DateTime.add_seconds`, `IEnumerable` | + +Some example tags: + +- `"paradigm:functional"` +- `"technique:recursion"` +- `"construct:bitwise-and"` +- `"uses:DateTime.add_seconds"` + +## Commonly used tags + +Whilst tracks are free to tag solutions as they see fit, we recommend trying to use the below list of commonly used tags when possible. +Using a common set of tags will allow us to do some nifty things, like cross-track linking of solutions. + +### Paradigm + +| Tag | Description | +| ---------------------------- | ---------------------------------------------------------------- | +| `"paradigm:declarative"` | Uses [declarative programming][declarative-programming]. | +| `"paradigm:functional"` | Uses [functional programming][functional-programming]. | +| `"paradigm:generic"` | Uses [generic programming][generic-programming]. | +| `"paradigm:imperative"` | Uses [imperative programming][imperative-programming]. | +| `"paradigm:logic"` | Uses [logic programming][logic-programming]. | +| `"paradigm:meta"` | Uses [metaprogramming][metaprogramming]. | +| `"paradigm:object-oriented"` | Uses [object-oriented programming][object-oriented-programming]. | +| `"paradigm:procedural"` | Uses [procedural programming][procedural-programming]. | +| `"paradigm:reflective"` | Uses [reflective programming][reflective-programming]. | + +### Techniques + +| Tag | Description | +| ------------------------------------ | ------------------------------------------------------------------------------ | +| `"technique:bit-manipulation"` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | +| `"technique:bit-shifting"` | Using bit shifting (special case of `"technique:bit-manipulation"`) | +| `"technique:boolean-logic"` | Using boolean logic (AND, OR, NOT) | +| `"technique:concurrency"` | Using concurrency | +| `"technique:enumeration"` | Enumerating over values | +| `"technique:exceptions"` | Working with exceptions | +| `"technique:higher-order-functions"` | Using higher-order functions | +| `"technique:immutability"` | Using immutability | +| `"technique:immutable-collection"` | Using immutable collections (special case of `"technique:immutability"`) | +| `"technique:inheritance"` | Using inheritance | +| `"technique:laziness"` | Using laziness, where values are produced only when needed | +| `"technique:locks"` | Using locks to get exclusive access to resources | +| `"technique:looping"` | Using loops | +| `"technique:memory-management"` | Managing memory | +| `"technique:ordering"` | Ordering data | +| `"technique:parallelism"` | Running code in parallel | +| `"technique:pointers"` | Using pointers | +| `"technique:randomness"` | Using randomness | +| `"technique:recursion"` | Using recursion | +| `"technique:regular-expression"` | Using regular expressions | +| `"technique:sorted-collection"` | Using sorted collections (special case of `"technique:sorting"`) | +| `"technique:sorting"` | Sorting data | +| `"technique:type-conversion"` | Converting values from one type to another type | +| `"technique:unsafe"` | Using unsafe code, e.g. pointer arithmetic | + +### Constructs + +| Tag | Description | +| -------------------------------- | --------------------------------------------------------------- | +| `construct:assignment` | Assif | +| `construct:catch` | Catch an exception | +| `construct:constant` | A constant (immutable) value | +| `construct:constructor` | A constructor | +| `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | +| `construct:extension-method` | An extension method | +| `construct:field` | A field | +| `construct:generic-method` | A method that is parameterized with one or more types | +| `construct:generic-type` | A type that is parameterized with one or more types | +| `construct:getter` | A getter | +| `construct:implicit-conversion` | Implicitly convert from one type to another type | +| `construct:indexer` | An indexer | +| `construct:invocation` | An invocation of a method/function | +| `construct:lambda` | A lambda (aka an "anonymous function") | +| `construct:nested-function` | A nested function | +| `construct:logical-and` | A logical AND | +| `construct:logical-not` | A logical NOT | +| `construct:logical-or` | A logical OR | +| `construct:method` | A method | +| `construct:method-overloading` | Method overloading | +| `construct:module` | A module (grouping of code) | +| `construct:named-argument` | An argument passed by name | +| `construct:namespace` | A namespace (grouping of code) | +| `construct:nested-type` | A nested type | +| `construct:nullability` | Nullability, dealing with `null` values | +| `construct:operator-overloading` | Operator overloading | +| `construct:optional-parameter` | An optional parameter (doesn't have to be passed) | +| `construct:parameter` | A parameter | +| `construct:pattern-matching` | Pattern matching | +| `construct:property` | A property (getter/setter) | +| `construct:setter` | A setter | +| `construct:throw` | Throw/raise an exception | +| `construct:try` | Explicitly handle an exception | +| `construct:type-inference` | Automatically infer the type of a value | +| `construct:import` | Import functionality (e.g. from a namespace/module) | +| `construct:varargs` | Allow passing in zero or more values for a parameter | +| `construct:variable` | A variable | +| `construct:visibility-modifiers` | Determine the visibility of a method/function | + +#### Control flow + +| Tag | Description | +| -------------------------------- | -------------------------------------------- | +| `construct:async-await` | An `async`/`await` statement | +| `construct:break` | Break from a loop | +| `construct:conditional-operator` | A ternary conditional operator | +| `construct:continue` | Continue to the next iteration of a loop | +| `construct:do-while-loop` | A `do-while` loop | +| `construct:finally` | Ensure that a certain code block always runs | +| `construct:for-loop` | A `for` loop | +| `construct:foreach` | A `foreach` loop | +| `construct:if` | An `if` statement | +| `construct:return` | Return from a function/method | +| `construct:switch` | A `switch` | +| `construct:while-loop` | A `while` loop | +| `construct:yield` | Yield a value in a loop | + +#### Arithmetic + +| Tag | Description | +| ------------------------------- | -------------------------------------------- | +| `construct:add` | Addition | +| `construct:bitwise-and` | Bitwise AND | +| `construct:bitwise-left-shift` | Bitwise left shift | +| `construct:bitwise-not` | Bitwise NOT | +| `construct:bitwise-or` | Bitwise OR | +| `construct:bitwise-right-shift` | Bitwise right shift | +| `construct:bitwise-xor` | Bitwise XOR | +| `construct:checked-arithmetic` | Checked arithmetic (guard against overflows) | +| `construct:divide` | Division | +| `construct:multiply` | Multiplication | +| `construct:overflow` | Arithmetic overflow | +| `construct:subtract` | Subtraction | + +#### Value types + +| Tag | Description | +| --------------------- | ---------------------------------------------------------- | +| `construct:boolean` | A boolean | +| `construct:char` | A character | +| `construct:date-time` | A combination of date + time | +| `construct:date` | A date (no time) | +| `construct:enum` | An enum (enumeration of values) | +| `construct:null` | Represents the absence of a value (something called `nil`) | +| `construct:number` | A number (signed or unsigned) | +| `construct:string` | A string | +| `construct:time` | A time (no date) | + +#### Integral types + +| Tag | Description | +| --------------------------- | ----------------------------------------------------- | +| `construct:big-integer` | A big integer | +| `construct:byte` | An unsigned 8-bit integer | +| `construct:int` | A signed 32-bit integer | +| `construct:integral-number` | An integral number (integer) | +| `construct:long` | A signed 64-bit integer | +| `construct:nint` | A signed platform-specific integer (32- or 64-bit) | +| `construct:nuint` | An unsigned platform-specific integer (32- or 64-bit) | +| `construct:sbyte` | A signed 8-bit integer | +| `construct:short` | A signed 16-bit integer | +| `construct:uint` | An unsigned 32-bit integer | +| `construct:ulong` | An unsigned 64-bit integer | +| `construct:ushort` | An unsigned 16-bit integer | + +#### Floating-point types + +| Tag | Description | +| --------------------------------- | ------------------------------- | +| `construct:decimal` | A 128-bit floating-point number | +| `construct:double` | A 64-bit floating-point number | +| `construct:float` | A 32-bit floating-point number | +| `construct:floating-point-number` | A floating-point number | + +#### Composite types + +| Tag | Description | +| ---------------------------------- | ---------------------------------------------------------------- | +| `construct:class` | A class | +| `construct:exception` | An exception | +| `construct:interface` | An interface | +| `construct:option` | An option type | +| `construct:record` | A record (usually immutable) | +| `construct:struct` | A struct | +| `construct:sum-type` | A sum type | +| `construct:tuple` | A tuple | +| `construct:union-type` | A union type | +| `construct:user-defined-exception` | A user-defined exception (special case of `construct:exception`) | + +#### Collection types + +| Tag | Description | +| ----------------------- | --------------------------------------------------- | +| `construct:array` | An array | +| `construct:bit-array` | A bit array (similar to an array but works on bits) | +| `construct:dictionary` | A dictionary (aka map) | +| `construct:linked-list` | A linked list (special case of `construct:list`) | +| `construct:list` | A list | +| `construct:queue` | A queue | +| `construct:set` | A set | +| `construct:stack` | A stack | + +#### Notation + +| Tag | Description | +| -------------------------------------- | ----------------------------------------------- | +| `construct:binary-number` | A number in binary notation | +| `construct:hexadecimal-number` | A number in hexadecimal notation | +| `construct-multiline-string` | A multiline string | +| `construct:octal-number` | A number in octal notation | +| `construct:scientific-notation-number` | A number in scientific notation | +| `construct-string-interpolation` | An interpolated string | +| `construct:underscored-number` | A number using underscores as a digit separator | +| `construct-verbatim-string` | A verbatim string (no escape sequences) | + +### Uses + +As this category is language-specific, there are no commonly used tags here. + +[declarative-programming]: https://en.wikipedia.org/wiki/Declarative_programming +[logic-programming]: https://en.wikipedia.org/wiki/Logic_programming +[object-oriented-programming]: https://en.wikipedia.org/wiki/Object-oriented_programming +[reflective-programming]: https://en.wikipedia.org/wiki/Reflective_programming +[metaprogramming]: https://en.wikipedia.org/wiki/Metaprogramming +[generic-programming]: https://en.wikipedia.org/wiki/Generic_programming +[imperative-programming]: https://en.wikipedia.org/wiki/Imperative_programming +[functional-programming]: https://en.wikipedia.org/wiki/Functional_programming +[procedural-programming]: https://en.wikipedia.org/wiki/Procedural_programming +[programming-paradigms]: https://en.wikipedia.org/wiki/Programming_paradigm diff --git a/building/tracks/concept-exercises.md b/building/tracks/concept-exercises.md index edc74090..337b2863 100644 --- a/building/tracks/concept-exercises.md +++ b/building/tracks/concept-exercises.md @@ -439,6 +439,10 @@ This file contains meta information on the exercise's approaches: - Including reviewers if their reviews substantially change the exercise approach (to the extent where it feels like "you got there together") - `contributors`: The GitHub username(s) of the exercise approach's contributor(s) (optional) - Including reviewers if their reviews are meaningful/actionable/actioned. + - `tags`: Specify the conditions for when a submission is linked to an approach. (optional) + - `all`: An array of tags that must all be present on a submission (optional, unless `any` has no elements) + - `any`: An array of tags of which at least one must be present on a submission (optional, unless `all` has no elements) + - `not`: none of the tags must be present on a submission (optional) #### Example diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index 5e03f66f..85ff09a1 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -231,6 +231,10 @@ Each concept is an entry in the top-level `concepts` array. The following fields - `uuid`: a V4 UUID that uniquely identifies the concept. The UUID must be unique both within the track as well as across all tracks, and must never change - `slug`: the concept's slug, which is a lowercased, kebab-case string. The slug must be unique across all concepts within the track. Its length must be <= 255. - `name`: the concept's name. Its length must be <= 255. +- `tags`: Specify the conditions for when a submission is linked to an approach. (optional) + - `all`: An array of tags that must all be present on a submission (optional, unless `any` has no elements) + - `any`: An array of tags of which at least one must be present on a submission (optional, unless `all` has no elements) + - `not`: none of the tags must be present on a submission (optional) ### Example @@ -240,7 +244,10 @@ Each concept is an entry in the top-level `concepts` array. The following fields { "uuid": "b9a421b2-c5ff-4213-bd6d-b886da31ea0d", "slug": "numbers", - "name": "Numbers" + "name": "Numbers", + "tags": { + "all": ["concept:number"] + } } ] } diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 109d5e85..4cc8f42a 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -421,6 +421,10 @@ This file contains meta information on the exercise's approaches: - Including reviewers if their reviews substantially change the exercise approach (to the extent where it feels like "you got there together") - `contributors`: The GitHub username(s) of the exercise approach's contributor(s) (optional) - Including reviewers if their reviews are meaningful/actionable/actioned. + - `tags`: Specify the conditions for when a submission is linked to an approach. (optional) + - `all`: An array of tags that must all be present on a submission (optional, unless `any` has no elements) + - `any`: An array of tags of which at least one must be present on a submission (optional, unless `all` has no elements) + - `not`: none of the tags must be present on a submission (optional) #### Example From c93d4b760ce0097dcfeb000d7617c49a34defec9 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 13 Oct 2023 12:06:29 +0200 Subject: [PATCH 033/129] Use separate file for analyzer tags (#470) --- building/tooling/analyzers/interface.md | 36 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index 5c01995d..07159203 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -10,6 +10,7 @@ All interactions with the Exercism website are handled automatically. Analyzers - A path to a directory containing the submitted file(s) (with a trailing slash). - A path to an output directory (with a trailing slash). This directory is writable. - The script must write an `analysis.json` file to the output directory. +- The script must write a `tags.json` file to the output directory. ### Allowed run time @@ -18,6 +19,8 @@ After 20 seconds, the process is halted and reports a time-out. ## Output format +### analysis.json + The `analysis.json` file should be structured as followed: ```json @@ -38,32 +41,26 @@ The `analysis.json` file should be structured as followed: "comment": "ruby.general.some_unparameterised_message" }, "ruby.general.some_unparameterised_message" - ], - "tags": [ - "construct:list", - "paradigm:functional", - "technique:higher-order-functions", - "uses:List.unfold" ] } ``` -### `summary` (optional) +#### `summary` (optional) The `summary` field is a text (not markdown) field that summarizes the output. It might say something like "Your solution is nearly there - there's just two small changes you can make." or "The code works great, but there's a little bit of linting that needs doing.". This summary is rendered on the website above the comments. -### `comments` +#### `comments` The `comments` field is an array of comments that link to Markdown documents in the [`exercism/website-copy`][website-copy-repo] (see [Writing Analyzer comments][writing-analyzer-comments] for more information). Each value in the array is either a pointer-string or a JSON object with the following format: -#### `comment` +##### `comment` The pointer-string to a file in `website-copy`. -#### `params` (optional) +##### `params` (optional) A JSON Object containing any params that should be interpolated during rendering. For example, in the markdown file, you could write `Try %{variable_name} += 1 instead`, and then set `params` to `{ "variable_name": "foo"}` in order to substitute `%{variable_name}` for the actual variable that the student used. @@ -71,7 +68,7 @@ For example, in the markdown file, you could write `Try %{variable_name} += 1 in When using parameterized files, ensure to escape all uses of `%` by placing anther `%` in front of it. e.g. `Try aim aim for 100%% of the tests passing`. -#### `type` (optional) +##### `type` (optional) The following `type`s are valid: @@ -85,7 +82,22 @@ Comments without a type field default to `informative `. Currently in the website, we soft-block on `essential` comments, encourage students to complete `actionable` comments before marking as complete on Practice Exercises (but not Concept Exercises), but don't suggest any action on `informative` or `celebratory`. However, in the future we may choose to add emojis or indicators to other types, or group them separately. -### `tags` (optional) +### tags.json + +The `tags.json` file should be structured as followed: + +```json +{ + "tags": [ + "construct:list", + "paradigm:functional", + "technique:higher-order-functions", + "uses:List.unfold" + ] +} +``` + +#### `tags` The `tags` field is an array of strings. Each tag is formatted as: `":"`. From 396fd7fe1b27485ce8503b6ce81722a1e8f771cd Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 17 Oct 2023 08:27:55 +0200 Subject: [PATCH 034/129] Make writing tags.json optional --- building/tooling/analyzers/interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index 07159203..f4f69660 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -10,7 +10,7 @@ All interactions with the Exercism website are handled automatically. Analyzers - A path to a directory containing the submitted file(s) (with a trailing slash). - A path to an output directory (with a trailing slash). This directory is writable. - The script must write an `analysis.json` file to the output directory. -- The script must write a `tags.json` file to the output directory. +- The script should write a `tags.json` file to the output directory. ### Allowed run time From e6e11c7830f15f703052ea84f9a9850cf3e980d1 Mon Sep 17 00:00:00 2001 From: Adrien LUDWIG <42720099+Adrien-LUDWIG@users.noreply.github.com> Date: Wed, 25 Oct 2023 08:18:28 +0200 Subject: [PATCH 035/129] Fix typos (#471) --- building/configlet/README.md | 2 +- building/markdown/style-guide.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/building/configlet/README.md b/building/configlet/README.md index 13f947e2..6babe588 100644 --- a/building/configlet/README.md +++ b/building/configlet/README.md @@ -19,7 +19,7 @@ How these documents are generated can be found [here](/docs/building/configlet/g ## Syncing exercise data with the problem-specifications repo -The tertiary function of configlet is to various data for practice exercises. +The tertiary function of configlet is to provide various data for practice exercises. A [Practice Exercise](/docs/building/tracks/practice-exercises) on an Exercism track is often implemented from a specification in the [`exercism/problem-specifications`](https://github.com/exercism/problem-specifications) repo. diff --git a/building/markdown/style-guide.md b/building/markdown/style-guide.md index 9357fc25..92ddd527 100644 --- a/building/markdown/style-guide.md +++ b/building/markdown/style-guide.md @@ -44,7 +44,7 @@ Here are some example rules: - Instead of describing an interface as "RESTful", describe its specific properties - If you must use "CRUD", explain that it stands for Create, Read, Update, Delete and that these are the basic actions in standard databases -An some examples of good usage: +And some examples of good usage: - "HyperText Markup Language (HTML) is the language used to describe document structure and content on the web" _(expanded and explained)_ - "DNA, a set of chemical instructions that influence how our bodies are constructed" _(DNA is not expanded because "deoxyribonucleic acid" is unlikely to help explain what DNA is to our audience)_ @@ -72,7 +72,7 @@ Many American English style guides state that the abbreviations "i.e." and "e.g. ## Choice of words -### Mathematical and jargon terms. +### Mathematical and jargon terms In any place that mathematical terms are used they should be explained or substituted out for terms that require less domain knowledge. From 7f0d6a52eb198f88ffaedd4d5ac42c2fdb9443a4 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 25 Oct 2023 10:16:28 +0200 Subject: [PATCH 036/129] Fix links on request new track page (#472) --- building/tracks/new/request-new.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/building/tracks/new/request-new.md b/building/tracks/new/request-new.md index 21d3d619..ad9b119a 100644 --- a/building/tracks/new/request-new.md +++ b/building/tracks/new/request-new.md @@ -5,6 +5,7 @@ Please follow the instructions below to request a new Language Track. If you wish to request a new track, **you must post in the [Support category on the Exercism Community Forum][forum-support]** to discuss this with the team. Before opening the post, it's worth considering: + - Is it a programming language? - Does Exercism already support the language? - Has anyone else asked for it? @@ -38,6 +39,7 @@ Exercism is a not-for-profit organization, and all the language tracks are built We will only create a track if there is at least one volunteer who has offered to take the lead on building it. The minimum to launch a track is: + - 20 practice exercises (these can be based on specifications in the [problem-specifications][problem-specifications] repository. - Continuous integration that checks that the exercises can be solved. - A bit of configuration and documentation. @@ -50,4 +52,6 @@ Awesome!! Please create a new post in the [Exercism Forum][forum-support] so we [forum-support]: https://forum.exercism.org/c/support/8 [generic-track-repo]: https://github.com/exercism/generic-track/issues -[preblem-specifications]: https://github.com/exercism/problem-specifications +[problem-specifications]: https://github.com/exercism/problem-specifications +[track-repositories]: https://github.com/topics/exercism-track +[exercism-tracks]: https://exercism.org/tracks/ From 0ee57b2e6929bab7d3e9644ab6de88aef1518984 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Thu, 26 Oct 2023 09:35:06 +0100 Subject: [PATCH 037/129] Update tags.md Fix formatting --- building/tooling/analyzers/tags.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index c61e8edc..f9c8f39c 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -14,8 +14,8 @@ A tag is a string that is formatted as: `":"`. We support four different categories, going from highest to lowest level: -| Category | Description | Specificity | | Examples | -| ----------- | ----------------------------------------------- | ---------------------------------------- | ------------------------------------- | -------- | +| Category | Description | Specificity | Examples | +| ----------- | ----------------------------------------------- | ---------------------------------------- | --------------------------- | | `paradigm` | A [programming paradigm][programming-paradigms] | Very high-level, not track-specific | `imperative`, `functional` | | `technique` | A technique being used | High-level, most won't be track-specific | `recursion`, `immutability` | | `construct` | A language construct | Many won't be track-specific | `bitwise-and`, `for-loop` | From e625102c32b426c755d6640f1cae1810b07dfa67 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 31 Oct 2023 13:55:21 +0100 Subject: [PATCH 038/129] Add remark on line endings (#473) --- building/tooling/representers/normalization.md | 1 + 1 file changed, 1 insertion(+) diff --git a/building/tooling/representers/normalization.md b/building/tooling/representers/normalization.md index 67a804c6..0f59edcd 100644 --- a/building/tooling/representers/normalization.md +++ b/building/tooling/representers/normalization.md @@ -50,6 +50,7 @@ public static class PLACEHOLDER_1 ## Normalize white space Inconsistent white space is so frequent, that normalizing it is a common normalization step. +Line endings should also be normalized. ### Source code From 0320b193c64be01b4cc07226feb7d3335e798499 Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Tue, 31 Oct 2023 12:44:56 -0700 Subject: [PATCH 039/129] tags.md: drop the sometimes-quotes around tags (#474) --- building/tooling/analyzers/tags.md | 84 +++++++++++++++--------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index f9c8f39c..eb20039a 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -10,7 +10,7 @@ These tags can then be used to: ## Format -A tag is a string that is formatted as: `":"`. +A tag is a string that is formatted as: `:`. We support four different categories, going from highest to lowest level: @@ -23,10 +23,10 @@ We support four different categories, going from highest to lowest level: Some example tags: -- `"paradigm:functional"` -- `"technique:recursion"` -- `"construct:bitwise-and"` -- `"uses:DateTime.add_seconds"` +- `paradigm:functional` +- `technique:recursion` +- `construct:bitwise-and` +- `uses:DateTime.add_seconds` ## Commonly used tags @@ -35,46 +35,46 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra ### Paradigm -| Tag | Description | -| ---------------------------- | ---------------------------------------------------------------- | -| `"paradigm:declarative"` | Uses [declarative programming][declarative-programming]. | -| `"paradigm:functional"` | Uses [functional programming][functional-programming]. | -| `"paradigm:generic"` | Uses [generic programming][generic-programming]. | -| `"paradigm:imperative"` | Uses [imperative programming][imperative-programming]. | -| `"paradigm:logic"` | Uses [logic programming][logic-programming]. | -| `"paradigm:meta"` | Uses [metaprogramming][metaprogramming]. | -| `"paradigm:object-oriented"` | Uses [object-oriented programming][object-oriented-programming]. | -| `"paradigm:procedural"` | Uses [procedural programming][procedural-programming]. | -| `"paradigm:reflective"` | Uses [reflective programming][reflective-programming]. | +| Tag | Description | +| -------------------------- | ---------------------------------------------------------------- | +| `paradigm:declarative` | Uses [declarative programming][declarative-programming]. | +| `paradigm:functional` | Uses [functional programming][functional-programming]. | +| `paradigm:generic` | Uses [generic programming][generic-programming]. | +| `paradigm:imperative` | Uses [imperative programming][imperative-programming]. | +| `paradigm:logic` | Uses [logic programming][logic-programming]. | +| `paradigm:meta` | Uses [metaprogramming][metaprogramming]. | +| `paradigm:object-oriented` | Uses [object-oriented programming][object-oriented-programming]. | +| `paradigm:procedural` | Uses [procedural programming][procedural-programming]. | +| `paradigm:reflective` | Uses [reflective programming][reflective-programming]. | ### Techniques -| Tag | Description | -| ------------------------------------ | ------------------------------------------------------------------------------ | -| `"technique:bit-manipulation"` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | -| `"technique:bit-shifting"` | Using bit shifting (special case of `"technique:bit-manipulation"`) | -| `"technique:boolean-logic"` | Using boolean logic (AND, OR, NOT) | -| `"technique:concurrency"` | Using concurrency | -| `"technique:enumeration"` | Enumerating over values | -| `"technique:exceptions"` | Working with exceptions | -| `"technique:higher-order-functions"` | Using higher-order functions | -| `"technique:immutability"` | Using immutability | -| `"technique:immutable-collection"` | Using immutable collections (special case of `"technique:immutability"`) | -| `"technique:inheritance"` | Using inheritance | -| `"technique:laziness"` | Using laziness, where values are produced only when needed | -| `"technique:locks"` | Using locks to get exclusive access to resources | -| `"technique:looping"` | Using loops | -| `"technique:memory-management"` | Managing memory | -| `"technique:ordering"` | Ordering data | -| `"technique:parallelism"` | Running code in parallel | -| `"technique:pointers"` | Using pointers | -| `"technique:randomness"` | Using randomness | -| `"technique:recursion"` | Using recursion | -| `"technique:regular-expression"` | Using regular expressions | -| `"technique:sorted-collection"` | Using sorted collections (special case of `"technique:sorting"`) | -| `"technique:sorting"` | Sorting data | -| `"technique:type-conversion"` | Converting values from one type to another type | -| `"technique:unsafe"` | Using unsafe code, e.g. pointer arithmetic | +| Tag | Description | +| ---------------------------------- | ------------------------------------------------------------------------------ | +| `technique:bit-manipulation` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | +| `technique:bit-shifting` | Using bit shifting (special case of `technique:bit-manipulation`) | +| `technique:boolean-logic` | Using boolean logic (AND, OR, NOT) | +| `technique:concurrency` | Using concurrency | +| `technique:enumeration` | Enumerating over values | +| `technique:exceptions` | Working with exceptions | +| `technique:higher-order-functions` | Using higher-order functions | +| `technique:immutability` | Using immutability | +| `technique:immutable-collection` | Using immutable collections (special case of `technique:immutability`) | +| `technique:inheritance` | Using inheritance | +| `technique:laziness` | Using laziness, where values are produced only when needed | +| `technique:locks` | Using locks to get exclusive access to resources | +| `technique:looping` | Using loops | +| `technique:memory-management` | Managing memory | +| `technique:ordering` | Ordering data | +| `technique:parallelism` | Running code in parallel | +| `technique:pointers` | Using pointers | +| `technique:randomness` | Using randomness | +| `technique:recursion` | Using recursion | +| `technique:regular-expression` | Using regular expressions | +| `technique:sorted-collection` | Using sorted collections (special case of `technique:sorting`) | +| `technique:sorting` | Sorting data | +| `technique:type-conversion` | Converting values from one type to another type | +| `technique:unsafe` | Using unsafe code, e.g. pointer arithmetic | ### Constructs From 5a0d2cc00af30b374885f4133dc806bc996ccfe8 Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Fri, 3 Nov 2023 06:13:33 +0100 Subject: [PATCH 040/129] Update tags.md (#475) --- building/tooling/analyzers/tags.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index eb20039a..ab18e945 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -228,12 +228,12 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | -------------------------------------- | ----------------------------------------------- | | `construct:binary-number` | A number in binary notation | | `construct:hexadecimal-number` | A number in hexadecimal notation | -| `construct-multiline-string` | A multiline string | +| `construct:multiline-string` | A multiline string | | `construct:octal-number` | A number in octal notation | | `construct:scientific-notation-number` | A number in scientific notation | -| `construct-string-interpolation` | An interpolated string | +| `construct:string-interpolation` | An interpolated string | | `construct:underscored-number` | A number using underscores as a digit separator | -| `construct-verbatim-string` | A verbatim string (no escape sequences) | +| `construct:verbatim-string` | A verbatim string (no escape sequences) | ### Uses From 63e7a75267c2b62a89a9cce518b0e25dfeb8c57e Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 7 Nov 2023 11:34:15 +0100 Subject: [PATCH 041/129] Document max length for `message` field (#476) --- building/tooling/test-runners/interface.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index c49b391d..bfce0b62 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -67,7 +67,9 @@ Where the status is `error` (no test was executed correctly), the top level `mes In Ruby, in the case of a syntax error, we provide the runtime error and stack trace. In compiled languages, the compilation error should be provided. -The top level `message` value is not limited in length. +The top level `message` value is limited to 65535 characters. +The effective maximum length is less if the value contains multibyte characters. + When the status is not `error`, either set the value to `null` or omit the key entirely. #### Tests From a00424ddd2fc3d81bd284f4b73c71a082f11cc02 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 23 Nov 2023 09:39:49 +0100 Subject: [PATCH 042/129] Update config.json formatting (#477) --- building/tracks/config-json.md | 209 +++++++++++++++++++++++---------- 1 file changed, 147 insertions(+), 62 deletions(-) diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index 85ff09a1..83d2343b 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -27,13 +27,13 @@ The following top-level properties contain general track metadata: - `exemplar`: exemplar implementation file(s) pattern (optional) - `editor`: additional read-only editor file(s) patterns (optional) - `test_runner`: an object describing the track's test runner (if any): (required if `status.test_runner` is `true`) - - `average_run_time`: a `number` value for the number of seconds the test runner takes on average to run, rounded to one decimal point precision (e.g. `1.8`) (required if `status.test_runner` is `true`) + - `average_run_time`: an integer `number` value for the number of seconds the test runner takes on average to run (e.g. `4`) (required if `status.test_runner` is `true`) - `approaches`: an object with metadata on the track's approaches: (required if the track has any approaches) - `snippet_extension`: a string value used for the snippet file's extension (e.g. `rb`) (required if the track has any approaches) ### Files -This key is used to specify track-wide file locations. Rather than maintainers having to manually set the `files` key in the _exercises_' `config.json` files, support will be added to [configlet](/docs/building/configlet) to use these track-wide patterns to automatically populate the exercises' `files` key. +This key is used to specify track-wide file locations. Rather than maintainers having to manually set the `files` key in the _exercises_' `config.json` files, [configlet](/docs/building/configlet) can automatically populate it using these track-wide patterns. The file patterns defined in the `files` object support the following placeholders: @@ -46,38 +46,45 @@ Support will be added to [configlet](/docs/building/configlet) to use these patt ### Example + ```json { "language": "C#", "slug": "csharp", "active": true, - "blurb": "C# is a modern, object-oriented language with lots of great features, such as type-inference and async/await. The tooling is excellent, and there is extensive, well-written documentation.", - "version": 3, - "online_editor": { - "indent_style": "space", - "indent_size": 4, - "highlightjs_language": "csharp" - }, "status": { "concept_exercises": true, "test_runner": true, "representer": false, "analyzer": false }, - "files": { - "solution": ["%{pascal_slug}.cs"], - "test": ["%{pascal_slug}Tests.cs"], - "example": [".meta/Example.cs"], - "exemplar": [".meta/Exemplar.cs"] + "blurb": "C# is a modern, object-oriented language with lots of great features, such as type-inference and async/await. The tooling is excellent, and there is extensive, well-written documentation.", + "version": 3, + "online_editor": { + "indent_style": "space", + "indent_size": 4, + "highlightjs_language": "csharp" }, "test_runner": { - "average_run_time": 2.3 + "average_run_time": 2 }, - "approaches": { - "snippet_extension": "cs" + "files": { + "solution": [ + "%{pascal_slug}.cs" + ], + "test": [ + "%{pascal_slug}Tests.cs" + ], + "example": [ + ".meta/Example.cs" + ], + "exemplar": [ + ".meta/Exemplar.cs" + ] } } ``` + ## Exercises @@ -106,35 +113,48 @@ The following fields make up a concept exercise: #### Example + ```json { "exercises": { "concept": [ { - "uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9", "slug": "cars-assemble", "name": "Cars, Assemble!", - "concepts": ["if-statements", "numbers"], - "prerequisites": ["basics"] + "uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9", + "concepts": [ + "if-statements", + "numbers" + ], + "prerequisites": [ + "basics" + ] }, ... ] } } ``` + #### Example of work-in-progress + ```json { "exercises": { "concept": [ { - "uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9", "slug": "cars-assemble", "name": "Cars, Assemble!", - "concepts": ["if-statements", "numbers"], - "prerequisites": ["basics"], + "uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9", + "concepts": [ + "if-statements", + "numbers" + ], + "prerequisites": [ + "basics" + ], "status": "wip" }, ... @@ -142,6 +162,7 @@ The following fields make up a concept exercise: } } ``` + ### Practice exercises @@ -166,16 +187,24 @@ The "Recommended Order" of the Practice Exercises on the website corresponds wit #### Example + ```json { "exercises": { "practice": [ { - "uuid": "8ba15933-29a2-49b1-a9ce-70474bad3007", "slug": "leap", "name": "Leap", - "practices": ["if-statements", "numbers", "operator-precedence"], - "prerequisites": ["if-statements", "numbers"], + "uuid": "8ba15933-29a2-49b1-a9ce-70474bad3007", + "practices": [ + "if-statements", + "numbers", + "operator-precedence" + ], + "prerequisites": [ + "if-statements", + "numbers" + ], "difficulty": 1 }, ... @@ -183,19 +212,28 @@ The "Recommended Order" of the Practice Exercises on the website corresponds wit } } ``` + #### Example of beta + ```json { "exercises": { "practice": [ { - "uuid": "8ba15933-29a2-49b1-a9ce-70474bad3007", "slug": "leap", "name": "Leap", - "practices": ["if-statements", "numbers", "operator-precedence"], - "prerequisites": ["if-statements", "numbers"], + "uuid": "8ba15933-29a2-49b1-a9ce-70474bad3007", + "practices": [ + "if-statements", + "numbers", + "operator-precedence" + ], + "prerequisites": [ + "if-statements", + "numbers" + ], "difficulty": 1, "status": "beta" }, @@ -204,6 +242,7 @@ The "Recommended Order" of the Practice Exercises on the website corresponds wit } } ``` + ### Foregone exercises @@ -216,13 +255,17 @@ Reasons for why an track might _not_ want to implement an exercise could be: #### Example + ```json { "exercises": { - "foregone": ["lens-person"] + "foregone": [ + "lens-person" + ] } } ``` + ## Concepts @@ -238,6 +281,7 @@ Each concept is an entry in the top-level `concepts` array. The following fields ### Example + ```json { "concepts": [ @@ -246,12 +290,15 @@ Each concept is an entry in the top-level `concepts` array. The following fields "slug": "numbers", "name": "Numbers", "tags": { - "all": ["concept:number"] + "all": [ + "concept:number" + ] } } ] } ``` + ## Key features @@ -302,6 +349,7 @@ Exactly 6 key features must be specified. ### Example + ```json { "key_features": [ @@ -311,9 +359,10 @@ Exactly 6 key features must be specified. "icon": "safe" }, ... - ] + ], } ``` + ## Tags @@ -382,43 +431,60 @@ Note that it is perfectly fine to include multiple tags from a single category. Example + ```json { "tags": [ - "runtime/jvm", - "platform/windows", - "platform/linux", "paradigm/declarative", "paradigm/functional", - "paradigm/object_oriented" + "paradigm/object_oriented", + "platform/linux", + "platform/windows", + "runtime/jvm" ] } ``` + ### Example This is an example of what a valid `config.json` file can look like: + ```json { "language": "C#", "slug": "csharp", "active": true, - "blurb": "C# is a modern, object-oriented language with lots of great features, such as type-inference and async/await. The tooling is excellent, and there is extensive, well-written documentation.", - "version": 3, "status": { "concept_exercises": true, "test_runner": true, "representer": false, "analyzer": false }, + "blurb": "C# is a modern, object-oriented language with lots of great features, such as type-inference and async/await. The tooling is excellent, and there is extensive, well-written documentation.", + "version": 3, "online_editor": { "indent_style": "space", "indent_size": 4, "highlightjs_language": "csharp" }, "test_runner": { - "average_run_time": 2.3 + "average_run_time": 2 + }, + "files": { + "solution": [ + "%{pascal_slug}.cs" + ], + "test": [ + "%{pascal_slug}Tests.cs" + ], + "example": [ + ".meta/Example.cs" + ], + "exemplar": [ + ".meta/Exemplar.cs" + ] }, "exercises": { "concept": [ @@ -426,15 +492,22 @@ This is an example of what a valid `config.json` file can look like: "slug": "lucians-luscious-lasagna", "name": "Lucian's Luscious Lasagna", "uuid": "7d358894-4fbd-4c91-b49f-d68f1c5aa6bc", - "concepts": ["basics"], + "concepts": [ + "basics" + ], "prerequisites": [] }, { "slug": "cars-assemble", "name": "Cars, Assemble!", "uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9", - "concepts": ["if-statements", "numbers"], - "prerequisites": ["basics"], + "concepts": [ + "if-statements", + "numbers" + ], + "prerequisites": [ + "basics" + ], "status": "wip" } ], @@ -443,16 +516,27 @@ This is an example of what a valid `config.json` file can look like: "slug": "hello-world", "name": "Hello, World!", "uuid": "6c88f46b-5acb-4fae-a6ec-b48ae3f8168f", - "practices": ["strings"], - "prerequisites": ["basics"], + "practices": [ + "strings" + ], + "prerequisites": [ + "basics" + ], "difficulty": 1 }, { "slug": "leap", "name": "Leap", "uuid": "8ba15933-29a2-49b1-a9ce-70474bad3007", - "practices": ["if-statements", "numbers", "operator-precedence"], - "prerequisites": ["if-statements", "numbers"], + "practices": [ + "if-statements", + "numbers", + "operator-precedence" + ], + "prerequisites": [ + "if-statements", + "numbers" + ], "difficulty": 2, "status": "beta" } @@ -482,45 +566,46 @@ This is an example of what a valid `config.json` file can look like: ], "key_features": [ { - "icon": "expressive", "title": "Modern", - "content": "C# is a modern, fast-evolving language." + "content": "C# is a modern, fast-evolving language.", + "icon": "expressive" }, { - "icon": "cross-platform", "title": "Cross-platform", - "content": "C# runs on almost any platform and chipset." + "content": "C# runs on almost any platform and chipset.", + "icon": "cross-platform" }, { - "icon": "multi-paradigm", "title": "Multi-paradigm", - "content": "C# is primarily an object-oriented language, but also has lots of functional features." + "content": "C# is primarily an object-oriented language, but also has lots of functional features.", + "icon": "multi-paradigm" }, { - "icon": "general-purpose", "title": "General purpose", - "content": "C# can be used for a wide variety of workloads, like websites, console applications, and even games." + "content": "C# can be used for a wide variety of workloads, like websites, console applications, and even games.", + "icon": "general-purpose" }, { - "icon": "tooling", "title": "Tooling", - "content": "C# has excellent tooling, with linting and advanced refactoring options built-in." + "content": "C# has excellent tooling, with linting and advanced refactoring options built-in.", + "icon": "tooling" }, { - "icon": "documentation", "title": "Documentation", - "content": "Documentation is excellent and exhaustive, making it easy to get started with C#." + "content": "Documentation is excellent and exhaustive, making it easy to get started with C#.", + "icon": "documentation" } ], "tags": [ - "runtime/jvm", - "platform/windows", - "platform/linux", "paradigm/declarative", "paradigm/functional", - "paradigm/object_oriented" + "paradigm/object_oriented", + "platform/linux", + "platform/windows", + "runtime/jvm" ] } ``` + [key-feature-icons]: /docs/building/tracks/icons#h-key-feature-icons From 3bcac27803b8dfbde8db9b8ddf01ab80463e0e14 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 23 Nov 2023 15:04:52 +0100 Subject: [PATCH 043/129] Add more tags (#478) --- building/tooling/analyzers/tags.md | 172 +++++++++++++++++------------ 1 file changed, 103 insertions(+), 69 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index ab18e945..7fe5a594 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -14,8 +14,8 @@ A tag is a string that is formatted as: `:`. We support four different categories, going from highest to lowest level: -| Category | Description | Specificity | Examples | -| ----------- | ----------------------------------------------- | ---------------------------------------- | --------------------------- | +| Category | Description | Specificity | Examples | +| ----------- | ----------------------------------------------- | ---------------------------------------- | ------------------------------------- | | `paradigm` | A [programming paradigm][programming-paradigms] | Very high-level, not track-specific | `imperative`, `functional` | | `technique` | A technique being used | High-level, most won't be track-specific | `recursion`, `immutability` | | `construct` | A language construct | Many won't be track-specific | `bitwise-and`, `for-loop` | @@ -46,20 +46,21 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `paradigm:object-oriented` | Uses [object-oriented programming][object-oriented-programming]. | | `paradigm:procedural` | Uses [procedural programming][procedural-programming]. | | `paradigm:reflective` | Uses [reflective programming][reflective-programming]. | +| `paradigm:stack-oriented` | Uses [stack-oriented programming][stack-oriented-programming]. | ### Techniques | Tag | Description | | ---------------------------------- | ------------------------------------------------------------------------------ | | `technique:bit-manipulation` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | -| `technique:bit-shifting` | Using bit shifting (special case of `technique:bit-manipulation`) | +| `technique:bit-shifting` | Using bit shifting (special case of `technique:bit-manipulation`) | | `technique:boolean-logic` | Using boolean logic (AND, OR, NOT) | | `technique:concurrency` | Using concurrency | | `technique:enumeration` | Enumerating over values | | `technique:exceptions` | Working with exceptions | | `technique:higher-order-functions` | Using higher-order functions | | `technique:immutability` | Using immutability | -| `technique:immutable-collection` | Using immutable collections (special case of `technique:immutability`) | +| `technique:immutable-collection` | Using immutable collections (special case of `technique:immutability`) | | `technique:inheritance` | Using inheritance | | `technique:laziness` | Using laziness, where values are produced only when needed | | `technique:locks` | Using locks to get exclusive access to resources | @@ -71,53 +72,64 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:randomness` | Using randomness | | `technique:recursion` | Using recursion | | `technique:regular-expression` | Using regular expressions | -| `technique:sorted-collection` | Using sorted collections (special case of `technique:sorting`) | +| `technique:short-circuiting` | Use short-circuiting to prevent unnecessary evaluation | +| `technique:sorted-collection` | Using sorted collections (special case of `technique:sorting`) | | `technique:sorting` | Sorting data | | `technique:type-conversion` | Converting values from one type to another type | | `technique:unsafe` | Using unsafe code, e.g. pointer arithmetic | ### Constructs -| Tag | Description | -| -------------------------------- | --------------------------------------------------------------- | -| `construct:assignment` | Assif | -| `construct:catch` | Catch an exception | -| `construct:constant` | A constant (immutable) value | -| `construct:constructor` | A constructor | -| `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | -| `construct:extension-method` | An extension method | -| `construct:field` | A field | -| `construct:generic-method` | A method that is parameterized with one or more types | -| `construct:generic-type` | A type that is parameterized with one or more types | -| `construct:getter` | A getter | -| `construct:implicit-conversion` | Implicitly convert from one type to another type | -| `construct:indexer` | An indexer | -| `construct:invocation` | An invocation of a method/function | -| `construct:lambda` | A lambda (aka an "anonymous function") | -| `construct:nested-function` | A nested function | -| `construct:logical-and` | A logical AND | -| `construct:logical-not` | A logical NOT | -| `construct:logical-or` | A logical OR | -| `construct:method` | A method | -| `construct:method-overloading` | Method overloading | -| `construct:module` | A module (grouping of code) | -| `construct:named-argument` | An argument passed by name | -| `construct:namespace` | A namespace (grouping of code) | -| `construct:nested-type` | A nested type | -| `construct:nullability` | Nullability, dealing with `null` values | -| `construct:operator-overloading` | Operator overloading | -| `construct:optional-parameter` | An optional parameter (doesn't have to be passed) | -| `construct:parameter` | A parameter | -| `construct:pattern-matching` | Pattern matching | -| `construct:property` | A property (getter/setter) | -| `construct:setter` | A setter | -| `construct:throw` | Throw/raise an exception | -| `construct:try` | Explicitly handle an exception | -| `construct:type-inference` | Automatically infer the type of a value | -| `construct:import` | Import functionality (e.g. from a namespace/module) | -| `construct:varargs` | Allow passing in zero or more values for a parameter | -| `construct:variable` | A variable | -| `construct:visibility-modifiers` | Determine the visibility of a method/function | +| Tag | Description | +| -------------------------------------------- | --------------------------------------------------------------- | +| `construct:abstract-method` | An abstract method | +| `construct:assignment` | Assign/bind a value to a variable/name | +| `construct:catch` | Catch an exception | +| `construct:constant` | A constant (immutable) value | +| `construct:constructor` | A constructor | +| `construct:default-interface-implementation` | A default implementation in an interface | +| `construct:default` | A default value (e.g. for a parameter) | +| `construct:equality` | Compare equality of two values | +| `construct:event` | An event | +| `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | +| `construct:extension-method` | An extension method | +| `construct:field` | A field | +| `construct:generic-function` | A function that is parameterized with one or more types | +| `construct:generic-method` | A method that is parameterized with one or more types | +| `construct:generic-type` | A type that is parameterized with one or more types | +| `construct:getter` | A getter | +| `construct:implicit-conversion` | Implicitly convert from one type to another type | +| `construct:import` | Import functionality (e.g. from a namespace/module) | +| `construct:indexer` | An indexer | +| `construct:inequality` | Compare inequality of two values | +| `construct:invocation` | An invocation of a method/function | +| `construct:lambda` | A lambda (aka an "anonymous function") | +| `construct:logical-and` | A logical AND | +| `construct:logical-not` | A logical NOT | +| `construct:logical-or` | A logical OR | +| `construct:method-overloading` | Method overloading | +| `construct:method-override` | An overridden method | +| `construct:method` | A method | +| `construct:module` | A module (grouping of code) | +| `construct:multiple-dispatch` | Multiple dispatch | +| `construct:named-argument` | An argument passed by name | +| `construct:namespace` | A namespace (grouping of code) | +| `construct:nested-function` | A nested function | +| `construct:nested-type` | A nested type | +| `construct:nullability` | Nullability, dealing with `null` values | +| `construct:operator-overloading` | Operator overloading | +| `construct:optional-parameter` | An optional parameter (doesn't have to be passed) | +| `construct:parameter` | A parameter | +| `construct:pattern-matching` | Pattern matching | +| `construct:property` | A property (getter/setter) | +| `construct:setter` | A setter | +| `construct:throw` | Throw/raise an exception | +| `construct:try` | Explicitly handle an exception | +| `construct:type-inference` | Automatically infer the type of a value | +| `construct:varargs` | Allow passing in zero or more values for a parameter | +| `construct:variable` | A variable | +| `construct:virtual-method` | A virtual method | +| `construct:visibility-modifiers` | Determine the visibility of a method/function | #### Control flow @@ -125,6 +137,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | -------------------------------- | -------------------------------------------- | | `construct:async-await` | An `async`/`await` statement | | `construct:break` | Break from a loop | +| `construct:conditional-access` | Conditionally access a method | | `construct:conditional-operator` | A ternary conditional operator | | `construct:continue` | Continue to the next iteration of a loop | | `construct:do-while-loop` | A `do-while` loop | @@ -139,20 +152,37 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra #### Arithmetic -| Tag | Description | -| ------------------------------- | -------------------------------------------- | -| `construct:add` | Addition | -| `construct:bitwise-and` | Bitwise AND | -| `construct:bitwise-left-shift` | Bitwise left shift | -| `construct:bitwise-not` | Bitwise NOT | -| `construct:bitwise-or` | Bitwise OR | -| `construct:bitwise-right-shift` | Bitwise right shift | -| `construct:bitwise-xor` | Bitwise XOR | -| `construct:checked-arithmetic` | Checked arithmetic (guard against overflows) | -| `construct:divide` | Division | -| `construct:multiply` | Multiplication | -| `construct:overflow` | Arithmetic overflow | -| `construct:subtract` | Subtraction | +| Tag | Description | +| ------------------------------------------ | -------------------------------------------- | +| `construct:add-assignment` | Addition and assignment combined | +| `construct:add` | Addition | +| `construct:bitwise-and-assignment` | Bitwise AND and assignment combined | +| `construct:bitwise-and` | Bitwise AND | +| `construct:bitwise-left-shift-assignment` | Bitwise left shift and assignment combined | +| `construct:bitwise-left-shift` | Bitwise left shift | +| `construct:bitwise-not` | Bitwise NOT | +| `construct:bitwise-or-assignment` | Bitwise OR and assignment combined | +| `construct:bitwise-or` | Bitwise OR | +| `construct:bitwise-right-shift-assignment` | Bitwise right shift and assignment combined | +| `construct:bitwise-right-shift` | Bitwise right shift | +| `construct:bitwise-xor-assignment` | Bitwise XOR and assignment combined | +| `construct:bitwise-xor` | Bitwise XOR | +| `construct:checked-arithmetic` | Checked arithmetic (guard against overflows) | +| `construct:divide-assignment` | Division and assignment combined | +| `construct:divide` | Division | +| `construct:modulo-assignment` | Division remainder and assignment combined | +| `construct:modulo` | Division remainder | +| `construct:multiply-assignment` | Multiplication and assignment combined | +| `construct:multiply` | Multiplication | +| `construct:overflow` | Arithmetic overflow | +| `construct:postfix-decrement` | Decrement a value using postfix notation | +| `construct:postfix-increment` | Increment a value using postfix notation | +| `construct:prefix-decrement` | Decrement a value using prefix notation | +| `construct:prefix-increment` | Increment a value using prefix notation | +| `construct:subtract-assignment` | Subtraction and assignment combined | +| `construct:subtract` | Subtraction | +| `construct:unary-minus` | Unary minus | +| `construct:unary-plus` | Unary plus | #### Value types @@ -198,6 +228,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | Tag | Description | | ---------------------------------- | ---------------------------------------------------------------- | +| `construct:abstract-class` | An abstract class | | `construct:class` | A class | | `construct:exception` | An exception | | `construct:interface` | An interface | @@ -211,16 +242,18 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra #### Collection types -| Tag | Description | -| ----------------------- | --------------------------------------------------- | -| `construct:array` | An array | -| `construct:bit-array` | A bit array (similar to an array but works on bits) | -| `construct:dictionary` | A dictionary (aka map) | -| `construct:linked-list` | A linked list (special case of `construct:list`) | -| `construct:list` | A list | -| `construct:queue` | A queue | -| `construct:set` | A set | -| `construct:stack` | A stack | +| Tag | Description | +| ----------------------------------- | --------------------------------------------------- | +| `construct:array` | An array | +| `construct:bit-array` | A bit array (similar to an array but works on bits) | +| `construct:dictionary` | A dictionary (aka map) | +| `construct:jagged-array` | A jagged array (an array of arrays) | +| `construct:linked-list` | A linked list (special case of `construct:list`) | +| `construct:list` | A list | +| `construct:multi-dimensional-array` | An array with multiple dimensions | +| `construct:queue` | A queue | +| `construct:set` | A set | +| `construct:stack` | A stack | #### Notation @@ -249,3 +282,4 @@ As this category is language-specific, there are no commonly used tags here. [functional-programming]: https://en.wikipedia.org/wiki/Functional_programming [procedural-programming]: https://en.wikipedia.org/wiki/Procedural_programming [programming-paradigms]: https://en.wikipedia.org/wiki/Programming_paradigm +[stack-oriented-programming]: https://en.wikipedia.org/wiki/Stack-oriented_programming From 4e6541cc3aac9a2a9f2a3e966576a2aa3e2c4263 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 28 Nov 2023 16:01:16 +0100 Subject: [PATCH 044/129] Add `construct:else` --- building/tooling/analyzers/tags.md | 1 + 1 file changed, 1 insertion(+) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 7fe5a594..00b881cf 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -141,6 +141,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:conditional-operator` | A ternary conditional operator | | `construct:continue` | Continue to the next iteration of a loop | | `construct:do-while-loop` | A `do-while` loop | +| `construct:else` | An `else` statement | | `construct:finally` | Ensure that a certain code block always runs | | `construct:for-loop` | A `for` loop | | `construct:foreach` | A `foreach` loop | From b555597c91f6131c9660631d1f575af309629f0b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 29 Nov 2023 09:02:57 +0100 Subject: [PATCH 045/129] Add type test --- building/tooling/analyzers/tags.md | 1 + 1 file changed, 1 insertion(+) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 00b881cf..35a53313 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -126,6 +126,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:throw` | Throw/raise an exception | | `construct:try` | Explicitly handle an exception | | `construct:type-inference` | Automatically infer the type of a value | +| `construct:type-test` | Test if a value has a specific type | | `construct:varargs` | Allow passing in zero or more values for a parameter | | `construct:variable` | A variable | | `construct:virtual-method` | A virtual method | From d82fc065009ba4720834c623fb68ccb47d7356de Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 29 Nov 2023 13:34:26 +0100 Subject: [PATCH 046/129] Add more tags --- building/tooling/analyzers/tags.md | 78 +++++++++++++++++++----------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 35a53313..ad9d8a9f 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -55,6 +55,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:bit-manipulation` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | | `technique:bit-shifting` | Using bit shifting (special case of `technique:bit-manipulation`) | | `technique:boolean-logic` | Using boolean logic (AND, OR, NOT) | +| `technique:composition` | Using composition | | `technique:concurrency` | Using concurrency | | `technique:enumeration` | Enumerating over values | | `technique:exceptions` | Working with exceptions | @@ -65,6 +66,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:laziness` | Using laziness, where values are produced only when needed | | `technique:locks` | Using locks to get exclusive access to resources | | `technique:looping` | Using loops | +| `technique:math` | Using math | | `technique:memory-management` | Managing memory | | `technique:ordering` | Ordering data | | `technique:parallelism` | Running code in parallel | @@ -84,16 +86,23 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | -------------------------------------------- | --------------------------------------------------------------- | | `construct:abstract-method` | An abstract method | | `construct:assignment` | Assign/bind a value to a variable/name | +| `construct:boxing` | A boxed value | | `construct:catch` | Catch an exception | +| `construct:comment` | A comment | +| `construct:comparison` | Compare two values for their relative order | +| `construct:concatenation` | Concatenate two values | | `construct:constant` | A constant (immutable) value | | `construct:constructor` | A constructor | +| `construct:coroutine` | A coroutine | | `construct:default-interface-implementation` | A default implementation in an interface | | `construct:default` | A default value (e.g. for a parameter) | +| `construct:destructuring` | Decontruct a value into its parts | | `construct:equality` | Compare equality of two values | | `construct:event` | An event | | `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | | `construct:extension-method` | An extension method | | `construct:field` | A field | +| `construct:function` | A function | | `construct:generic-function` | A function that is parameterized with one or more types | | `construct:generic-method` | A method that is parameterized with one or more types | | `construct:generic-type` | A type that is parameterized with one or more types | @@ -114,6 +123,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:multiple-dispatch` | Multiple dispatch | | `construct:named-argument` | An argument passed by name | | `construct:namespace` | A namespace (grouping of code) | +| `construct:nesting` | Use nesting | | `construct:nested-function` | A nested function | | `construct:nested-type` | A nested type | | `construct:nullability` | Nullability, dealing with `null` values | @@ -123,8 +133,11 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:pattern-matching` | Pattern matching | | `construct:property` | A property (getter/setter) | | `construct:setter` | A setter | +| `construct:static-field` | A static field | +| `construct:static-method` | A static method | | `construct:throw` | Throw/raise an exception | | `construct:try` | Explicitly handle an exception | +| `construct:type-alias` | An alias for a type | | `construct:type-inference` | Automatically infer the type of a value | | `construct:type-test` | Test if a value has a specific type | | `construct:varargs` | Allow passing in zero or more values for a parameter | @@ -134,23 +147,28 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra #### Control flow -| Tag | Description | -| -------------------------------- | -------------------------------------------- | -| `construct:async-await` | An `async`/`await` statement | -| `construct:break` | Break from a loop | -| `construct:conditional-access` | Conditionally access a method | -| `construct:conditional-operator` | A ternary conditional operator | -| `construct:continue` | Continue to the next iteration of a loop | -| `construct:do-while-loop` | A `do-while` loop | -| `construct:else` | An `else` statement | -| `construct:finally` | Ensure that a certain code block always runs | -| `construct:for-loop` | A `for` loop | -| `construct:foreach` | A `foreach` loop | -| `construct:if` | An `if` statement | -| `construct:return` | Return from a function/method | -| `construct:switch` | A `switch` | -| `construct:while-loop` | A `while` loop | -| `construct:yield` | Yield a value in a loop | +| Tag | Description | +| -------------------------------- | ------------------------------------------------------ | +| `construct:async-await` | An `async`/`await` statement | +| `construct:break` | Break from a loop | +| `construct:conditional-access` | Conditionally access a method | +| `construct:conditional-operator` | A ternary conditional operator | +| `construct:continue` | Continue to the next iteration of a loop | +| `construct:do-while-loop` | A `do-while` loop | +| `construct:else` | An `else` statement | +| `construct:finally` | Ensure that a certain code block always runs | +| `construct:for-loop` | A `for` loop | +| `construct:foreach` | A `foreach` loop | +| `construct:if` | An `if` statement | +| `construct:loop` | A loop | +| `construct:pipe-backward` | A backward pipe | +| `construct:pipe-forward` | A forward pipe | +| `construct:pipeline` | A pipeline | +| `construct:point-free` | Define functions without the arguments they operate on | +| `construct:return` | Return from a function/method | +| `construct:switch` | A `switch` | +| `construct:while-loop` | A `while` loop | +| `construct:yield` | Yield a value in a loop | #### Arithmetic @@ -177,6 +195,8 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:multiply-assignment` | Multiplication and assignment combined | | `construct:multiply` | Multiplication | | `construct:overflow` | Arithmetic overflow | +| `construct:decrement` | Decrement a value | +| `construct:increment` | Increment a value | | `construct:postfix-decrement` | Decrement a value using postfix notation | | `construct:postfix-increment` | Increment a value using postfix notation | | `construct:prefix-decrement` | Decrement a value using prefix notation | @@ -188,17 +208,19 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra #### Value types -| Tag | Description | -| --------------------- | ---------------------------------------------------------- | -| `construct:boolean` | A boolean | -| `construct:char` | A character | -| `construct:date-time` | A combination of date + time | -| `construct:date` | A date (no time) | -| `construct:enum` | An enum (enumeration of values) | -| `construct:null` | Represents the absence of a value (something called `nil`) | -| `construct:number` | A number (signed or unsigned) | -| `construct:string` | A string | -| `construct:time` | A time (no date) | +| Tag | Description | +| ------------------------------ | ---------------------------------------------------------- | +| `construct:boolean` | A boolean | +| `construct:char` | A character | +| `construct:date-time` | A combination of date + time | +| `construct:date` | A date (no time) | +| `construct:enum` | An enum (enumeration of values) | +| `construct:null` | Represents the absence of a value (something called `nil`) | +| `construct:number` | A number (signed or unsigned) | +| `construct:string` | A string | +| `construct:time` | A time (no date) | +| `construct:pointer` | A pointer | +| `construct:regular-expression` | A regular expression | #### Integral types From 357b5c2d13a32d621604281a9c02031651757f5f Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 29 Nov 2023 15:25:13 +0100 Subject: [PATCH 047/129] Yet more tags --- building/tooling/analyzers/tags.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index ad9d8a9f..90280bba 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -58,6 +58,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:composition` | Using composition | | `technique:concurrency` | Using concurrency | | `technique:enumeration` | Enumerating over values | +| `technique:error-handling` | Handling errors | | `technique:exceptions` | Working with exceptions | | `technique:higher-order-functions` | Using higher-order functions | | `technique:immutability` | Using immutability | @@ -77,6 +78,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:short-circuiting` | Use short-circuiting to prevent unnecessary evaluation | | `technique:sorted-collection` | Using sorted collections (special case of `technique:sorting`) | | `technique:sorting` | Sorting data | +| `technique:tail-call-optimization` | Use tail-call optimization for efficient recursion | | `technique:type-conversion` | Converting values from one type to another type | | `technique:unsafe` | Using unsafe code, e.g. pointer arithmetic | @@ -97,47 +99,61 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:default-interface-implementation` | A default implementation in an interface | | `construct:default` | A default value (e.g. for a parameter) | | `construct:destructuring` | Decontruct a value into its parts | +| `construct:discard` | Discard a result | | `construct:equality` | Compare equality of two values | | `construct:event` | An event | | `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | +| `construct:explicit-import` | Explicitly import only the specified functionality | | `construct:extension-method` | An extension method | | `construct:field` | A field | +| `construct:field-access` | Access a field | +| `construct:function-overloading` | Function overloading | | `construct:function` | A function | +| `construct:generator` | A function or method that produces values | | `construct:generic-function` | A function that is parameterized with one or more types | | `construct:generic-method` | A method that is parameterized with one or more types | | `construct:generic-type` | A type that is parameterized with one or more types | | `construct:getter` | A getter | | `construct:implicit-conversion` | Implicitly convert from one type to another type | | `construct:import` | Import functionality (e.g. from a namespace/module) | +| `construct:indexing` | Accessing a value by index | | `construct:indexer` | An indexer | | `construct:inequality` | Compare inequality of two values | | `construct:invocation` | An invocation of a method/function | +| `construct:iterator` | A function or method that can be iterated over | | `construct:lambda` | A lambda (aka an "anonymous function") | | `construct:logical-and` | A logical AND | | `construct:logical-not` | A logical NOT | | `construct:logical-or` | A logical OR | +| `construct:macro` | A macro | +| `construct:method-chaining` | Chains several method calls | | `construct:method-overloading` | Method overloading | | `construct:method-override` | An overridden method | | `construct:method` | A method | | `construct:module` | A module (grouping of code) | +| `construct:multiple-assignment` | Assign multiple values at once | | `construct:multiple-dispatch` | Multiple dispatch | | `construct:named-argument` | An argument passed by name | | `construct:namespace` | A namespace (grouping of code) | -| `construct:nesting` | Use nesting | | `construct:nested-function` | A nested function | | `construct:nested-type` | A nested type | +| `construct:nesting` | Use nesting | | `construct:nullability` | Nullability, dealing with `null` values | | `construct:operator-overloading` | Operator overloading | | `construct:optional-parameter` | An optional parameter (doesn't have to be passed) | | `construct:parameter` | A parameter | +| `construct:parenthesized-expression` | A parenthesized expression | | `construct:pattern-matching` | Pattern matching | | `construct:property` | A property (getter/setter) | | `construct:setter` | A setter | | `construct:static-field` | A static field | | `construct:static-method` | A static method | +| `construct:string-formatting` | Building a string via a format string | | `construct:throw` | Throw/raise an exception | | `construct:try` | Explicitly handle an exception | | `construct:type-alias` | An alias for a type | +| `construct:type-conversion` | Converts/casts a value to another type | +| `construct:type-extension` | Extend a type with new functionality | | `construct:type-inference` | Automatically infer the type of a value | | `construct:type-test` | Test if a value has a specific type | | `construct:varargs` | Allow passing in zero or more values for a parameter | @@ -217,6 +233,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:enum` | An enum (enumeration of values) | | `construct:null` | Represents the absence of a value (something called `nil`) | | `construct:number` | A number (signed or unsigned) | +| `construct:complex-number` | A complex number | | `construct:string` | A string | | `construct:time` | A time (no date) | | `construct:pointer` | A pointer | @@ -258,6 +275,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:interface` | An interface | | `construct:option` | An option type | | `construct:record` | A record (usually immutable) | +| `construct:result` | A result type | | `construct:struct` | A struct | | `construct:sum-type` | A sum type | | `construct:tuple` | A tuple | @@ -276,7 +294,9 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:list` | A list | | `construct:multi-dimensional-array` | An array with multiple dimensions | | `construct:queue` | A queue | +| `construct:range` | A range | | `construct:set` | A set | +| `construct:slice` | A subset of a collection | | `construct:stack` | A stack | #### Notation From 38d090d93a6b24602a959c8b810c0eb85cd04ed5 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 29 Nov 2023 15:25:39 +0100 Subject: [PATCH 048/129] Minor fix --- building/tooling/analyzers/tags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 90280bba..6f12ba46 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -99,7 +99,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `construct:default-interface-implementation` | A default implementation in an interface | | `construct:default` | A default value (e.g. for a parameter) | | `construct:destructuring` | Decontruct a value into its parts | -| `construct:discard` | Discard a result | +| `construct:discard` | Discard a value | | `construct:equality` | Compare equality of two values | | `construct:event` | An event | | `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | From b18c04f91f0a0fb15f9fc4785f4f40d730e570f0 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 1 Dec 2023 10:10:35 +0100 Subject: [PATCH 049/129] Update list of tags --- building/tooling/analyzers/tags.md | 549 +++++++++++++++-------------- 1 file changed, 281 insertions(+), 268 deletions(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 6f12ba46..09f3bcc6 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -37,280 +37,293 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | Tag | Description | | -------------------------- | ---------------------------------------------------------------- | -| `paradigm:declarative` | Uses [declarative programming][declarative-programming]. | -| `paradigm:functional` | Uses [functional programming][functional-programming]. | -| `paradigm:generic` | Uses [generic programming][generic-programming]. | -| `paradigm:imperative` | Uses [imperative programming][imperative-programming]. | -| `paradigm:logic` | Uses [logic programming][logic-programming]. | -| `paradigm:meta` | Uses [metaprogramming][metaprogramming]. | -| `paradigm:object-oriented` | Uses [object-oriented programming][object-oriented-programming]. | -| `paradigm:procedural` | Uses [procedural programming][procedural-programming]. | -| `paradigm:reflective` | Uses [reflective programming][reflective-programming]. | -| `paradigm:stack-oriented` | Uses [stack-oriented programming][stack-oriented-programming]. | +| `paradigm:declarative` | uses [declarative programming][declarative-programming]. | +| `paradigm:functional` | uses [functional programming][functional-programming]. | +| `paradigm:generic` | uses [generic programming][generic-programming]. | +| `paradigm:imperative` | uses [imperative programming][imperative-programming]. | +| `paradigm:logic` | uses [logic programming][logic-programming]. | +| `paradigm:meta` | uses [metaprogramming][metaprogramming]. | +| `paradigm:object-oriented` | uses [object-oriented programming][object-oriented-programming]. | +| `paradigm:procedural` | uses [procedural programming][procedural-programming]. | +| `paradigm:reflective` | uses [reflective programming][reflective-programming]. | +| `paradigm:stack-oriented` | uses [stack-oriented programming][stack-oriented-programming]. | ### Techniques -| Tag | Description | -| ---------------------------------- | ------------------------------------------------------------------------------ | -| `technique:bit-manipulation` | Manipulating bits, usually via bitwise operators (e.g. AND, XOR or left shift) | -| `technique:bit-shifting` | Using bit shifting (special case of `technique:bit-manipulation`) | -| `technique:boolean-logic` | Using boolean logic (AND, OR, NOT) | -| `technique:composition` | Using composition | -| `technique:concurrency` | Using concurrency | -| `technique:enumeration` | Enumerating over values | -| `technique:error-handling` | Handling errors | -| `technique:exceptions` | Working with exceptions | -| `technique:higher-order-functions` | Using higher-order functions | -| `technique:immutability` | Using immutability | -| `technique:immutable-collection` | Using immutable collections (special case of `technique:immutability`) | -| `technique:inheritance` | Using inheritance | -| `technique:laziness` | Using laziness, where values are produced only when needed | -| `technique:locks` | Using locks to get exclusive access to resources | -| `technique:looping` | Using loops | -| `technique:math` | Using math | -| `technique:memory-management` | Managing memory | -| `technique:ordering` | Ordering data | -| `technique:parallelism` | Running code in parallel | -| `technique:pointers` | Using pointers | -| `technique:randomness` | Using randomness | -| `technique:recursion` | Using recursion | -| `technique:regular-expression` | Using regular expressions | -| `technique:short-circuiting` | Use short-circuiting to prevent unnecessary evaluation | -| `technique:sorted-collection` | Using sorted collections (special case of `technique:sorting`) | -| `technique:sorting` | Sorting data | -| `technique:tail-call-optimization` | Use tail-call optimization for efficient recursion | -| `technique:type-conversion` | Converting values from one type to another type | -| `technique:unsafe` | Using unsafe code, e.g. pointer arithmetic | +| Tag | Description | +| ---------------------------------- | ----------------------------------------------------------------------------- | +| `technique:bit-manipulation` | manipulates bits, usually via bitwise operators (e.g. AND, XOR or left shift) | +| `technique:bit-shifting` | shifts bits in a number | +| `technique:boolean-logic` | executes boolean logic (AND, OR, NOT) | +| `technique:composition` | uses composition | +| `technique:concurrency` | runs code concurrently | +| `technique:enumeration` | enumerates over values | +| `technique:error-handling` | handles errors | +| `technique:exceptions` | catches or throws exceptions | +| `technique:function-composition` | uses function composition | +| `technique:generator` | uses a generator | +| `technique:generators` | uses generators | +| `technique:higher-order-functions` | uses higher-order functions | +| `technique:immutability` | uses immutability | +| `technique:immutable-collection` | uses a collection whose contents are immutable | +| `technique:inheritance` | uses inheritance | +| `technique:laziness` | produces values lazily, only when needed | +| `technique:locks` | uses locks to get exclusive access to resources | +| `technique:looping` | uses loops | +| `technique:math` | uses math | +| `technique:memory-management` | manages memory | +| `technique:ordering` | orders data | +| `technique:parallelism` | runs code in parallel | +| `technique:parser` | parses data | +| `technique:pointers` | uses pointers | +| `technique:randomness` | uses randomness | +| `technique:recursion` | uses recursion | +| `technique:regular-expression` | uses regular expressions | +| `technique:short-circuiting` | uses short-circuiting to prevent unnecessary evaluation | +| `technique:sorted-collection` | uses a collection whose elements are always sorted | +| `technique:sorting` | sorts data | +| `technique:tail-call-optimization` | uses tail-call optimization for efficient recursion | +| `technique:type-conversion` | converts values from one type to another type | +| `technique:unsafe` | uses unsafe code (for example pointer arithmetic) | ### Constructs -| Tag | Description | -| -------------------------------------------- | --------------------------------------------------------------- | -| `construct:abstract-method` | An abstract method | -| `construct:assignment` | Assign/bind a value to a variable/name | -| `construct:boxing` | A boxed value | -| `construct:catch` | Catch an exception | -| `construct:comment` | A comment | -| `construct:comparison` | Compare two values for their relative order | -| `construct:concatenation` | Concatenate two values | -| `construct:constant` | A constant (immutable) value | -| `construct:constructor` | A constructor | -| `construct:coroutine` | A coroutine | -| `construct:default-interface-implementation` | A default implementation in an interface | -| `construct:default` | A default value (e.g. for a parameter) | -| `construct:destructuring` | Decontruct a value into its parts | -| `construct:discard` | Discard a value | -| `construct:equality` | Compare equality of two values | -| `construct:event` | An event | -| `construct:explicit-conversion` | Exlicitly convert from one type to another type (aka "casting") | -| `construct:explicit-import` | Explicitly import only the specified functionality | -| `construct:extension-method` | An extension method | -| `construct:field` | A field | -| `construct:field-access` | Access a field | -| `construct:function-overloading` | Function overloading | -| `construct:function` | A function | -| `construct:generator` | A function or method that produces values | -| `construct:generic-function` | A function that is parameterized with one or more types | -| `construct:generic-method` | A method that is parameterized with one or more types | -| `construct:generic-type` | A type that is parameterized with one or more types | -| `construct:getter` | A getter | -| `construct:implicit-conversion` | Implicitly convert from one type to another type | -| `construct:import` | Import functionality (e.g. from a namespace/module) | -| `construct:indexing` | Accessing a value by index | -| `construct:indexer` | An indexer | -| `construct:inequality` | Compare inequality of two values | -| `construct:invocation` | An invocation of a method/function | -| `construct:iterator` | A function or method that can be iterated over | -| `construct:lambda` | A lambda (aka an "anonymous function") | -| `construct:logical-and` | A logical AND | -| `construct:logical-not` | A logical NOT | -| `construct:logical-or` | A logical OR | -| `construct:macro` | A macro | -| `construct:method-chaining` | Chains several method calls | -| `construct:method-overloading` | Method overloading | -| `construct:method-override` | An overridden method | -| `construct:method` | A method | -| `construct:module` | A module (grouping of code) | -| `construct:multiple-assignment` | Assign multiple values at once | -| `construct:multiple-dispatch` | Multiple dispatch | -| `construct:named-argument` | An argument passed by name | -| `construct:namespace` | A namespace (grouping of code) | -| `construct:nested-function` | A nested function | -| `construct:nested-type` | A nested type | -| `construct:nesting` | Use nesting | -| `construct:nullability` | Nullability, dealing with `null` values | -| `construct:operator-overloading` | Operator overloading | -| `construct:optional-parameter` | An optional parameter (doesn't have to be passed) | -| `construct:parameter` | A parameter | -| `construct:parenthesized-expression` | A parenthesized expression | -| `construct:pattern-matching` | Pattern matching | -| `construct:property` | A property (getter/setter) | -| `construct:setter` | A setter | -| `construct:static-field` | A static field | -| `construct:static-method` | A static method | -| `construct:string-formatting` | Building a string via a format string | -| `construct:throw` | Throw/raise an exception | -| `construct:try` | Explicitly handle an exception | -| `construct:type-alias` | An alias for a type | -| `construct:type-conversion` | Converts/casts a value to another type | -| `construct:type-extension` | Extend a type with new functionality | -| `construct:type-inference` | Automatically infer the type of a value | -| `construct:type-test` | Test if a value has a specific type | -| `construct:varargs` | Allow passing in zero or more values for a parameter | -| `construct:variable` | A variable | -| `construct:virtual-method` | A virtual method | -| `construct:visibility-modifiers` | Determine the visibility of a method/function | - -#### Control flow - -| Tag | Description | -| -------------------------------- | ------------------------------------------------------ | -| `construct:async-await` | An `async`/`await` statement | -| `construct:break` | Break from a loop | -| `construct:conditional-access` | Conditionally access a method | -| `construct:conditional-operator` | A ternary conditional operator | -| `construct:continue` | Continue to the next iteration of a loop | -| `construct:do-while-loop` | A `do-while` loop | -| `construct:else` | An `else` statement | -| `construct:finally` | Ensure that a certain code block always runs | -| `construct:for-loop` | A `for` loop | -| `construct:foreach` | A `foreach` loop | -| `construct:if` | An `if` statement | -| `construct:loop` | A loop | -| `construct:pipe-backward` | A backward pipe | -| `construct:pipe-forward` | A forward pipe | -| `construct:pipeline` | A pipeline | -| `construct:point-free` | Define functions without the arguments they operate on | -| `construct:return` | Return from a function/method | -| `construct:switch` | A `switch` | -| `construct:while-loop` | A `while` loop | -| `construct:yield` | Yield a value in a loop | - -#### Arithmetic - -| Tag | Description | -| ------------------------------------------ | -------------------------------------------- | -| `construct:add-assignment` | Addition and assignment combined | -| `construct:add` | Addition | -| `construct:bitwise-and-assignment` | Bitwise AND and assignment combined | -| `construct:bitwise-and` | Bitwise AND | -| `construct:bitwise-left-shift-assignment` | Bitwise left shift and assignment combined | -| `construct:bitwise-left-shift` | Bitwise left shift | -| `construct:bitwise-not` | Bitwise NOT | -| `construct:bitwise-or-assignment` | Bitwise OR and assignment combined | -| `construct:bitwise-or` | Bitwise OR | -| `construct:bitwise-right-shift-assignment` | Bitwise right shift and assignment combined | -| `construct:bitwise-right-shift` | Bitwise right shift | -| `construct:bitwise-xor-assignment` | Bitwise XOR and assignment combined | -| `construct:bitwise-xor` | Bitwise XOR | -| `construct:checked-arithmetic` | Checked arithmetic (guard against overflows) | -| `construct:divide-assignment` | Division and assignment combined | -| `construct:divide` | Division | -| `construct:modulo-assignment` | Division remainder and assignment combined | -| `construct:modulo` | Division remainder | -| `construct:multiply-assignment` | Multiplication and assignment combined | -| `construct:multiply` | Multiplication | -| `construct:overflow` | Arithmetic overflow | -| `construct:decrement` | Decrement a value | -| `construct:increment` | Increment a value | -| `construct:postfix-decrement` | Decrement a value using postfix notation | -| `construct:postfix-increment` | Increment a value using postfix notation | -| `construct:prefix-decrement` | Decrement a value using prefix notation | -| `construct:prefix-increment` | Increment a value using prefix notation | -| `construct:subtract-assignment` | Subtraction and assignment combined | -| `construct:subtract` | Subtraction | -| `construct:unary-minus` | Unary minus | -| `construct:unary-plus` | Unary plus | - -#### Value types - -| Tag | Description | -| ------------------------------ | ---------------------------------------------------------- | -| `construct:boolean` | A boolean | -| `construct:char` | A character | -| `construct:date-time` | A combination of date + time | -| `construct:date` | A date (no time) | -| `construct:enum` | An enum (enumeration of values) | -| `construct:null` | Represents the absence of a value (something called `nil`) | -| `construct:number` | A number (signed or unsigned) | -| `construct:complex-number` | A complex number | -| `construct:string` | A string | -| `construct:time` | A time (no date) | -| `construct:pointer` | A pointer | -| `construct:regular-expression` | A regular expression | - -#### Integral types - -| Tag | Description | -| --------------------------- | ----------------------------------------------------- | -| `construct:big-integer` | A big integer | -| `construct:byte` | An unsigned 8-bit integer | -| `construct:int` | A signed 32-bit integer | -| `construct:integral-number` | An integral number (integer) | -| `construct:long` | A signed 64-bit integer | -| `construct:nint` | A signed platform-specific integer (32- or 64-bit) | -| `construct:nuint` | An unsigned platform-specific integer (32- or 64-bit) | -| `construct:sbyte` | A signed 8-bit integer | -| `construct:short` | A signed 16-bit integer | -| `construct:uint` | An unsigned 32-bit integer | -| `construct:ulong` | An unsigned 64-bit integer | -| `construct:ushort` | An unsigned 16-bit integer | - -#### Floating-point types - -| Tag | Description | -| --------------------------------- | ------------------------------- | -| `construct:decimal` | A 128-bit floating-point number | -| `construct:double` | A 64-bit floating-point number | -| `construct:float` | A 32-bit floating-point number | -| `construct:floating-point-number` | A floating-point number | - -#### Composite types - -| Tag | Description | -| ---------------------------------- | ---------------------------------------------------------------- | -| `construct:abstract-class` | An abstract class | -| `construct:class` | A class | -| `construct:exception` | An exception | -| `construct:interface` | An interface | -| `construct:option` | An option type | -| `construct:record` | A record (usually immutable) | -| `construct:result` | A result type | -| `construct:struct` | A struct | -| `construct:sum-type` | A sum type | -| `construct:tuple` | A tuple | -| `construct:union-type` | A union type | -| `construct:user-defined-exception` | A user-defined exception (special case of `construct:exception`) | - -#### Collection types - -| Tag | Description | -| ----------------------------------- | --------------------------------------------------- | -| `construct:array` | An array | -| `construct:bit-array` | A bit array (similar to an array but works on bits) | -| `construct:dictionary` | A dictionary (aka map) | -| `construct:jagged-array` | A jagged array (an array of arrays) | -| `construct:linked-list` | A linked list (special case of `construct:list`) | -| `construct:list` | A list | -| `construct:multi-dimensional-array` | An array with multiple dimensions | -| `construct:queue` | A queue | -| `construct:range` | A range | -| `construct:set` | A set | -| `construct:slice` | A subset of a collection | -| `construct:stack` | A stack | - -#### Notation - -| Tag | Description | -| -------------------------------------- | ----------------------------------------------- | -| `construct:binary-number` | A number in binary notation | -| `construct:hexadecimal-number` | A number in hexadecimal notation | -| `construct:multiline-string` | A multiline string | -| `construct:octal-number` | A number in octal notation | -| `construct:scientific-notation-number` | A number in scientific notation | -| `construct:string-interpolation` | An interpolated string | -| `construct:underscored-number` | A number using underscores as a digit separator | -| `construct:verbatim-string` | A verbatim string (no escape sequences) | +| Tag | Description | +| -------------------------------------------- | -------------------------------------------------------------------------- | +| `construct:abstract-class` | defines an abstract class | +| `construct:abstract-method` | defines an abstract method | +| `construct:add-assignment` | combines addition with assignment | +| `construct:add` | uses addition | +| `construct:array` | uses or declares an array | +| `construct:assignment` | assigns or binds a value to a variable/name | +| `construct:async-await` | uses `async`/`await` | +| `construct:attribute` | annotates a member with metadata | +| `construct:auto-implemented-property` | defines an auto-implemented property | +| `construct:big-integer` | uses a big integer | +| `construct:binary-number` | defines a number in binary notation | +| `construct:bit-array` | uses or declares a bit array | +| `construct:bitwise-and-assignment` | combines a bitwise AND with assignment | +| `construct:bitwise-and` | executes a bitwise AND | +| `construct:bitwise-left-shift-assignment` | combines a bitwise left shift with assignment | +| `construct:bitwise-left-shift` | executes a bitwise left shift | +| `construct:bitwise-not` | executes a bitwise NOT | +| `construct:bitwise-or-assignment` | combines a bitwise OR with assignment | +| `construct:bitwise-or` | executes a bitwise OR | +| `construct:bitwise-right-shift-assignment` | combines a bitwise right shift with assignment | +| `construct:bitwise-right-shift` | executes a bitwise right shift | +| `construct:bitwise-xor-assignment` | combines a bitwise XOR with assignment | +| `construct:bitwise-xor` | executes a bitwise XOR | +| `construct:block` | defines a block containing a list of statements | +| `construct:boolean` | defines or uses a boolean value | +| `construct:boxing` | uses boxing | +| `construct:break` | breaks from a loop | +| `construct:byte` | uses an unsigned 8-bit integer | +| `construct:catch` | catches an exception | +| `construct:char` | uses a character | +| `construct:checked-arithmetic` | uses checked arithmetic (which guards against overflows) | +| `construct:class` | defines a class | +| `construct:collection-initializer` | sets a collection's values via an initializer | +| `construct:command` | defines a command | +| `construct:comment` | defines a comment | +| `construct:comparison-chaining` | chains comparison calls | +| `construct:comparison` | compares two values for their relative order | +| `construct:complex-number` | uses a complex number | +| `construct:computed-string` | defines a computed string | +| `construct:concatenation` | concatenates values | +| `construct:conditional-access` | accesses a method conditionally | +| `construct:conditional-operator` | uses a ternary conditional operator | +| `construct:conditional` | executes code conditionally | +| `construct:constant` | defines a constant (immutable) value | +| `construct:constructor` | defines a constructor | +| `construct:continue` | continues to the next iteration of a loop | +| `construct:conversion-operator` | defines a conversion operator | +| `construct:copy` | copies a value | +| `construct:coroutine` | defines or uses a coroutine | +| `construct:custom-attribute` | defines a custom attribute | +| `construct:date-time` | uses a value that represents a combination of date and time | +| `construct:date` | uses va value that represents a date (no time) | +| `construct:decimal` | uses a 128-bit floating-point number | +| `construct:decrement` | decrements a value | +| `construct:default-interface-implementation` | defines default implementation in an interface | +| `construct:default` | defines a default value (e.g. for a parameter) | +| `construct:destructuring` | decontructs a value into its parts | +| `construct:dictionary-comprehension` | creates a dictionary via a comprehension | +| `construct:dictionary` | uses/defines a dictionary/map | +| `construct:discard` | uses the special discard value | +| `construct:divide-assignment` | combines division with assignment | +| `construct:divide` | uses division | +| `construct:do-while-loop` | uses a `do-while` loop | +| `construct:double` | uses a 64-bit floating-point number | +| `construct:else` | defines an else branch | +| `construct:enum` | defines or uses an enum (enumeration of values) | +| `construct:equality` | compares the equality of two values | +| `construct:error` | defines or uses an error | +| `construct:event` | defines or uses events | +| `construct:exception` | defines or uses exceptions | +| `construct:explicit-conversion` | converts from one type to another type explicitly | +| `construct:explicit-import` | imports only select functionality | +| `construct:exponentiation` | calculates the exponentiation of a value | +| `construct:expression-bodied-member` | defines an expression-bodied member | +| `construct:extension-method` | defines an extension method | +| `construct:field-access` | accesses a field | +| `construct:field` | defines a field | +| `construct:finally` | uses finally to ensure that a specific code block always runs | +| `construct:flags-enum` | defines or uses a flag enum | +| `construct:float` | uses a 32-bit floating-point number | +| `construct:floating-point-number` | uses a floating-point number | +| `construct:for-loop` | uses a `for` loop | +| `construct:foreach` | uses a `foreach` loop | +| `construct:function-overloading` | uses function overloading | +| `construct:function` | defines a function | +| `construct:generator` | defines a generator function or method | +| `construct:generic-function` | defines a function that is parameterized with one or more types | +| `construct:generic-method` | defines a method that is parameterized with one or more types | +| `construct:generic-type` | defines type that is parameterized with one or more types | +| `construct:getter` | defines a getter | +| `construct:global-function` | defines a function that is available globally | +| `construct:global-variable` | defines a variable that is available globally | +| `construct:header` | defines a header file | +| `construct:hexadecimal-number` | defines a number in hexadecimal notation | +| `construct:if` | uses an `if` statement | +| `construct:implicit-conversion` | converts from one type to another type implicitly | +| `construct:implicit-loop` | loops over a collection of values implicitly | +| `construct:implicit-return` | returns a value implicitly | +| `construct:import` | imports functionality implemented elsewhere (e.g. from a namespace/module) | +| `construct:increment` | increments a value | +| `construct:index-operator` | defines an operator for indexing an object | +| `construct:indexer` | defines or uses an indexer | +| `construct:indexing` | accesses a value by index | +| `construct:inequality` | compares the inequality of two values | +| `construct:infix-operator` | defines an infix operator | +| `construct:initialization` | initializes an object after creation | +| `construct:initializer-list` | initializes the values of an object's fields | +| `construct:initializer` | initializes an object | +| `construct:instance` | creates or uses an instance of a type | +| `construct:instantiation` | creates an instance of a type | +| `construct:int` | uses a signed 32-bit integer | +| `construct:integral-number` | uses an integral number (integer) | +| `construct:interface` | defines an interface | +| `construct:invocation` | invokes a method/function | +| `construct:iterator` | defines a function or method that can be iterated over | +| `construct:jagged-array` | uses a jagged array (an array of arrays) | +| `construct:lambda` | defines a lambda (aka an "anonymous function") | +| `construct:linked-list` | uses a linked list | +| `construct:linq` | uses LINQ | +| `construct:list-comprehension` | builds a list via a comprehension | +| `construct:list` | uses a list | +| `construct:local-function` | defines a local function | +| `construct:local-variable` | defines a local variable | +| `construct:lock` | uses a lock | +| `construct:logical-and` | executes a logical AND | +| `construct:logical-not` | executes a logical NOT | +| `construct:logical-or` | executes a logical OR | +| `construct:long` | uses a signed 64-bit integer | +| `construct:loop` | defines a loop | +| `construct:macro` | defines a macro | +| `construct:membership-test` | tests a value for membership in another value | +| `construct:metatable` | defines a metatable | +| `construct:method-chaining` | chains several method calls | +| `construct:method-overloading` | uses method overloading | +| `construct:method-override` | defines an overridden method | +| `construct:method` | defines a method | +| `construct:module` | defines a module (grouping of code) | +| `construct:modulo-assignment` | combines division remainder with assignment | +| `construct:modulo` | uses division remainder | +| `construct:multi-dimensional-array` | uses an array with multiple dimensions | +| `construct:multiline-string` | defines a multiline string | +| `construct:multiple-assignment` | assigns multiple values at once | +| `construct:multiple-dispatch` | uses multiple dispatch | +| `construct:multiply-assignment` | combines multiplication with assignment | +| `construct:multiply` | uses multiplication | +| `construct:named-argument` | passes an argument by name | +| `construct:namespace` | defines a namespace (grouping of code) | +| `construct:nested-function` | defines a nested function | +| `construct:nested-type` | defines a nested type | +| `construct:nesting` | uses nesting | +| `construct:nint` | uses a signed platform-specific integer (32- or 64-bit) | +| `construct:nuint` | uses an unsigned platform-specific integer (32- or 64-bit) | +| `construct:null` | uses null/nil to represents the absence of a value | +| `construct:nullability` | deals with null/nil values | +| `construct:number` | uses a number (signed or unsigned) | +| `construct:object-initializer` | sets an object's values via an initializer | +| `construct:octal-number` | defines a number in octal notation | +| `construct:operator-overloading` | uses operator overloading | +| `construct:option` | uses an option type | +| `construct:optional-parameter` | defines an optional parameter | +| `construct:overflow` | uses arithmetic overflow | +| `construct:parameter` | defines a parameter | +| `construct:parenthesized-expression` | encloses an expression in parentheses | +| `construct:pattern-matching` | uses pattern matching | +| `construct:pattern` | defines a pattern uses in pattern matching | +| `construct:pipe-backward` | uses a backward pipe | +| `construct:pipe-forward` | uses a forward pipe | +| `construct:pipeline` | defines a pipeline | +| `construct:point-free` | defines functions without the arguments they operate on | +| `construct:pointer` | uses a pointer | +| `construct:postfix-decrement` | decrements a value using postfix notation | +| `construct:postfix-increment` | increments a value using postfix notation | +| `construct:prefix-decrement` | decrements a value using prefix notation | +| `construct:prefix-increment` | increments a value using prefix notation | +| `construct:print` | prints a value to the console | +| `construct:property` | defines a property (getter/setter) | +| `construct:query-expression` | queries data via an expression | +| `construct:queue` | uses a queue | +| `construct:range` | defines a range | +| `construct:read-only` | defines a read-only value | +| `construct:record` | defines a record | +| `construct:regular-expression` | defines or uses a regular expression | +| `construct:result` | uses a result type | +| `construct:return-type` | defines the return type of a function or method | +| `construct:return` | returns from a function/method | +| `construct:sbyte` | uses a signed 8-bit integer | +| `construct:scientific-notation-number` | defines a number in scientific notation | +| `construct:set-comprehension` | creates a set via a comprehension | +| `construct:set` | uses a set | +| `construct:setter` | defines a setter | +| `construct:short` | uses a signed 16-bit integer | +| `construct:slice` | uses a slice | +| `construct:stack` | uses a stack | +| `construct:static-field` | defines a static field | +| `construct:static-method` | defines a static method | +| `construct:string-formatting` | builds a string via a format string | +| `construct:string-interpolation` | defines an interpolated string | +| `construct:string` | defines a string | +| `construct:struct` | defines a struct | +| `construct:subtract-assignment` | combines subtraction with assignment | +| `construct:subtract` | uses subtraction | +| `construct:sum-type` | defines a sum type | +| `construct:switch` | uses a `switch` | +| `construct:table` | defines a table | +| `construct:template-alias` | defines an alias for a template | +| `construct:template-parameter` | defines a template parameter | +| `construct:template` | defines a template | +| `construct:throw` | throws an exception | +| `construct:time` | uses an object that represents the time | +| `construct:try` | handles an exception explicitly | +| `construct:tuple` | uses a tuple | +| `construct:type-alias` | defines an alias for a type | +| `construct:type-conversion` | converts (casts) a value to another type | +| `construct:type-extension` | extends a type with new functionality | +| `construct:type-inference` | infers the type of a value/function automatically | +| `construct:type-test` | test if a value has a specific type | +| `construct:uint` | uses an unsigned 32-bit integer | +| `construct:ulong` | uses an unsigned 64-bit integer | +| `construct:unary-minus` | uses a unary minus | +| `construct:unary-plus` | uses a unary plus | +| `construct:underscored-number` | uses a number with underscores as its digit separators | +| `construct:union-type` | defines a union type | +| `construct:user-defined-exception` | defines a custom, user-defined exception | +| `construct:ushort` | uses an unsigned 16-bit integer | +| `construct:using-directive` | uses code from another file | +| `construct:using-statement` | assigns and disposes a value via the using statement | +| `construct:varargs` | defines a parameter that supports passing in zero or more values | +| `construct:variable` | declares variable | +| `construct:vector` | uses a vector | +| `construct:verbatim-string` | uses a verbatim string (no escape sequences) | +| `construct:virtual-method` | defines a virtual method | +| `construct:visibility-modifiers` | specifies the visibility of a construct (e.g. a method or class) | +| `construct:while-loop` | uses a `while` loop | +| `construct:yield` | yields a value in a loop lazily | ### Uses From d7a7a460b7d042679109fe1e56035588c40fc044 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 1 Dec 2023 10:21:04 +0100 Subject: [PATCH 050/129] Fix duplicate value --- building/tooling/analyzers/tags.md | 1 - 1 file changed, 1 deletion(-) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 09f3bcc6..75757ec2 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -62,7 +62,6 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | `technique:exceptions` | catches or throws exceptions | | `technique:function-composition` | uses function composition | | `technique:generator` | uses a generator | -| `technique:generators` | uses generators | | `technique:higher-order-functions` | uses higher-order functions | | `technique:immutability` | uses immutability | | `technique:immutable-collection` | uses a collection whose contents are immutable | From e988e9f056818d678ffc72f919f8c53539a6a9ae Mon Sep 17 00:00:00 2001 From: Aron Demeter <66035744+dem4ron@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:26:03 +0100 Subject: [PATCH 051/129] Fix wrong formatting of mentoring/faqs links (#479) --- mentoring/faqs.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mentoring/faqs.md b/mentoring/faqs.md index ac69179f..29f50362 100644 --- a/mentoring/faqs.md +++ b/mentoring/faqs.md @@ -2,7 +2,7 @@ [What qualifies someone to be a mentor?](#h-what-qualifies-someone-to-be-a-mentor) -[Can I mentor a language while I'm still learning it?](#h-can-i-mentor-a-language-while-im-still-learning-it) +[Can I mentor a language while I'm still learning it?](#h-can-i-mentor-a-language-while-i-m-still-learning-it) [Can I mentor more than one language?](#h-can-i-mentor-more-than-one-language) @@ -12,17 +12,17 @@ [What if I have nothing to suggest about a solution?](#h-what-if-i-have-nothing-to-suggest-about-a-solution) -[Should I mentor an exercise I've never solved?](#h-should-i-mentor-an-exercise-ive-never-solved) +[Should I mentor an exercise I've never solved?](#h-should-i-mentor-an-exercise-i-ve-never-solved) -[Should I mentor an exercise I've solved, but solved in a different language?](#h-should-i-mentor-an-exercise-ive-solved-but-solved-in-a-different-language) +[Should I mentor an exercise I've solved, but solved in a different language?](#h-should-i-mentor-an-exercise-i-ve-solved-but-solved-in-a-different-language) -[Do I have to mentor a solution once I've seen it?](#h-do-i-have-to-mentor-a-solution-once-ive-seen-it) +[Do I have to mentor a solution once I've seen it?](#h-do-i-have-to-mentor-a-solution-once-i-ve-seen-it) -[What if the student does not understand after I've explained something several times?](#h-what-if-the-student-does-not-understand-after-ive-explained-something-several-times) +[What if the student does not understand after I've explained something several times?](#h-what-if-the-student-does-not-understand-after-i-ve-explained-something-several-times) [How do I respond if the student gets defensive about my suggestion(s)?](#h-how-do-i-respond-if-the-student-gets-defensive-about-my-suggestions) -[Should the student have the last word, even if I think they're wrong?](#h-should-the-student-have-the-last-word-even-if-i-think-theyre-wrong) +[Should the student have the last word, even if I think they're wrong?](#h-should-the-student-have-the-last-word-even-if-i-think-they-re-wrong) [How do I best phrase a suggestion?](#h-how-do-i-best-phrase-a-suggestion) From b704daec0bb883ad223c4fa85ff0ae9ee7c0445a Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 9 Jan 2024 14:23:39 +0100 Subject: [PATCH 052/129] Add approach and article widget documentation (#481) --- building/markdown/widgets.md | 45 ++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/building/markdown/widgets.md b/building/markdown/widgets.md index 40e79667..58e5ed5d 100644 --- a/building/markdown/widgets.md +++ b/building/markdown/widgets.md @@ -18,19 +18,34 @@ You may like to render these widgets yourself in Markdown documents, for example Widgets can be used wherever you can use Markdown, such as documentation, approaches and student/mentor discussions. -They are rendered using the following format +They are rendered using the following format: ```md -[:/]() +[:]() ``` +The `` and `` parts are variable and depend on the actual widget being used. + The link reference, i.e. the contents of `()`, is ignored by the website. You can point them at whatever you want for maintenance purposes. +## Exercise widget + +Link format: `[exercise:/]()` + +### Example + +```markdown +Great job on solving this exercise! Some other exercises that you might also like to try: + +- [exercise:fsharp/anagram]() +- [exercise:fsharp/isogram]() +``` + ## Concept widget Link format: `[concept:/]()` -### Example: list +### Example ```markdown There are three primary conditional statements that are used in Julia: @@ -40,15 +55,27 @@ There are three primary conditional statements that are used in Julia: - [concept:julia/short-circuiting]() ``` -## Exercise widget +## Approach widget -Link format: `[exercise:/]()` +Link format: `[approach://]()` -### Example: list +### Example ```markdown -Great job on solving this exercise! Some other exercises that you might also like to try: +Here are some approaches you might want to checkout! -- [exercise:fsharp/anagram]() -- [exercise:fsharp/isogram]() +- [approach:csharp/two-fer/method-overloading]() +- [approach:csharp/two-fer/optional-parameter]() +``` + +## Article widget + +Link format: `[article://]()` + +### Example + +```markdown +We have an article you might be interested in: + +- [article:csharp/reverse-string/performance]() ``` From 8c92c5caf33d957b3eab1b5a03d7844f6ae5df5b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 11 Jan 2024 14:37:41 +0100 Subject: [PATCH 053/129] Add docs for themed images (#482) --- building/markdown/markdown.md | 40 ++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/building/markdown/markdown.md b/building/markdown/markdown.md index 91be75c3..d58a11f4 100644 --- a/building/markdown/markdown.md +++ b/building/markdown/markdown.md @@ -149,16 +149,32 @@ You can test that your markdown comment gets removed by checking how your commen - Inline HTML is allowed, but should be used sparingly - Always use native markdown alternatives if available (e.g. use `# ...` rather than `

...

`) +## Images + +As the website supports light and dark themes, community-submitted images must render well in both themes. +The solution to this is to suffix your image name (before the file extension): + +1. Have the image name end in `-invertible` (e.g. `graph-invertible.svg`). The image will automatically be inverted when shown in the dark theme (via `filter: invert(100%)`). +1. Have the image name end in `-light` (e.g. `graph-light.png`) and also create dark-theme compatible version that ends in `-dark` (which would be `graph-dark.png`). We'll automatically render the correct image depending on the user's theme. + +Images with neither suffix will be used without modifications across both themes. +This means that if you want to create grayscale images, you can generally make them look good on light theme, and then set them to be invertible. +But if you’re using an image with lots of colours, you might like to make two variants. + +The full image name (including `-invertible`) should be added to the markdown where the image is rendered. +For light/dark ones, **ONLY** include the `-light` variant. +This logic is honoured across all markdown docs. + ## Linters There are various rules you can use to configure linters to meet this spec: -- [MD001][MD001]: Enable -- [MD002][MD002]: Enable -- [MD003][MD003]: Use `atx` style -- [MD004][MD004]: Use `dash` style -- [MD013][MD013]: Disable -- [MD033][MD033]: Disable +- [MD001][md001]: Enable +- [MD002][md002]: Enable +- [MD003][md003]: Use `atx` style +- [MD004][md004]: Use `dash` style +- [MD013][md013]: Disable +- [MD033][md033]: Disable ## Auto formatting @@ -173,12 +189,12 @@ All the above can greatly help reduce churn in reviews, which is frustrating for --- -[MD001]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md002---header-levels-should-only-increment-by-one-level-at-a-time -[MD002]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md002---first-header-should-be-a-top-level-header -[MD003]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md003---header-style -[MD004]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md004---unordered-list-style -[MD013]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md013---line-length -[MD033]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md033---inline-html +[md001]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md002---header-levels-should-only-increment-by-one-level-at-a-time +[md002]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md002---first-header-should-be-a-top-level-header +[md003]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md003---header-style +[md004]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md004---unordered-list-style +[md013]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md013---line-length +[md033]: https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md033---inline-html [asciidoctor]: https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line [babelmark3]: https://babelmark.github.io [google-link]: https://google.com From 9618086df2afdc3fb49ffd85789c296a3f690c39 Mon Sep 17 00:00:00 2001 From: Aron Demeter <66035744+dem4ron@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:01:00 +0100 Subject: [PATCH 054/129] Adjust mentoring/faqs anchor (#483) --- mentoring/faqs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mentoring/faqs.md b/mentoring/faqs.md index 29f50362..acdcc0af 100644 --- a/mentoring/faqs.md +++ b/mentoring/faqs.md @@ -20,7 +20,7 @@ [What if the student does not understand after I've explained something several times?](#h-what-if-the-student-does-not-understand-after-i-ve-explained-something-several-times) -[How do I respond if the student gets defensive about my suggestion(s)?](#h-how-do-i-respond-if-the-student-gets-defensive-about-my-suggestions) +[How do I respond if the student gets defensive about my suggestion(s)?](#h-how-do-i-respond-if-the-student-gets-defensive-about-my-suggestion-s) [Should the student have the last word, even if I think they're wrong?](#h-should-the-student-have-the-last-word-even-if-i-think-they-re-wrong) From f30f185fbababe483cf81d73831146ecfee3e3cc Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Wed, 24 Jan 2024 19:17:42 +0000 Subject: [PATCH 055/129] Add practice exercise video (#484) --- building/tracks/practice-exercises.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 4cc8f42a..79b1771e 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -1,5 +1,7 @@ # Practice Exercises +[video:vimeo/906101866]() + [Practice Exercises](/docs/building/product/practice-exercises) are exercises designed to allow students to solve an arbitrary problem, with the aim of them making use of the concepts they have learned so far. ## Metadata From eb95345b2350b9a285e9e30895b12502edba4057 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Wed, 24 Jan 2024 19:59:17 +0000 Subject: [PATCH 056/129] Update practice-exercises.md --- building/tracks/practice-exercises.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 79b1771e..16b84838 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -1,6 +1,6 @@ # Practice Exercises -[video:vimeo/906101866]() +[video:vimeo/906101866?h=2954ad331e]() [Practice Exercises](/docs/building/product/practice-exercises) are exercises designed to allow students to solve an arbitrary problem, with the aim of them making use of the concepts they have learned so far. From 2b372fe38f00cac8dca270a6d63059431650d0ad Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Wed, 24 Jan 2024 20:42:20 +0000 Subject: [PATCH 057/129] Update practice-exercises.md --- building/tracks/practice-exercises.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 16b84838..51fb7bfa 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -1,9 +1,11 @@ # Practice Exercises -[video:vimeo/906101866?h=2954ad331e]() - [Practice Exercises](/docs/building/product/practice-exercises) are exercises designed to allow students to solve an arbitrary problem, with the aim of them making use of the concepts they have learned so far. +Interested in adding your first Practice Exercise to a track? Watch our walkthrough video 👇 + +[video:vimeo/906101866?h=2954ad331e]() + ## Metadata Practice Exercise metadata is defined in the `exercises.practice` key in the [config.json file](/docs/building/tracks/config-json). The metadata defines the exercise's UUID, slug and more. From 4d7bcf35a0e7c0d746ae5088a38e575bc2c3c79f Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 25 Jan 2024 13:40:34 +0100 Subject: [PATCH 058/129] Document configlet create (#485) * configlet: document `create` command * Add notes about using ' --- building/configlet/README.md | 6 ++ building/configlet/create.md | 95 +++++++++++++++++++++++++++ building/tracks/concept-exercises.md | 11 ++++ building/tracks/practice-exercises.md | 11 ++++ 4 files changed, 123 insertions(+) create mode 100644 building/configlet/create.md diff --git a/building/configlet/README.md b/building/configlet/README.md index 6babe588..03ba944c 100644 --- a/building/configlet/README.md +++ b/building/configlet/README.md @@ -36,6 +36,12 @@ Tests in this file are identified by their UUID and each test has a boolean valu You can find the details about how to sync the different parts of an exercise [here](/docs/building/configlet/sync). +## Create files + +Configlet can be used to quickly scaffold files for a new approach, article or exercise. + +You can learn more about how to create these files [here](/docs/building/configlet/create). + ## Generating UUIDs Exercises, tracks and concepts are identified by a UUID. diff --git a/building/configlet/create.md b/building/configlet/create.md new file mode 100644 index 00000000..3ab9ff5b --- /dev/null +++ b/building/configlet/create.md @@ -0,0 +1,95 @@ +# Configlet creating files + +When adding a new approach, article or exercise, you'll have to create files with very specific names. +They also require configuration files to be added or updated. +With the `create` command, [configlet](/docs/building/configlet) can do all this for you. + +## Usage + +The `create` command can be used to create the files required to add a new approach, article or exercise, as well as modify any configuration files. + +```shell +configlet [global-options] create [command-options] + +Options for create: + --approach The slug of the approach + --article The slug of the article + --practice-exercise The slug of the practice exercise + --concept-exercise The slug of the concept exercise + -e, --exercise Only operate on this exercise + -o, --offline Do not update the cached 'problem-specifications' data +``` + +## Create Practice Exercise + +To create a practice exercise, one has to specify its slug: + +```shell +configlet create --practice-exercise collatz-conjecture +``` + +This will create the practice exercise's required files, as specified in the [Practice Exercises docs](/docs/building/tracks/practice-exercises). +If the practice exercise is defined in the [Problem Specifications repo](https://github.com/exercism/problem-specifications/), configlet will sync the docs and metadata from there. + +Of course, this is just the first step towards creating an exercise. +You'll then have to: + +- Add tests to the tests file +- Add an example implementation +- Define the stub file's contents +- Within the exercise's `.meta/config.json` file: + - Add the GitHub username of the exercise's authors to the `authors` key +- Within the track's `config.json` file: + - Check/update the exercise's difficulty + - Add concepts to the `practices` key (only required when the track has concept exercises) + - Add concepts to the `prerequisites` key (only required when the track has concept exercises) + +```exercism/note +Some tracks have implemented an exercise/test _generator_, which is a tool that can generate the test file's contents based on the exercise's `canonical-data.json` found in the [Problem Specifications repo](https://github.com/exercism/problem-specifications/). +Make sure to read the track's documentation to see if there is a generator that you can use. +``` + +## Create Concept Exercise + +To create a concept exercise, one has to specify its slug: + +```shell +configlet create --concept-exercise bird-watcher +``` + +This will create the concept exercise's required files, as specified in the [Concept Exercises docs](/docs/building/tracks/concept-exercises). + +Of course, this is just the first step towards creating an exercise. +You'll then have to: + +- Add tests to the tests file +- Add an exemplar implementation +- Define the stub file's contents +- Write the introduction in `.docs/introduction.md` +- Write the instructions in `.docs/instructions.md` +- Within the exercise's `.meta/config.json` file: + - Add the GitHub username of the exercise's authors to the `authors` key +- Within the track's `config.json` file: + - Check/update the exercise's difficulty + - Add concepts to the `concepts` key + - Add concepts to the `prerequisites` key + +## Create Approach + +To create an approach's files, one has to specify the slug of the approach and its exercise: + +```shell +configlet create --approach recursion --exercise collatz-conjecture +``` + +This will create the approach's required files, as specified in the [Approaches docs](/docs/building/tracks/approaches). + +## Create Article + +To create an article's files, one has to specify the slug of the article and its exercise: + +```shell +configlet create --article performance --exercise collatz-conjecture +``` + +This will create the article's required files, as specified in the [Articles docs](/docs/building/tracks/articles). diff --git a/building/tracks/concept-exercises.md b/building/tracks/concept-exercises.md index 337b2863..d551807a 100644 --- a/building/tracks/concept-exercises.md +++ b/building/tracks/concept-exercises.md @@ -4,6 +4,17 @@ The concepts taught by the concept exercises form a _syllabus_. For more information on how to design a syllabus, check the [syllabus documentation](/docs/building/tracks/syllabus). +````exercism/note +You can quickly scaffold a new Concept Exercise by running the following commands from the track's root directory: + +```shell +bin/fetch-configlet +bin/configlet create --concept-exercise +``` + +For more information, check the [`configlet create` docs](/docs/building/configlet/create) +```` + ## Metadata Concept Exercise metadata is defined in the `exercises.concept` key in the [config.json file](/docs/building/tracks/config-json#concept-exercises). The metadata defines the exercise's UUID, slug and more. diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index 51fb7bfa..e6eccb0d 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -6,6 +6,17 @@ Interested in adding your first Practice Exercise to a track? Watch our walkthro [video:vimeo/906101866?h=2954ad331e]() +````exercism/note +You can quickly scaffold a new Practice Exercise by running the following commands from the track's root directory: + +```shell +bin/fetch-configlet +bin/configlet create --practice-exercise +``` + +For more information, check the [`configlet create` docs](/docs/building/configlet/create) +```` + ## Metadata Practice Exercise metadata is defined in the `exercises.practice` key in the [config.json file](/docs/building/tracks/config-json). The metadata defines the exercise's UUID, slug and more. From 733e008e3d6a3f5e1862fde9afc02a13c9d49fb7 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 25 Jan 2024 13:48:40 +0100 Subject: [PATCH 059/129] add configlet create (#486) * configlet: document `create` command * Add notes about using ' * Add config entry --- building/config.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/building/config.json b/building/config.json index 1b7472d7..07fc7eb6 100644 --- a/building/config.json +++ b/building/config.json @@ -31,6 +31,12 @@ "path": "building/configlet/lint.md", "title": "configlet lint" }, + { + "uuid": "f7e00f6d-2a68-48d4-be84-d86fa43d677d", + "slug": "configlet/create", + "path": "building/configlet/create.md", + "title": "configlet create" + }, { "uuid": "d8d9ff53-fe8f-4333-80c5-87517433cf7d", "slug": "configlet/generating-documents", From cb60600ce4ee342cb16f5562bc73839962ba8880 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 29 Jan 2024 15:24:17 +0000 Subject: [PATCH 060/129] Add programming docs (#487) --- programming/README.md | 3 +++ programming/config.json | 23 +++++++++++++++++++++++ programming/operators/README.md | 3 +++ programming/operators/remainder.md | 30 ++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 programming/README.md create mode 100644 programming/config.json create mode 100644 programming/operators/README.md create mode 100644 programming/operators/remainder.md diff --git a/programming/README.md b/programming/README.md new file mode 100644 index 00000000..bc618b8f --- /dev/null +++ b/programming/README.md @@ -0,0 +1,3 @@ +# Programming + +Docs on programming topics diff --git a/programming/config.json b/programming/config.json new file mode 100644 index 00000000..665c43e4 --- /dev/null +++ b/programming/config.json @@ -0,0 +1,23 @@ +[ + { + "uuid": "1a737701-e2f7-4021-9057-a0faf4738740", + "slug": "APEX", + "path": "programming/README.md", + "title": "Programming docs", + "blurb": "Docs on programming topics" + }, + { + "uuid": "54390764-ef34-4385-b714-ab2851bdd814", + "slug": "reaminder", + "path": "programming/operators/README.md", + "title": "Operators", + "blurb": "Docs on operators" + }, + { + "uuid": "f1848a5a-593d-411f-9d44-4d4849690e1e", + "slug": "remainder", + "path": "programming/operators/remainder.md", + "title": "Remainder", + "blurb": "The remainder operators" + }, +] diff --git a/programming/operators/README.md b/programming/operators/README.md new file mode 100644 index 00000000..66a6f3cf --- /dev/null +++ b/programming/operators/README.md @@ -0,0 +1,3 @@ +# Operators + +Docs on operators diff --git a/programming/operators/remainder.md b/programming/operators/remainder.md new file mode 100644 index 00000000..fa5aa6de --- /dev/null +++ b/programming/operators/remainder.md @@ -0,0 +1,30 @@ +# Remainder + +In mathematics, the **remainder** is the amount "left over" after performing some computation. +For example, the remainder of `5 / 3` is `2`. + +Many programming languages use the percentage sign (`%`) as an operator to calculate the remainder. +For example, this is valid code in Javascript and Python: +```javascript +5 % 3 == 2 +``` + +Remainders can often be calculated on both integers and floating point numbers. +For example, +```javascript +5.3 % 3 == 2.3 +``` + +When working with negative numbers, the result always has the same sign as the dividend (the number on the left hand side that is being divided). +For example: +```javascript +-5 % 3 == -2 +5 % -3 == 2 +-5 % -3 == -2 +``` + +Some languages use the `%` operator for the calculating the modulus, not the remainder. +This treats negative numbers differently. +You can learn more about this [on Wikipedia](https://en.wikipedia.org/wiki/Modulo). + + From c47bad9f6f0b1bf27065d810a72846cb8df2af12 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 29 Jan 2024 15:25:25 +0000 Subject: [PATCH 061/129] Fix config.json --- programming/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programming/config.json b/programming/config.json index 665c43e4..285216b3 100644 --- a/programming/config.json +++ b/programming/config.json @@ -19,5 +19,5 @@ "path": "programming/operators/remainder.md", "title": "Remainder", "blurb": "The remainder operators" - }, + } ] From 8394fbbd067a51e0593823dea85d4edb5adb85a6 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 29 Jan 2024 15:26:54 +0000 Subject: [PATCH 062/129] Fix paths --- programming/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programming/config.json b/programming/config.json index 285216b3..3995ff0f 100644 --- a/programming/config.json +++ b/programming/config.json @@ -8,14 +8,14 @@ }, { "uuid": "54390764-ef34-4385-b714-ab2851bdd814", - "slug": "reaminder", + "slug": "operators", "path": "programming/operators/README.md", "title": "Operators", "blurb": "Docs on operators" }, { "uuid": "f1848a5a-593d-411f-9d44-4d4849690e1e", - "slug": "remainder", + "slug": "operators/remainder", "path": "programming/operators/remainder.md", "title": "Remainder", "blurb": "The remainder operators" From 3a271137729a2595011eb6fa3269b63919ec6086 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 29 Jan 2024 16:03:24 +0000 Subject: [PATCH 063/129] Update remainder.md --- programming/operators/remainder.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/programming/operators/remainder.md b/programming/operators/remainder.md index fa5aa6de..e851b999 100644 --- a/programming/operators/remainder.md +++ b/programming/operators/remainder.md @@ -4,7 +4,7 @@ In mathematics, the **remainder** is the amount "left over" after performing som For example, the remainder of `5 / 3` is `2`. Many programming languages use the percentage sign (`%`) as an operator to calculate the remainder. -For example, this is valid code in Javascript and Python: +For example: ```javascript 5 % 3 == 2 ``` @@ -23,8 +23,6 @@ For example: -5 % -3 == -2 ``` -Some languages use the `%` operator for the calculating the modulus, not the remainder. +Some languages (such as Python) use the `%` operator for the calculating the modulus, not the remainder. This treats negative numbers differently. You can learn more about this [on Wikipedia](https://en.wikipedia.org/wiki/Modulo). - - From d3ff6a8da015546dbe2e8120d570bae7103a79f1 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 30 Jan 2024 13:48:19 +0100 Subject: [PATCH 064/129] configlet create usage (#488) * Document configlet create in add-initial-exercises * Add more documentation for new tracks --- building/tracks/new/add-first-exercise.md | 109 +++++++++++++------ building/tracks/new/add-initial-exercises.md | 37 +++++-- 2 files changed, 106 insertions(+), 40 deletions(-) diff --git a/building/tracks/new/add-first-exercise.md b/building/tracks/new/add-first-exercise.md index 5ad950af..2fbd127e 100644 --- a/building/tracks/new/add-first-exercise.md +++ b/building/tracks/new/add-first-exercise.md @@ -23,52 +23,95 @@ The "Hello, World!" exercise has some special rules applied to it: - It has no `prerequisites` - It has no `practices` -### Updating the config +### Determine file paths -Start by adding an entry to the `exercises.practice` array in the top-level `config.json` file. +The "Hello, World!" exercise (and indeed, _all_ exercises on Exercism) requires a specific set of files: -``` -{ - "exercises": { - "practice": [ - { - "uuid": "", - "slug": "hello-world", - "name": "Hello World", - "practices": [], - "prerequisites": [], - "difficulty": 1 - } - ] - } +- Documentation: explain to the student what they need to do (can be auto-generated). +- Metadata: provides Exercism with some metadata on the exercise (can be mostly auto-generated). +- Test suite: verifies a solution's correctness (track-specific). +- Stub implementation: provided a starting point for students (track-specific). +- Example implementation: provides an example implementation that passes all the tests (track-specific). +- Additional files: ensure that the tests can run (track-specific, optional). + +Before we can create the "Hello, World!" exercise, you need to make some decisions about the track-specific filenames and file paths (test suite, stub implementation, example implementation and any additional files). + +The rule of thumb is to use names that are idiomatic for the language. +Where there are no strong preferences, prefer shallower directory structures. +The example implementation will need to be identifiable by the CI script, so it's advisable to choose a generic basename that all exercises can use, e.g. `example`, `sample`, or `reference-solution`. + +#### Configuring file paths + +Having chosen the track-specific file paths, you should configure them in the `files` key in the root `config.json` file. +The `files` key will serve as the template for all exercises, which allows any tooling (some of which we'll use in a second) to know where to look for files. +You can use various placeholders to allow for easy configuring of the exercise's slug (`hello-world` in this case). + +##### Example + +If your track uses PascalCase for its files, the `files` key might look like this: + +```json +"files": { + "solution": [ + "%{pascal_slug}.cs" + ], + "test": [ + "%{pascal_slug}Tests.cs" + ], + "example": [ + ".meta/Example.cs" + ] } ``` -You can use the [Configlet][configlet] tool to get a UUID. -Download Configlet by running `bin/fetch-configlet` from the root of the repository. -Then generate a UUID using the `bin/configlet uuid` command. +```exercism/note +The example file(s) should be stored within the `.meta` directory. +``` -### Generating required files +For more information, check the [`files` key documentation](/docs/building/tracks/config-json#files). -To implement the "Hello, World!" exercise, you'll need to create several files. -Which files exactly is described in the [Practice Exercises documentation](/docs/building/tracks/practice-exercises). +### Creating files -Most of the files can be added automatically by running Configlet's `sync` command: +Having specified the file path templates, you can then quickly scaffold the "Hello, World!" exercise's files by running the following commands from the track's root directory: +```shell +bin/fetch-configlet +bin/configlet create --practice-exercise hello-world ``` -bin/configlet sync --update --yes --docs --metadata --exercise hello-world -bin/configlet sync --update --tests include --exercise hello-world -``` -In addition to the generated files, you will to create a test suite, a stub solution that serves as the starting point for the student, and a sample solution that passes all the tests to verify it is possible to solve the exercise (CI will verify this). +### Implement exercise + +Once the scaffolded files have been created, you'll then have to: + +- Add tests to the tests file +- Add an example implementation +- Define the stub file's contents +- Within the exercise's `.meta/config.json` file: + - Add the GitHub username of the exercise's authors to the `authors` key + +#### Add tests + +A key part of adding an exercise is adding tests. +Rougly speaking, there are two options when implementing one of the above exercises: + +1. Implement the tests from scratch, using the test cases from the [exercise's `canonical-data.json`][canonical-data.json] +2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/hello-world` to get an overview of which tracks have implemented a specific exercise). + +For the "Hello, World!" exercise, there will only be one test case, so either option should be fine. + +#### Add example implementation + +The example implementation file should contain the code requires to solve the tests. + +#### Define the stub -In order to create these files, you need to make some decisions about filenames and file paths. -The rule of thumb is to use names that are idiomatic for the language, and where there are no strong preferences prefer shallower directory structures. -The sample solution will need to be identifiable by the CI script, so it's advisable to choose a generic basename that all exercises can use, e.g. `example`, `sample`, or `reference-solution`. +The stub file should have an _almost_ working solution to the tests, but with the "Hello, World!" text replaced with "Goodbye, Mars!". +Tip: just can copy-paste-modify the example solution. -### Configuring the exercise +### Update the exercise's author(s) -One you've decided on the filenames and paths, edit the `exercises/practice/hello-world/.meta/config.json` file to reflect those choices. -Also add your GitHub username to the `"authors"` array. +Once you're done with the exercise, please add your your GitHub username to the `"authors"` array in the exercise's `.meta/config.json` file. +This will ensure we correctly credit you with having created the exercise. [configlet]: /docs/building/configlet +[canonical-data.json]: (https://github.com/exercism/problem-specifications/blob/main/exercises/hello-world/canonical-data.json) diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index 937f92af..22ed938b 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -103,23 +103,46 @@ To make this all a bit more concrete, this is what a sample selection of initial ## Implement exercises +### Scaffold exercise + Having selected the exercises you want include in your track, the next step is to implement them. -Each of the above-mentioned exercises has three bits of information: +You can quickly scaffold a new Practice Exercise by running the following commands from the track's root directory: + +```shell +bin/fetch-configlet +bin/configlet create --practice-exercise +``` + +For more information, check the [`configlet create` docs](/docs/building/configlet/create) + +### Implement exercise + +Once the scaffolded files have been created, you'll then have to: + +- Add tests to the tests file +- Add an example implementation +- Define the stub file's contents +- Within the exercise's `.meta/config.json` file: + - Add the GitHub username of the exercise's authors to the `authors` key +- Within the track's `config.json` file: + - Check/update the exercise's difficulty + - Add concepts to the `practices` key (only required when the track has concept exercises) + - Add concepts to the `prerequisites` key (only required when the track has concept exercises) -1. `description.md`: explains what the exercise is about (required) -2. `metadata.toml`: metadata like the exercise's blurb (required) -3. `canonical-data.json`: a set of input/output combinations that describe the behavior of the exercise (optional) +#### Add tests -There are two options when implementing one of the above exercises: +A key part of adding an exercise is adding tests. +Rougly speaking, there are two options when adding tests for one of the above exercises: -1. Implement the exercise from scratch, using the test cases in the `canonical-data.json` file. -2. Port an implementation of the exercise from another track. +1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo][problem-specifications-exercises]. +2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). The second option can be particularly appealing, as it can give you results quickly. Keep in mind, though, that you should tweak the implementation to best fit your track. As an example, some tracks do not use classes but only work with functions. If your track usually works with objects though, you should adapt the implementation to what best fits your track. +[problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises/ [allergies]: https://github.com/exercism/problem-specifications/tree/main/exercises/allergies [alphametics]: https://github.com/exercism/problem-specifications/tree/main/exercises/alphametics [bank-account]: https://github.com/exercism/problem-specifications/tree/main/exercises/bank-account From 2f050901d0a30e432987d6e176b9ec6bfb73d4eb Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 15 Feb 2024 15:17:04 +0100 Subject: [PATCH 065/129] Fix invalid link (#490) --- building/tracks/new/add-first-exercise.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/new/add-first-exercise.md b/building/tracks/new/add-first-exercise.md index 2fbd127e..00a61935 100644 --- a/building/tracks/new/add-first-exercise.md +++ b/building/tracks/new/add-first-exercise.md @@ -114,4 +114,4 @@ Once you're done with the exercise, please add your your GitHub username to the This will ensure we correctly credit you with having created the exercise. [configlet]: /docs/building/configlet -[canonical-data.json]: (https://github.com/exercism/problem-specifications/blob/main/exercises/hello-world/canonical-data.json) +[canonical-data.json]: https://github.com/exercism/problem-specifications/blob/main/exercises/hello-world/canonical-data.json From afc4bc7d6fd9b4888e98a6824f9c3956d22901d3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 20 Feb 2024 13:03:07 +0100 Subject: [PATCH 066/129] fix configlet link (#491) * Add missing configlet format entry * Improve configlet format documentation --- building/config.json | 6 ++++++ building/configlet/format.md | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/building/config.json b/building/config.json index 07fc7eb6..5e961f41 100644 --- a/building/config.json +++ b/building/config.json @@ -37,6 +37,12 @@ "path": "building/configlet/create.md", "title": "configlet create" }, + { + "uuid": "425c2bff-675b-4e15-9d53-eb595d7632f9", + "slug": "configlet/format", + "path": "building/configlet/format.md", + "title": "configlet fmt" + }, { "uuid": "d8d9ff53-fe8f-4333-80c5-87517433cf7d", "slug": "configlet/generating-documents", diff --git a/building/configlet/format.md b/building/configlet/format.md index 88fc86d5..d9f444d5 100644 --- a/building/configlet/format.md +++ b/building/configlet/format.md @@ -10,7 +10,7 @@ An Exercism track repo has many JSON files, including: These files are more readable if they have a consistent formatting Exercism-wide, and so configlet has a `fmt` command for rewriting a track's JSON files in a canonical form. -The `fmt` command currently only operates on the exercise `.meta/config.json` files, but it is likely to operate on all the track JSON files in the future. +The `fmt` command currently operates on the exercise `.meta/config.json` files and the track `config.json` file, but it is likely to operate on all the track JSON files in the future. ## Usage @@ -31,7 +31,7 @@ Options for fmt: -y, --yes Auto-confirm the prompt from --update ``` -A plain `configlet fmt` makes no changes to the track and checks the formatting of the `.meta/config.json` file for every Concept Exercise and Practice Exercise. +A plain `configlet fmt` makes no changes to the track and checks the formatting of the `.meta/config.json` file for every Concept Exercise and Practice Exercise and the track `config.json` file. To print a list of paths for which there is not already a formatted exercise `.meta/config.json` file (exiting with a non-zero exit code if at least one exercise lacks a formatted config file): @@ -102,4 +102,4 @@ Note that `configlet fmt` only operates on exercises that exist in the track-lev Therefore if you are implementing a new exercise on a track and want to format its `.meta/config.json` file, please add the exercise to the track-level `config.json` file first. If the exercise is not yet ready to be user-facing, please set its `status` value to `wip`. -The exit code is 0 when every seen exercise has a formatted `.meta/config.json` file when configlet exits, and 1 otherwise. +The exit code is 0 when every seen config file is formatted when configlet exits, and 1 otherwise. From 77ccd2ff1bdf86d4c04f948e8f6de5384cc26b2a Mon Sep 17 00:00:00 2001 From: Exercism Bot Date: Fri, 1 Mar 2024 14:06:33 +0000 Subject: [PATCH 067/129] =?UTF-8?q?=F0=9F=A4=96=20Sync=20org-wide=20files?= =?UTF-8?q?=20to=20upstream=20repo=20(#492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More info: https://github.com/exercism/org-wide-files/commit/0c0972d1df4cd18d98c7df316348315b06ef49b4 --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index df8e3676..3f7813de 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -90,4 +90,4 @@ This policy was initially adopted from the Front-end London Slack community and A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md). _This policy is a "living" document, and subject to refinement and expansion in the future. -This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._ +This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Discord, Forum, Twitter, email) and any other Exercism entity or event._ From e452e0b83e874b420ba08beb575b003faa31ef15 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 26 Mar 2024 12:47:34 +0100 Subject: [PATCH 068/129] Document `custom` key in `.meta/config.json` file (#493) --- building/tracks/concept-exercises.md | 6 +++++- building/tracks/practice-exercises.md | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/building/tracks/concept-exercises.md b/building/tracks/concept-exercises.md index d551807a..2172bb21 100644 --- a/building/tracks/concept-exercises.md +++ b/building/tracks/concept-exercises.md @@ -339,6 +339,7 @@ This file contains meta information on the exercise: - `representer`: Meta information related to how the representer processes this file (optional) - `version`: An integer for the version of the representer to use for the exercise (required if parent key is present) - `icon`: The slug of the icon (see [the full list of icons][exercise-icons]). If not specified, the exercise's slug will be used (optional) +- `custom`: Any exercise-specific, non-standard data. Can be used to customize behavior of the track's tooling per exercise (optional) If someone is both an author _and_ a contributor, only list that person as an author. @@ -378,7 +379,10 @@ Assume that the user `FSharpForever` has written an exercise called `log-levels` "representer": { "version": 2 }, - "icon": "logs" + "icon": "logs", + "custom": { + "parallel": true + } } ``` diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises.md index e6eccb0d..983de55d 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises.md @@ -307,6 +307,7 @@ This file contains meta information on the exercise: - `representer`: Meta information related to how the representer processes this file (optional) - `version`: An integer for the version of the representer to use for the exercise (required if parent key is present) - `icon`: The slug of the icon (see [the full list of icons][exercise-icons]). If not specified, the exercise's slug will be used (optional) +- `custom`: Any exercise-specific, non-standard data. Can be used to customize behavior of the track's tooling per exercise (optional) If someone is both an author _and_ a contributor, only list that person as an author. From 7640d7f7a8d45caf622d8c5b820e29018a8c3bba Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 9 Apr 2024 12:19:22 +0200 Subject: [PATCH 069/129] Add more articles/approaches file docs (#494) --- building/tracks/approaches.md | 16 ++++++++++++++-- building/tracks/articles.md | 11 +++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/building/tracks/approaches.md b/building/tracks/approaches.md index 8a6ba9ea..d0b3a6f2 100644 --- a/building/tracks/approaches.md +++ b/building/tracks/approaches.md @@ -43,6 +43,18 @@ In general, [Practice Exercises](/docs/building/tracks/practice-exercises) are m For [Concept Exercises](/docs/building/tracks/concept-exercises), discussing the exemplar approach might be interesting. For example, you could show how the concept being taught makes certain code easier to write. -## Configuring +## Files -Make sure to set the `approaches.snippet_extension` field in your [track's `config.json` file](/docs/building/tracks/config-json). +Each approach must add the following two files: + +- `.approaches//content.md`: description of the approach (see [the docs](/docs/building/tracks/practice-exercises#file-approaches-approach-slug-content-md)) +- `.approaches//snippet.txt`: snippet showcasing the approach (see [the docs](/docs/building/tracks/practice-exercises#file-approaches-approach-slug-snippet-txt)) + +You'll then need to add or update: + +- `.approaches/config.json`: metadata for the approaches (see [the docs](/docs/building/tracks/practice-exercises#file-approaches-config-json)) + +### Configuring snippet extension + +You can also use a custom extension instead of the default `.txt` extension by setting the `approaches.snippet_extension` field in your [track's `config.json` file](/docs/building/tracks/config-json). +If set, the snippet file you need to add must be named `.approaches//snippet.`. diff --git a/building/tracks/articles.md b/building/tracks/articles.md index 9ec30efc..3706acdf 100644 --- a/building/tracks/articles.md +++ b/building/tracks/articles.md @@ -17,3 +17,14 @@ Potential topics an article could explore: ## What exercises to write articles for? Any exercise, as long as there is something interesting to explore. + +## Files + +Each article must add the following two files: + +- `.articles//content.md`: description of the articleh (see [the docs](/docs/building/tracks/practice-exercises#file-articles-article-slug-content-md)) +- `.articles//snippet.md`: snippet showcasing the article (see [the docs](/docs/building/tracks/practice-exercises#file-articles-article-slug-snippet-txt)) + +You'll then need to add or update: + +- `.articles/config.json`: metadata for the articles (see [the docs](/docs/building/tracks/practice-exercises#file-article-config-json)) From 46b304375b18456c2ec94a187bc999f5d6016f8f Mon Sep 17 00:00:00 2001 From: mk-mxp <55182845+mk-mxp@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:28:53 +0200 Subject: [PATCH 070/129] Add test generator to track tooling (#495) * Write test generator article * Link article to track tooling docs * Link article to new track docs * Add article to config.json --- building/config.json | 7 ++ building/tooling/README.md | 6 + building/tooling/test-generators.md | 134 +++++++++++++++++++++++ building/tracks/new/implement-tooling.md | 23 +++- 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 building/tooling/test-generators.md diff --git a/building/config.json b/building/config.json index 5e961f41..32741015 100644 --- a/building/config.json +++ b/building/config.json @@ -820,6 +820,13 @@ "title": "Snippet Extractor", "blurb": "" }, + { + "uuid": "ba393f61-0f7d-4178-922a-c16cda052338", + "slug": "tooling/test-generators", + "path": "building/tooling/test-generators.md", + "title": "Test Generators", + "blurb": "" + }, { "uuid": "3423667a-3b30-4590-9a88-5a30f712f382", "slug": "product", diff --git a/building/tooling/README.md b/building/tooling/README.md index 25a92015..f8a61f98 100644 --- a/building/tooling/README.md +++ b/building/tooling/README.md @@ -19,3 +19,9 @@ There is also general tooling that can be configured for your track: - **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)** - **[Snippet Extractor](/docs/building/tooling/snippet-extractor)** + +## Tooling for contribution + +Tracks may also provide tooling for contribution: + +- **[Test Generators](/docs/building/tooling/test-generators)** diff --git a/building/tooling/test-generators.md b/building/tooling/test-generators.md new file mode 100644 index 00000000..9b4066ea --- /dev/null +++ b/building/tooling/test-generators.md @@ -0,0 +1,134 @@ +# Test Generators + +A Test Generator is a piece of software that creates a practice exercise's tests from the common [problem specifications](https://github.com/exercism/problem-specifications). +Some tracks also create tests for concept exercises from a similar track-owned data source. + +A Test Generator give us these advantages: + +1. They allow adding exercises more quickly without writing much boilerplate code. +2. Contributors can focus on the **design** of an exercise immediately. +3. Along the track life, automatic updates of existing tests can lower maintainer workload. + +## Contributing to Test Generators + +Each language may have its own Test Generator, written in that language. +It adds code and sometimes files to what [`configlet`](/docs/building/configlet) created / updated. +The code usually is rendered from template files, written for the tracks preferred templating engine. +You should find all the details in the tracks contribution docs or a `README` near the test generator. + +You should also know: + +- what [`configlet create`](/docs/building/configlet/create) or [`configlet sync`](/docs/building/configlet/sync) do. +- what [`canonical-data.json` in problem specifications](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson) may provide. +- why ["creating from scratch" is different from "reproducing for updates"](#from-scratch-vs-updating). + +## Creating a Test Generator from scratch + +There are various test generators in Exercism's tracks. +These guidelines are based on the experiences of these tracks. + +Even so test generators work very similar, they are very track specific. +It starts with the choice of the templating engine and ends with additional things they do for each track. +So a common test generator was not and will not be written. + +There were helpful discussions [around the Rust](https://forum.exercism.org/t/advice-for-writing-a-test-generator/7178) and the [JavaScript](https://forum.exercism.org/t/test-generators-for-tracks/10615) test generators. +The [forum](https://forum.exercism.org/c/exercism/building-exercism/125) also is the best place for seeking additional advice. + +### Things to know + +- `configlet` cache with a local copy of the problem specifications is stored in a [location depending on the users system](https://nim-lang.org/docs/osappdirs.html#getCacheDir). + Use `configlet info -o -v d | head -1 | cut -d " " -f 5` to get the location. + Or fetch data from the problem specifications repository directly (`https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/{{exercise-slug}}/canonical-data.json`) +- [`canonical-data.json` data structure](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson) is well documented. There is optional nesting of `cases` arrays in `cases` mixed with actual test cases. +- The contents of `input` and `expected` test case keys of `canonical-data.json` vary largely. These can include simple scalar values, lambdas in pseudo code, lists of operations to perform on the students code and any other kind of input or result one can imagine. + +### From Scratch vs. Updating + +There are 2 common tasks a test generator may do, that require separate approaches: + +- [Creating tests from scratch](#creating-tests-from-scratch) +- [Reproducing tests for updates](#reproducing-tests-for-updates) + +The reason for this distinction is "designing the exercise" vs. "production-ready code". + +When creating tests from scratch the test generator should provide all the information contained in `canonical-data.json` in the resulting files. +This enables contributors to simply open up the generated test file(s) and find all relevant information interwoven with the tracks boilerplate code. +They then design the exercise's tests and student facing code based on these files rather than on the original `canonical-data.json`. +As there is no knowledge of exercise specific things, yet, a one-fits-all template targeting the boilerplate code can be used. + +When the exercise is already in production, changes in `canonical-data.json` are rarely a reason to change the design of the exercise. +So reproducing tests for updates is based on the existing design and should result in production-ready code. +Much of the additional data presented when creating the exercise from scratch is no longer part of the result. + +Instead, very often additional conversion of test case data is required, which is specific to this exercise. +Most tracks opt for having at least one template per exercise for this. +This way they can represent all the design choices in that template without complicating things too much for further contribution. + +### Creating tests from scratch + +This is more productive in the beginning of a tracks life. +It is way more easy to implement than the "updating" part. + +Doing only the bare minimum required for a first usable test generator may already help contributors a lot: + +- Read the `canonical-data.json` of the exercise from `configlet` cache or retrieve it from GitHub directly +- Preserve all data (including `comments`, `description` and `scenarios`) +- If the tracks testing framework supports no nested test case groups, flatten the nested data structure into a list of test cases +- Dump the test cases into the one-fits-all boilerplate template(s) + - Preserve the test case grouping for nested test case groups, e.g. + - using the test frameworks grouping capability + - using comments and code folding markers (`{{{`, `}}}`) + - concatenating group `description` and test case `description` + - Show all data (including `comments`, `description` and `scenarios`) + +```exercism/note +Don't try to produce perfect production-ready code! +Dump all data and let the contributor design the exercise from that. +There is way too much variation in the exercises to handle all in one template. +``` + +There are optional things a test generator might do: + +- Provide code for a simple test case (e.g. call a function with `input`, compare result to `expected`) +- Provide boilerplate code for student code file(s) or additional files required by the track +- Respect `scenarios` for grouping / test case selection +- Skip over "reimplemented" test cases (those referred to in a `reimplements` key of another test case) +- Update `tests.toml` with `include=false` to reflect tests skipped by `scenarios` / `reimplements` + +### Reproducing tests for updates + +This may become more relevant over track life time. +It is much harder to implement than the "from scratch" part. +If you need to invest much effort here, maybe manual maintenance is more efficient. +Also keep in mind: maintaining the test generator adds to the maintainers workload, too. + +```exercism/note +Choose a flexible and extensible templating engine! +The test cases vary largely between exercises. +They include simple scalar values, lambdas in pseudo code, lists of operations to perform on the students code and any other kind of input or result one can imagine. +``` + +Doing the bare minimum required for a usable updating test generator includes: + +- Read the `canonical-data.json` of the exercise from `configlet` cache or retrieve it from GitHub directly +- If the tracks testing framework supports no nested test case groups, flatten the nested data structure into a list of test cases +- Render the test cases into the exercise specific template(s) located in an exercise's `.meta/` folder + - Render production-ready code that matches the manually designed exercise + - Skip over "reimplemented" test cases (those referred to in a `reimplements` key of another test case) + - Render only test cases selected by `tests.toml` (or another track-specific data source) + +There are different strategies for respecting test case changes like "replace always", "replace when forced to", "use `tests.toml` to ignore replaced test cases" (works like a baseline for known test issues). +None of them is perfect. + +```exercism/note +Don't try to have a versatile one-fits-all template! +There is way too much variation in the exercises to handle all in one template. +``` + +There are optional things a test generator might do: + +- Provide a library of templates and / or extensions to the template engine +- Maintain or respect another track-specific data source than `tests.toml` +- Maintain student code file(s) or additional files required by the track +- Handle `scenarios` for grouping / test case selection +- Have a check functionality (e.g. to run after `configlet sync`) to detect when updating is required diff --git a/building/tracks/new/implement-tooling.md b/building/tracks/new/implement-tooling.md index 008a7518..37e4e973 100644 --- a/building/tracks/new/implement-tooling.md +++ b/building/tracks/new/implement-tooling.md @@ -4,15 +4,23 @@ After launching the track with the first 20+ exercises, the focus should shift t Each track has various pieces of tooling that run in production. Each provides a key function to the learning experience of that language. -There are (currently) three pieces of tooling: +There also can be track tooling to run for contribution or maintainance. +Such tools provide help to create new exercises or keeping them up-to-date. +Each lowers the barriers for new contributors and speeds up the growth of the track. + +There are (currently) three pieces of tooling for production: - **[Test Runners](/docs/building/tooling/test-runners)**: runs an exercise's tests against a student's code. (required) - **[Representers](/docs/building/tooling/representers)**: create a normalized representation of a solution (optional) - **[Analyzers](/docs/building/tooling/analyzers)**: automatically assess student submissions and provide mentor-style commentary. (optional) +Some tracks have (currently) implemented these pieces of tooling for contribution: + +- **[Test Generators](/docs/building/tooling/test-generators)**: create or update an exercise's tests and student's code interface. (optional) + ## Which tool to implement? -Of these three tools, the test runner should be implemented first as it enables: +Of the three production tools, the test runner should be implemented first as it enables: - Students to solve exercises using the [in-browser editor](/docs/using/solving-exercises/using-the-online-editor) ([no CLI needed](/docs/using/solving-exercises/working-locally)). - The website to automatically verify if an iteration passes all the tests. @@ -31,9 +39,18 @@ To get started building a Representer, check the [Creating a Representer from sc Finally, after having implemented a Representer, the last tool to build is the Analyzer. To get started building an Analyzer, check the [Creating an Analyzer from scratch](/docs/building/tooling/analyzers/creating-from-scratch) document. +To speed up adding new exercises, a Test Generator is very handy. +The first thing to implement is creating tests for new exercises from scratch. +This takes away writing boilerplate code and leaves the focus on designing the exercises. +Later in track development, the test generator may become capable of reproducing production-ready tests for updates. +There are many hints and guidelines collected in the [Test Generators](/docs/building/tooling/test-generators) document. + ## Implementation The tooling is (generally) written in the track's language, but you're completely free to use whatever language (or combination of languages) you prefer. -Each tool is packaged and run as a [Docker container](/docs/building/tooling/docker). +Each production tool is packaged and run as a [Docker container](/docs/building/tooling/docker). Tooling images are deployed automatically using a [Docker workflow](https://github.com/exercism/generic-test-runner/blob/main/.github/workflows/docker.yml). + +Tools for contribution should fit into a workflow common for the language of the track. +When using external packages, make sure these do not get packaged into the production Docker images or loaded in CI. From 3ff8e286cf62d1682b423fc0a3115172a03788f6 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 16 Apr 2024 11:15:27 +0200 Subject: [PATCH 071/129] Cleanup tooling docs (#496) --- building/tooling/README.md | 49 +++++++++++++++++------- building/tracks/new/implement-tooling.md | 43 ++++++++++++--------- 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/building/tooling/README.md b/building/tooling/README.md index f8a61f98..dcbc5e53 100644 --- a/building/tooling/README.md +++ b/building/tooling/README.md @@ -1,27 +1,50 @@ # Track Tooling -Each track has various pieces of tooling that run in production. -Each provides a key function to the learning experience of that language. +There are two types of track tooling: + +- Production: provide a key function to the learning experience of that language +- Maintenance: help with track maintenance Tooling is (generally) written in the language of the specific track, and is built and maintained by maintainers. -All tooling is deployed using the same Docker workflow. +## Production tooling + +These tools run on Exercism's production servers. + +### Track-specific production tooling + +There are three pieces of track-specific production tooling: + +- **[Test Runners](/docs/building/tooling/test-runners)** (essential) +- **[Representers](/docs/building/tooling/representers)** (optional) +- **[Analyzers](/docs/building/tooling/analyzers)** (optional) -There are (currently) three pieces of track-specific tooling: +### General production tooling -- **[Test Runners](/docs/building/tooling/test-runners)** -- **[Representers](/docs/building/tooling/representers)** -- **[Analyzers](/docs/building/tooling/analyzers)** +There are two pieces of general production tooling that can be configured for your track: -## General tooling +- **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)** (optional) +- **[Snippet Extractor](/docs/building/tooling/snippet-extractor)** (optional) -There is also general tooling that can be configured for your track: +### Deployment -- **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)** -- **[Snippet Extractor](/docs/building/tooling/snippet-extractor)** +Production tools are built as Docker images. +They are auto-deployed to Exercism's production servers using CI workflows. -## Tooling for contribution +## Maintenance tooling -Tracks may also provide tooling for contribution: +Maintenance tooling is designed to help with maintaining the track. +They usually run locally (on the maintainer/contributor's machine) and sometimes in CI, but never in production. + +Here are some examples of maintenance tooling: - **[Test Generators](/docs/building/tooling/test-generators)** + +## Implementation + +Track tooling is usually (mostly) written in the track's language. + +```exercism/caution +While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. +We recommend using the track's language where possible, only using additional languages when it cannot be avoided. +``` diff --git a/building/tracks/new/implement-tooling.md b/building/tracks/new/implement-tooling.md index 37e4e973..8977b06c 100644 --- a/building/tracks/new/implement-tooling.md +++ b/building/tracks/new/implement-tooling.md @@ -1,12 +1,13 @@ # Implement tooling -After launching the track with the first 20+ exercises, the focus should shift to implementing the track tooling. -Each track has various pieces of tooling that run in production. -Each provides a key function to the learning experience of that language. +After launching the track with the first 20+ exercises, the focus should shift to implementing the [track tooling](/docs/building/tooling). -There also can be track tooling to run for contribution or maintainance. -Such tools provide help to create new exercises or keeping them up-to-date. -Each lowers the barriers for new contributors and speeds up the growth of the track. +There are two types of track tooling: + +- Production: provide a key function to the learning experience of that language +- Maintenance: help with track maintenance + +## Production tooling There are (currently) three pieces of tooling for production: @@ -14,13 +15,16 @@ There are (currently) three pieces of tooling for production: - **[Representers](/docs/building/tooling/representers)**: create a normalized representation of a solution (optional) - **[Analyzers](/docs/building/tooling/analyzers)**: automatically assess student submissions and provide mentor-style commentary. (optional) -Some tracks have (currently) implemented these pieces of tooling for contribution: +## Maintenance tooling + +To help with track maintenance, one can also build: -- **[Test Generators](/docs/building/tooling/test-generators)**: create or update an exercise's tests and student's code interface. (optional) +- **[Test Generators](/docs/building/tooling/test-generators)**: auto generate/update an exercise's tests and student's code interface. (optional) ## Which tool to implement? -Of the three production tools, the test runner should be implemented first as it enables: +The production tools are more important than maintenance tools. +Of the three production tools, the Test Runner should be implemented first as it enables: - Students to solve exercises using the [in-browser editor](/docs/using/solving-exercises/using-the-online-editor) ([no CLI needed](/docs/using/solving-exercises/working-locally)). - The website to automatically verify if an iteration passes all the tests. @@ -39,18 +43,21 @@ To get started building a Representer, check the [Creating a Representer from sc Finally, after having implemented a Representer, the last tool to build is the Analyzer. To get started building an Analyzer, check the [Creating an Analyzer from scratch](/docs/building/tooling/analyzers/creating-from-scratch) document. -To speed up adding new exercises, a Test Generator is very handy. -The first thing to implement is creating tests for new exercises from scratch. -This takes away writing boilerplate code and leaves the focus on designing the exercises. -Later in track development, the test generator may become capable of reproducing production-ready tests for updates. -There are many hints and guidelines collected in the [Test Generators](/docs/building/tooling/test-generators) document. +At this point, focus should probably shift back to adding more exercises. +To speed up adding new exercises, consider building a [Test Generator](/docs/building/tooling/test-generators). ## Implementation -The tooling is (generally) written in the track's language, but you're completely free to use whatever language (or combination of languages) you prefer. +Track tooling is usually (mostly) written in the track's language. + +```exercism/caution +While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. +We recommend using the track's language where possible, only using additional languages when it cannot be avoided. +``` + +## Deployment -Each production tool is packaged and run as a [Docker container](/docs/building/tooling/docker). +Production tools are packaged and run as a [Docker container](/docs/building/tooling/docker). Tooling images are deployed automatically using a [Docker workflow](https://github.com/exercism/generic-test-runner/blob/main/.github/workflows/docker.yml). -Tools for contribution should fit into a workflow common for the language of the track. -When using external packages, make sure these do not get packaged into the production Docker images or loaded in CI. +Maintenance tools are _not_ deployed. From 1ac8bd25eda0078971bfcf32d3f6d9c743fd3370 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 18 Apr 2024 07:59:51 +0200 Subject: [PATCH 072/129] Tweak test generator documentation (#497) * Tweak test generators docs * More updates * More work * CLI * Docs * More work * Tweaks * Minor rewording * Work * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tracks/new/implement-tooling.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Update building/tooling/README.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> * Clarify nesting * Update building/tooling/test-generators.md Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> --------- Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com> --- building/tooling/README.md | 2 +- building/tooling/test-generators.md | 356 +++++++++++++++++------ building/tracks/new/implement-tooling.md | 2 +- 3 files changed, 267 insertions(+), 93 deletions(-) diff --git a/building/tooling/README.md b/building/tooling/README.md index dcbc5e53..245a626d 100644 --- a/building/tooling/README.md +++ b/building/tooling/README.md @@ -46,5 +46,5 @@ Track tooling is usually (mostly) written in the track's language. ```exercism/caution While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. -We recommend using the track's language where possible, only using additional languages when it cannot be avoided. +We recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` diff --git a/building/tooling/test-generators.md b/building/tooling/test-generators.md index 9b4066ea..b66c289c 100644 --- a/building/tooling/test-generators.md +++ b/building/tooling/test-generators.md @@ -1,134 +1,308 @@ # Test Generators -A Test Generator is a piece of software that creates a practice exercise's tests from the common [problem specifications](https://github.com/exercism/problem-specifications). -Some tracks also create tests for concept exercises from a similar track-owned data source. +A Test Generator is a track-specific piece of software to automatically generate a practice exercise's tests. +It does this by converting the exercise's JSON test cases to tests in the track's language. -A Test Generator give us these advantages: +## Benefits -1. They allow adding exercises more quickly without writing much boilerplate code. -2. Contributors can focus on the **design** of an exercise immediately. -3. Along the track life, automatic updates of existing tests can lower maintainer workload. +Some benefits of having a Test Generator are: -## Contributing to Test Generators +1. Exercises can be added faster +2. Automates "boring" parts of adding an exercise +3. Easy to sync tests with latest canonical data -Each language may have its own Test Generator, written in that language. -It adds code and sometimes files to what [`configlet`](/docs/building/configlet) created / updated. -The code usually is rendered from template files, written for the tracks preferred templating engine. -You should find all the details in the tracks contribution docs or a `README` near the test generator. +## Use cases -You should also know: +In general, one runs a Test Generator to either: -- what [`configlet create`](/docs/building/configlet/create) or [`configlet sync`](/docs/building/configlet/sync) do. -- what [`canonical-data.json` in problem specifications](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson) may provide. -- why ["creating from scratch" is different from "reproducing for updates"](#from-scratch-vs-updating). +1. Generate the tests for a _new_ exercise +2. Update the tests of an _existing_ exercise -## Creating a Test Generator from scratch +### Generate tests for new exercise -There are various test generators in Exercism's tracks. -These guidelines are based on the experiences of these tracks. +Adding a Test Generator for a new exercise allows one to generate its tests file(s). +Provided the Test Generator itself has already been implemented, generating the tests for the new exercise will be (far) less work than writing them from scratch. -Even so test generators work very similar, they are very track specific. -It starts with the choice of the templating engine and ends with additional things they do for each track. -So a common test generator was not and will not be written. +### Update tests of existing exercise -There were helpful discussions [around the Rust](https://forum.exercism.org/t/advice-for-writing-a-test-generator/7178) and the [JavaScript](https://forum.exercism.org/t/test-generators-for-tracks/10615) test generators. -The [forum](https://forum.exercism.org/c/exercism/building-exercism/125) also is the best place for seeking additional advice. +Once an exercise has a Test Generator, you can re-run it to update/sync the exercise with its latest canonical data. +We recommend doing this periodically, to check if there are problematic test cases that need to be updated or new tests you might want to include. -### Things to know +## Starting point -- `configlet` cache with a local copy of the problem specifications is stored in a [location depending on the users system](https://nim-lang.org/docs/osappdirs.html#getCacheDir). - Use `configlet info -o -v d | head -1 | cut -d " " -f 5` to get the location. - Or fetch data from the problem specifications repository directly (`https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/{{exercise-slug}}/canonical-data.json`) -- [`canonical-data.json` data structure](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson) is well documented. There is optional nesting of `cases` arrays in `cases` mixed with actual test cases. -- The contents of `input` and `expected` test case keys of `canonical-data.json` vary largely. These can include simple scalar values, lambdas in pseudo code, lists of operations to perform on the students code and any other kind of input or result one can imagine. +There are two possible starting points when implementing a Test Generator for an exercise: -### From Scratch vs. Updating +1. The exercise is new and doesn't yet have any tests +2. The exercise already exists and has existing tests -There are 2 common tasks a test generator may do, that require separate approaches: +```exercism/caution +If there are existing tests, implement the Test Generator such that the tests it generates do not break existing solutions. +``` + +## Design + +Broadly speaking, test files are generated using either: + +- Code: the tests files are (mostly) generated via code +- Templates: the tests files are (mostly) generated using templates + +We've found that the code-based approach will lead to fairly complex Test Generator code, whereas the template-based approach is simpler. + +What we recommend is the following flow: + +1. Read the exercise's canonical data +2. Exclude the test cases that are marked as `include = false` in the exercise's `tests.toml` file +3. Convert the exercise's canonical data into a format that can be used in a template +4. Pass the exercise's canonical data to an exercise-specific template -- [Creating tests from scratch](#creating-tests-from-scratch) -- [Reproducing tests for updates](#reproducing-tests-for-updates) +The key benefit of this setup is that each exercise has its own template, which: -The reason for this distinction is "designing the exercise" vs. "production-ready code". +- Makes it obvious how the test files are generated +- Makes them easier to debug +- Makes it safe to edit them without risking breaking another exercise -When creating tests from scratch the test generator should provide all the information contained in `canonical-data.json` in the resulting files. -This enables contributors to simply open up the generated test file(s) and find all relevant information interwoven with the tracks boilerplate code. -They then design the exercise's tests and student facing code based on these files rather than on the original `canonical-data.json`. -As there is no knowledge of exercise specific things, yet, a one-fits-all template targeting the boilerplate code can be used. +```exercism/caution +Some additional things to be aware of when designing the test generator -When the exercise is already in production, changes in `canonical-data.json` are rarely a reason to change the design of the exercise. -So reproducing tests for updates is based on the existing design and should result in production-ready code. -Much of the additional data presented when creating the exercise from scratch is no longer part of the result. +- Minimize the pre-processing of canonical data inside the Test Generator +- Try to reduce coupling between templates +``` -Instead, very often additional conversion of test case data is required, which is specific to this exercise. -Most tracks opt for having at least one template per exercise for this. -This way they can represent all the design choices in that template without complicating things too much for further contribution. +## Implementation -### Creating tests from scratch +The Test Generator is usually (mostly) written in the track's language. -This is more productive in the beginning of a tracks life. -It is way more easy to implement than the "updating" part. +```exercism/caution +While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. +We recommend using the track's language where possible, only using additional languages when it makes maintaining or contributing easier. +``` -Doing only the bare minimum required for a first usable test generator may already help contributors a lot: +### Canonical data -- Read the `canonical-data.json` of the exercise from `configlet` cache or retrieve it from GitHub directly -- Preserve all data (including `comments`, `description` and `scenarios`) -- If the tracks testing framework supports no nested test case groups, flatten the nested data structure into a list of test cases -- Dump the test cases into the one-fits-all boilerplate template(s) - - Preserve the test case grouping for nested test case groups, e.g. - - using the test frameworks grouping capability - - using comments and code folding markers (`{{{`, `}}}`) - - concatenating group `description` and test case `description` - - Show all data (including `comments`, `description` and `scenarios`) +The core data the Test Generator works with is an exercise's [`canonical-data.json` file](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson). +This file is defined in the [exercism/problem-specifications repo](https://github.com/exercism/problem-specifications), which defines shared metadata for many Exercism's exercises. ```exercism/note -Don't try to produce perfect production-ready code! -Dump all data and let the contributor design the exercise from that. -There is way too much variation in the exercises to handle all in one template. +Not all exercises have a `canonical-data.json` file. +In case they don't, you'll need to manually create the tests, as there is no data for the Test Generator to work with. +``` + +#### Structure + +Canonical data is defined in a JSON object. +This object contains a `"cases"` field which contains the test cases. +These test cases (normally) correspond one-to-one to tests in your track. + +Each test case has a couple of properties, with the description, property, input value(s) and expected value being the most important ones. +Here is a (partial) example of the [canonical-data.json file of the leap exercise](https://github.com/exercism/problem-specifications/blob/main/exercises/leap/canonical-data.json): + +```json +{ + "exercise": "leap", + "cases": [ + { + "uuid": "6466b30d-519c-438e-935d-388224ab5223", + "description": "year not divisible by 4 in common year", + "property": "leapYear", + "input": { + "year": 2015 + }, + "expected": false + }, + { + "uuid": "4fe9b84c-8e65-489e-970b-856d60b8b78e", + "description": "year divisible by 4, not divisible by 100 in leap year", + "property": "leapYear", + "input": { + "year": 1996 + }, + "expected": true + } + ] +} ``` -There are optional things a test generator might do: +The Test Generator's main responsibility is to transform this JSON data into track-specific tests. +Here's how the above JSON could translate into Nim test code: -- Provide code for a simple test case (e.g. call a function with `input`, compare result to `expected`) -- Provide boilerplate code for student code file(s) or additional files required by the track -- Respect `scenarios` for grouping / test case selection -- Skip over "reimplemented" test cases (those referred to in a `reimplements` key of another test case) -- Update `tests.toml` with `include=false` to reflect tests skipped by `scenarios` / `reimplements` +```nim +import unittest +import leap + +suite "Leap": + test "year not divisible by 4 in common year": + check isLeapYear(2015) == false + + test "year divisible by 4, not divisible by 100 in leap year": + check isLeapYear(1996) == true +``` -### Reproducing tests for updates +The structure of the `canonical-data.json` file is [well documented](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson) and it also has a [JSON schema](https://github.com/exercism/problem-specifications/blob/main/canonical-data.schema.json) definition. -This may become more relevant over track life time. -It is much harder to implement than the "from scratch" part. -If you need to invest much effort here, maybe manual maintenance is more efficient. -Also keep in mind: maintaining the test generator adds to the maintainers workload, too. +##### Nesting + +Some exercises use nesting in their canonical data. +This means that each element in a `cases` array can be either: + +1. A regular test case (no child test cases) +2. A grouping of test cases (one or more child test cases) ```exercism/note -Choose a flexible and extensible templating engine! -The test cases vary largely between exercises. -They include simple scalar values, lambdas in pseudo code, lists of operations to perform on the students code and any other kind of input or result one can imagine. +You can identify the types of an element by checking for the presence of fields that are exclusive to one type of element. +Probably the best way to do this is using the `"cases"` key, which is only present in test case groups. +``` + +Here is an example of nested test cases: + +```json +{ + "cases": [ + { + "uuid": "e9c93a78-c536-4750-a336-94583d23fafa", + "description": "data is retained", + "property": "data", + "input": { + "treeData": ["4"] + }, + "expected": { + "data": "4", + "left": null, + "right": null + } + }, + { + "description": "insert data at proper node", + "cases": [ + { + "uuid": "7a95c9e8-69f6-476a-b0c4-4170cb3f7c91", + "description": "smaller number at left node", + "property": "data", + "input": { + "treeData": ["4", "2"] + }, + "expected": { + "data": "4", + "left": { + "data": "2", + "left": null, + "right": null + }, + "right": null + } + } + ] + } + ] +} ``` -Doing the bare minimum required for a usable updating test generator includes: +```exercism/caution +If your track does not support grouping tests, you'll need to: + +- Traverse/flatten the `cases` hierarchy to end up with only the innermost (leaf) test cases +- Combine the test case description with its parent description(s) to create a unique test name +```` + +#### Input and expected values + +The contents of the `input` and `expected` test case keys vary widely. +In most cases, they'll be scalar values (like numbers, booleans or strings) or simple objects. +However, occasionally you'll also find more complex values that will likely require a bit of preprocessing, such as lambdas in pseudo code, lists of operations to perform on the students code and more. + +#### Scenarios + +Test cases have an optional `scenarios` field. +This field can be used by the test generator to special case certain test cases. +The most common use case is to ignore certain types of tests, for example tests with the `"unicode"` scenario as your track's language might not support Unicode. + +The full list of scenarios can be found [here](https://github.com/exercism/problem-specifications/blob/main/SCENARIOS.txt). + +#### Reading canonical-data.json files + +There are a couple of options to read the `canonical-data.json` files: + +1. Fetch them directly from the `problem-specifications` repository (e.g. `https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/leap/canonical-data.json`). +2. Add the `problem-specifications` repo as a Git submodule to the track repo. +3. Read them from the `configlet` cache. + The [location depends on the user's system](https://nim-lang.org/docs/osappdirs.html#getCacheDir), but you can use `configlet info -o -v d | head -1 | cut -d " " -f 5` to programmatically get the location. + +#### Track-specific test cases + +If your track would like to add some additional, track-specific test cases (which are not found in the canonical data), one option is to creating an `additional-test-cases.json` file, which the Test Generator can then merge with the `canonical-data.json` file before passing it to the template for rendering. + +### Templates + +The template engine to use will likely be track-specific. +Ideally, you'll want your templates to be as straightforward as possible, so don't worry about code duplication and such. + +The templates themselves will get their data from the Test Generator on which they iterate over to render them. + +```exercism/note +To help keep the templates simple, it might be useful to do a little pre-processing on the Test Generator side or else define some "filters" or whatever extension mechanism your templates allow for. +``` + +### Using configlet + +`configlet` is the primary track maintenance tool and can be used to: + +- Create the exercise files for a new exercise: run `bin/configlet create --practice-exercise ` +- Sync the `tests.toml` file of an existing exercise: run `bin/configlet sync --tests --update --exercise ` +- Fetch the exercise's canonical data to disk (this is a side-effect of either of the above commands) + +This makes `configlet` a great tool to use in combination with the Test Generator for some really powerful workflows. + +### Command-line interface + +You'll want to make using the Test Generator both easy _and_ powerful. +For that, we recommend creating one or more script files. + +```exercism/note +You're free to choose whatever script file format fits your track best. +Shell scripts and PowerShell scripts are common options that can both work well. +``` + +Here is an example of a shell script that combines `configlet` and a Test Generator to quickly scaffold a new exercise: + +```shell +bin/fetch-configlet +bin/configlet create --practice-exercise +path/to/test-generator +``` + +## Building from scratch + +Before you start building a Test Generator, we suggest you look at a couple of existing Test Generators to get a feel for how other tracks have implemented them: + +- [C#](https://github.com/exercism/csharp/blob/main/docs/GENERATORS.md) +- [Clojure](https://github.com/exercism/clojure/blob/main/generator.clj) +- [Common Lisp](https://github.com/exercism/common-lisp/blob/main/bin/lisp_exercise_generator.py) +- [Crystal](https://github.com/exercism/crystal/tree/main/test-generator) +- [Emacs Lisp](https://github.com/exercism/emacs-lisp/blob/main/tools/practice-exercise-generator.el) +- [F#](https://github.com/exercism/fsharp/blob/main/docs/GENERATORS.md) +- [Perl 5](https://github.com/exercism/perl5/tree/main/t/generator) +- [Pharo Smalltalk](https://github.com/exercism/pharo-smalltalk/blob/main/dev/src/ExercismDev/ExercismGenerator.class.st) +- [Python](https://github.com/exercism/python/blob/main/docs/GENERATOR.md) +- [Rust](https://github.com/exercism/rust/blob/main/docs/CONTRIBUTING.md#creating-a-new-exercise) +- [Swift](https://github.com/exercism/swift/tree/main/generator) + +If you have any questions, the [forum](https://forum.exercism.org/c/exercism/building-exercism/125) is the best place to ask them. +The forum discussions [around the Rust](https://forum.exercism.org/t/advice-for-writing-a-test-generator/7178) and the [JavaScript](https://forum.exercism.org/t/test-generators-for-tracks/10615) test generators might be helpful too. + +### Minimum Viable Product + +We recommend incrementally building the Test Generator, starting with a Minimal Viable Product. +A bare minimum version would read an exercise's `canonical-data.json` and just pass that data to the template. -- Read the `canonical-data.json` of the exercise from `configlet` cache or retrieve it from GitHub directly -- If the tracks testing framework supports no nested test case groups, flatten the nested data structure into a list of test cases -- Render the test cases into the exercise specific template(s) located in an exercise's `.meta/` folder - - Render production-ready code that matches the manually designed exercise - - Skip over "reimplemented" test cases (those referred to in a `reimplements` key of another test case) - - Render only test cases selected by `tests.toml` (or another track-specific data source) +Start by focusing on a single exercise, preferrably a simple one like `leap`. +Only when you have that working should you gradually add more exercises. -There are different strategies for respecting test case changes like "replace always", "replace when forced to", "use `tests.toml` to ignore replaced test cases" (works like a baseline for known test issues). -None of them is perfect. +And try to keep the Test Generator as simple as it can be. ```exercism/note -Don't try to have a versatile one-fits-all template! -There is way too much variation in the exercises to handle all in one template. +Ideally, a contributor could just paste/modify an existing template without having to understand how the Test Generator works internally. ``` -There are optional things a test generator might do: +## Using or contributing -- Provide a library of templates and / or extensions to the template engine -- Maintain or respect another track-specific data source than `tests.toml` -- Maintain student code file(s) or additional files required by the track -- Handle `scenarios` for grouping / test case selection -- Have a check functionality (e.g. to run after `configlet sync`) to detect when updating is required +How to use or contribute to a Test Generator is track-specific. +Look for instructions in the track's `README.md`, `CONTRIBUTING.md` or the Test Generator code's directory. diff --git a/building/tracks/new/implement-tooling.md b/building/tracks/new/implement-tooling.md index 8977b06c..bab2f645 100644 --- a/building/tracks/new/implement-tooling.md +++ b/building/tracks/new/implement-tooling.md @@ -52,7 +52,7 @@ Track tooling is usually (mostly) written in the track's language. ```exercism/caution While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. -We recommend using the track's language where possible, only using additional languages when it cannot be avoided. +We recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` ## Deployment From 79736ae34f5f166ab699b6d7df24f9fdfe94a56b Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 18 Apr 2024 08:12:37 +0200 Subject: [PATCH 073/129] Tweaks to test generators (#498) --- building/tooling/README.md | 4 ++-- building/tooling/test-generators.md | 24 ++++++++++++------------ building/tracks/new/implement-tooling.md | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/building/tooling/README.md b/building/tooling/README.md index 245a626d..81f1cbfb 100644 --- a/building/tooling/README.md +++ b/building/tooling/README.md @@ -45,6 +45,6 @@ Here are some examples of maintenance tooling: Track tooling is usually (mostly) written in the track's language. ```exercism/caution -While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. -We recommend using the track's language where possible, because it makes maintaining or contributing easier. +While you're free to use other languages, each additional language will make it harder to maintain or contribute to the track. +Therefore, we recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` diff --git a/building/tooling/test-generators.md b/building/tooling/test-generators.md index b66c289c..14d8d43a 100644 --- a/building/tooling/test-generators.md +++ b/building/tooling/test-generators.md @@ -32,8 +32,8 @@ We recommend doing this periodically, to check if there are problematic test cas There are two possible starting points when implementing a Test Generator for an exercise: -1. The exercise is new and doesn't yet have any tests -2. The exercise already exists and has existing tests +1. The exercise is new and thus doesn't have any tests +2. The exercise already exists and thus has existing tests ```exercism/caution If there are existing tests, implement the Test Generator such that the tests it generates do not break existing solutions. @@ -51,7 +51,7 @@ We've found that the code-based approach will lead to fairly complex Test Genera What we recommend is the following flow: 1. Read the exercise's canonical data -2. Exclude the test cases that are marked as `include = false` in the exercise's `tests.toml` file +2. Exclude the test cases that are marked as `include = false` in the exercise's [`tests.toml` file](/docs/building/tracks/practice-exercises#file-meta-tests-toml) 3. Convert the exercise's canonical data into a format that can be used in a template 4. Pass the exercise's canonical data to an exercise-specific template @@ -62,10 +62,10 @@ The key benefit of this setup is that each exercise has its own template, which: - Makes it safe to edit them without risking breaking another exercise ```exercism/caution -Some additional things to be aware of when designing the test generator +When designing the test generator, try to: -- Minimize the pre-processing of canonical data inside the Test Generator -- Try to reduce coupling between templates +- Minimize pre-processing of canonical data inside the Test Generator +- Reduce coupling between templates ``` ## Implementation @@ -73,17 +73,17 @@ Some additional things to be aware of when designing the test generator The Test Generator is usually (mostly) written in the track's language. ```exercism/caution -While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. -We recommend using the track's language where possible, only using additional languages when it makes maintaining or contributing easier. +While you're free to use other languages, each additional language will make it harder to maintain or contribute to the track. +Therefore, we recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` ### Canonical data The core data the Test Generator works with is an exercise's [`canonical-data.json` file](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson). -This file is defined in the [exercism/problem-specifications repo](https://github.com/exercism/problem-specifications), which defines shared metadata for many Exercism's exercises. +This file is defined in the [exercism/problem-specifications repo](https://github.com/exercism/problem-specifications), which defines shared metadata for [many Exercism's exercises](https://github.com/search?q=repo%3Aexercism%2Fproblem-specifications+path%3A**%2Fcanonical-data.json&type=code&ref=advsearch). -```exercism/note -Not all exercises have a `canonical-data.json` file. +```exercism/caution +Not all exercises have a `canonical-data.json` file! In case they don't, you'll need to manually create the tests, as there is no data for the Test Generator to work with. ``` @@ -201,7 +201,7 @@ If your track does not support grouping tests, you'll need to: - Traverse/flatten the `cases` hierarchy to end up with only the innermost (leaf) test cases - Combine the test case description with its parent description(s) to create a unique test name -```` +``` #### Input and expected values diff --git a/building/tracks/new/implement-tooling.md b/building/tracks/new/implement-tooling.md index bab2f645..7febdb6f 100644 --- a/building/tracks/new/implement-tooling.md +++ b/building/tracks/new/implement-tooling.md @@ -51,8 +51,8 @@ To speed up adding new exercises, consider building a [Test Generator](/docs/bui Track tooling is usually (mostly) written in the track's language. ```exercism/caution -While you're free to use additional languages, each additional language will make it harder to find people that can maintain or contribute to the track. -We recommend using the track's language where possible, because it makes maintaining or contributing easier. +While you're free to use other languages, each additional language will make it harder to maintain or contribute to the track. +Therefore, we recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` ## Deployment From 21457b3de0cd898d00d93985871332875c6ecb5f Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 1 May 2024 12:57:27 +0200 Subject: [PATCH 074/129] Add `paradigm/array` paradigm (#499) --- building/tooling/analyzers/tags.md | 2 ++ building/tracks/config-json.md | 1 + 2 files changed, 3 insertions(+) diff --git a/building/tooling/analyzers/tags.md b/building/tooling/analyzers/tags.md index 75757ec2..d75543bc 100644 --- a/building/tooling/analyzers/tags.md +++ b/building/tooling/analyzers/tags.md @@ -37,6 +37,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra | Tag | Description | | -------------------------- | ---------------------------------------------------------------- | +| `paradigm:array` | uses [array programming][array-programming]. | | `paradigm:declarative` | uses [declarative programming][declarative-programming]. | | `paradigm:functional` | uses [functional programming][functional-programming]. | | `paradigm:generic` | uses [generic programming][generic-programming]. | @@ -328,6 +329,7 @@ Using a common set of tags will allow us to do some nifty things, like cross-tra As this category is language-specific, there are no commonly used tags here. +[array-programming]: https://en.wikipedia.org/wiki/Array_programming [declarative-programming]: https://en.wikipedia.org/wiki/Declarative_programming [logic-programming]: https://en.wikipedia.org/wiki/Logic_programming [object-oriented-programming]: https://en.wikipedia.org/wiki/Object-oriented_programming diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index 83d2343b..ea0f6fdb 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -374,6 +374,7 @@ Tags are specified in the top-level `tags` field which is defined as an array of ### Paradigms +- `paradigm/array`: the language is an array programming language - `paradigm/declarative`: the language supports a declarative style of programming - `paradigm/functional`: the language supports a function style of programming - `paradigm/imperative`: the language supports an imperative style of programming From 10e57200961c71ff0256a6b202da098b3c9d0a61 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 6 May 2024 21:28:38 +0100 Subject: [PATCH 075/129] Fix broken link (#500) --- building/tracks/syllabus/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/syllabus/README.md b/building/tracks/syllabus/README.md index e0a4684d..7a264d5f 100644 --- a/building/tracks/syllabus/README.md +++ b/building/tracks/syllabus/README.md @@ -135,7 +135,7 @@ A concept exercise always has a story. If you're forking an exercise from another track, then the exercise will already have a story. In that case, you're all set. -To see if there are any existing stories you can use or exercises you can fork, check out the [list of stories](docs/building/tracks/stories). +To see if there are any existing stories you can use or exercises you can fork, check out the [list of stories](/docs/building/tracks/stories). If you have a concept but no story then our recommendation is to write a small, simple code example that uses the concept that you're introducing. Then reverse engineer a story onto the code. From dce6f5b53125bfd12301b2f07d9ba07cf9c9d4b5 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 7 Jun 2024 12:12:44 +0200 Subject: [PATCH 076/129] Document how to check and add language to lines of code counter (#502) --- building/tooling/lines-of-code-counter.md | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/building/tooling/lines-of-code-counter.md b/building/tooling/lines-of-code-counter.md index f1bb4bf8..460db501 100644 --- a/building/tooling/lines-of-code-counter.md +++ b/building/tooling/lines-of-code-counter.md @@ -8,5 +8,33 @@ If the student submitted a test file, those will be ignored. While this works well for most submissions, some tracks might need to ignore additional files. For that, we'll allow the Lines of Code Counter to be [customized per track][lines-of-code-counter-customize]. +## Implementation + +We're using the [tokei][tokei] library to do the actual counting. +As pull requests are often not merged quickly, we've created our own [fork][tokei-fork]. +This allows us to iterate more quickly and add support for any language we want. + +## Supported languages + +You can check to see if your language is supported out of the box by looking it up in the [languages.json][languages.json] file. + +### Adding new language + +If your language is **not** supported, you can add support by: + +1. Adding an entry for your language to the [languages.json] file. + Check the [language addition docs][adding-language] for more information. +2. Adding tests to verify counting works correctly for your language. + Check the [adding tests docs][adding-tests] for more information. + +Once you've made these changes, open a pull request to the [exercism/tokei repository][tokei-fork]. +Here is an [example pull request][example-pr] that adds support for the Arturo language. + [lines-of-code-counter]: https://github.com/exercism/lines-of-code-counter/ [lines-of-code-counter-customize]: https://github.com/exercism/lines-of-code-counter/#ignore-additional-files +[languages.json]: https://github.com/exercism/tokei/blob/master/languages.json +[tokei]: https://github.com/XAMPPRocky/tokei +[tokei-fork]: https://github.com/exercism/tokei +[example-pr]: https://github.com/exercism/tokei/pull/14/files +[adding-language]: https://github.com/exercism/tokei/blob/master/CONTRIBUTING.md#language-addition +[adding-tests]: https://github.com/exercism/tokei/blob/master/CONTRIBUTING.md#tests From c7abdb72e342a066ea47e1d4a709fecb65f49c2a Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 7 Jun 2024 14:05:35 +0200 Subject: [PATCH 077/129] Document customizing snippet extraction (#503) --- building/tooling/snippet-extractor.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/building/tooling/snippet-extractor.md b/building/tooling/snippet-extractor.md index f6ea2036..1365060c 100644 --- a/building/tooling/snippet-extractor.md +++ b/building/tooling/snippet-extractor.md @@ -3,9 +3,27 @@ The [Snippet Extractor][snippet-extractor] takes a student's submission and extracts the first ten "interesting" lines of code from it. The extracted snippet is shown on various pages on the website. -By default, the first ten lines are shown, ignore leading empty lines. +By default, the first ten lines are shown, ignoring leading empty lines. These first ten lines _could_ include things like comments, which are usually not ideal to be presented in a snippet. -Each track can [customize the snippet extraction][snippet-extractor-customize], for example to remove said comments. + +## Customizing snippet extraction + +We recommend each track [customizes snippet extraction][snippet-extractor-customize] to at least remove comments from the source code. +To customize snippet extraction, you'll need to: + +1. Add a `lib/languages/.txt` file which configures snippet extraction. + This file can use either [basic mode][basic-mode] or [extended mode][extended-mode]. + Most tracks use extended mode, as it gives more flexibility. +2. Add one or more test cases to a `tests//` directory. + Each test directory will need two files: + + 1. `tests///code.`: the code to run the snippet extractor on + 2. `tests///expected_snippet.`: the expected snippet + +See [this example pull request][example-pr] that customizes snippet extraction for the Arturo language. [snippet-extractor]: https://github.com/exercism/snippet-extractor/ -[snippet-extractor-customize]: https://github.com/exercism/snippet-extractor/#add-your-language +[snippet-extractor-customize]: https://github.com/exercism/snippet-extractor/?tab=readme-ov-file#customizing-snippet-extraction +[example-pr]: https://github.com/exercism/snippet-extractor/pull/94 +[basic-mode]: https://github.com/exercism/snippet-extractor/blob/main/docs/basic.md +[extended-mode]: https://github.com/exercism/snippet-extractor/blob/main/docs/extended.md From 5c5f2cb8b33b050501d167fc1950d52e2e2be73d Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 7 Jun 2024 14:05:58 +0200 Subject: [PATCH 078/129] Add CLI tooling docs (#504) --- building/config.json | 7 +++++++ building/tooling/cli.md | 22 ++++++++++++++++++++++ building/tracks/new/configure-tooling.md | 3 ++- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 building/tooling/cli.md diff --git a/building/config.json b/building/config.json index 32741015..07f2db76 100644 --- a/building/config.json +++ b/building/config.json @@ -820,6 +820,13 @@ "title": "Snippet Extractor", "blurb": "" }, + { + "uuid": "0d24b14c-8995-4ca5-8e5f-6ca2dcc5318c", + "slug": "tooling/cli", + "path": "building/tooling/cli.md", + "title": "Exercism CLI", + "blurb": "" + }, { "uuid": "ba393f61-0f7d-4178-922a-c16cda052338", "slug": "tooling/test-generators", diff --git a/building/tooling/cli.md b/building/tooling/cli.md new file mode 100644 index 00000000..83d79b13 --- /dev/null +++ b/building/tooling/cli.md @@ -0,0 +1,22 @@ +# Exercism CLI + +The [Exercism CLI][cli] lets students download exercises and submit solutions to the site. +It also supports the `exercism test` command, which then runs the track-specific command to run the tests. + +## Adding new language + +The track-specific test commands are defined in a [configuration file][test-configurations]. +You can add support for your language by adding an entry to that [configuration file][test-configurations], where the key is the track's slug. + +### Test command placeholders + +There are two placeholders that can be used in the track-specific command: + +- `{{test_files}}`: a space-separated list of the test files (as found in the `.files.test` key in the exercise's `.meta/config.json` file) +- `{{solution_files}}`: a space-separated list of the solution files (as found in the `.files.solution` key in the exercise's `.meta/config.json` file) + +Here is an [example pull request][example-pr] that adds support for the Arturo language. + +[cli]: https://github.com/exercism/cli +[example-pr]: https://github.com/exercism/cli/pull/1147/files +[test-configurations]: https://github.com/exercism/cli/blob/main/workspace/test_configurations.go#L63 diff --git a/building/tracks/new/configure-tooling.md b/building/tracks/new/configure-tooling.md index 1f43e501..0def4268 100644 --- a/building/tracks/new/configure-tooling.md +++ b/building/tracks/new/configure-tooling.md @@ -1,8 +1,9 @@ # Configure tooling -There are two bits of tooling that you can optionally tweak for your track: +There are three bits of tooling that you can optionally tweak for your track: - The **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)** - The **[Snippet Extractor](/docs/building/tooling/snippet-extractor)** +- The **[Exercism CLI](/docs/building/tooling/cli)** While tweaking these tools is optional, doing so can make your track's integration into the website just _that_ bit better. From 78226aeaa5b854bc54380f1a89eb2fc94576e830 Mon Sep 17 00:00:00 2001 From: "Why are you reading my profile? This will clearly give you a stroke. It was mostly AI-generated, but this was not ai generated, but my username was, and also, you feel like you might enjoy some brain damage" <120068827+ConeDragon@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:22:35 -0400 Subject: [PATCH 079/129] correct spelling (#505) --- mentoring/mindset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mentoring/mindset.md b/mentoring/mindset.md index bcc360f0..286be440 100644 --- a/mentoring/mindset.md +++ b/mentoring/mindset.md @@ -115,7 +115,7 @@ A mentor may not be sure of their obligations. The obligations are few and simple: to be helpful and encouraging. If at any point the mentor feels they can't be either of those, then it may be best to politely suggest that the session end so the mentee can resubmit for another mentor. -It may be best for the mentor to invite the menteee to end the discussion, but if the mentor feels the need to end the discussion, +It may be best for the mentor to invite the mentee to end the discussion, but if the mentor feels the need to end the discussion, it would be polite to do so with a final comment explaining why. Although it may be tempting to focus on the faults of the mentee that made the session unsatisfactory, it would be more in the spirit of Exercism for the mentor to not focus blame on the mentee. From 9e35a497a29d316874a4db797f1c55d6db7235b4 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 24 Jul 2024 16:05:36 +0200 Subject: [PATCH 080/129] Separate document for test runner building (#519) --- building/config.json | 38 ++++++++++++++---------- building/tracks/new/README.md | 7 +++-- building/tracks/new/build-test-runner.md | 29 ++++++++++++++++++ building/tracks/new/configure-tooling.md | 13 ++++---- building/tracks/new/implement-tooling.md | 13 +++----- 5 files changed, 67 insertions(+), 33 deletions(-) create mode 100644 building/tracks/new/build-test-runner.md diff --git a/building/config.json b/building/config.json index 07f2db76..866ade12 100644 --- a/building/config.json +++ b/building/config.json @@ -632,6 +632,12 @@ "path": "building/tracks/new/add-first-exercise.md", "title": "Add the first exercise" }, + { + "uuid": "975a3a67-5d5e-4022-b363-a40661095c93", + "slug": "tracks/new/setup-continuous-integration", + "path": "building/tracks/new/setup-continuous-integration.md", + "title": "Setup Continuous Integration" + }, { "uuid": "c8f1ff4d-55f8-447e-b5e7-4a87f1e341f7", "slug": "tracks/new/add-initial-exercises", @@ -639,10 +645,16 @@ "title": "Add initial exercises" }, { - "uuid": "975a3a67-5d5e-4022-b363-a40661095c93", - "slug": "tracks/new/setup-continuous-integration", - "path": "building/tracks/new/setup-continuous-integration.md", - "title": "Setup Continuous Integration" + "uuid": "ae9c8c65-2c39-48d5-b647-4189cff77862", + "slug": "tracks/new/build-test-runner", + "path": "building/tracks/new/build-test-runner.md", + "title": "Build test runner" + }, + { + "uuid": "0113174d-314d-4255-a300-32150dc32179", + "slug": "tracks/new/configure-tooling", + "path": "building/tracks/new/configure-tooling.md", + "title": "Configure tooling" }, { "uuid": "3bc94082-8fa3-4129-bca7-c1892cba3ed4", @@ -662,24 +674,18 @@ "path": "building/tracks/new/launch.md", "title": "Launch!" }, + { + "uuid": "f697a7c1-ad33-460c-8458-18cd6d149920", + "slug": "tracks/new/implement-tooling", + "path": "building/tracks/new/implement-tooling.md", + "title": "Implement additional tooling" + }, { "uuid": "65fe2eb3-bd67-4312-9e9d-d7334cdcbdcf", "slug": "tracks/new/prepare-for-contributions", "path": "building/tracks/new/prepare-for-contributions.md", "title": "Prepare for open source contributions from strangers" }, - { - "uuid": "0113174d-314d-4255-a300-32150dc32179", - "slug": "tracks/new/configure-tooling", - "path": "building/tracks/new/configure-tooling.md", - "title": "Configure tooling" - }, - { - "uuid": "f697a7c1-ad33-460c-8458-18cd6d149920", - "slug": "tracks/new/implement-tooling", - "path": "building/tracks/new/implement-tooling.md", - "title": "Implement tooling" - }, { "uuid": "11894561-be89-471f-ac4e-581449add2bd", "slug": "tracks", diff --git a/building/tracks/new/README.md b/building/tracks/new/README.md index 831000af..6a92b590 100644 --- a/building/tracks/new/README.md +++ b/building/tracks/new/README.md @@ -10,11 +10,12 @@ Once you've completed that step, the next steps are: - [Select programming language variant](/docs/building/tracks/new/select-programming-language-variant) (if applicable) - [Select testing framework](/docs/building/tracks/new/select-testing-framework) - [Add the first exercise](/docs/building/tracks/new/add-first-exercise) -- [Add initial exercises](/docs/building/tracks/new/add-initial-exercises) - [Setup Continuous Integration](/docs/building/tracks/new/setup-continuous-integration) +- [Add initial exercises](/docs/building/tracks/new/add-initial-exercises) +- [Build test runner](/docs/building/tracks/new/build-test-runner) +- [Configure tooling](/docs/building/tracks/new/configure-tooling) - [Prepare for launch](/docs/building/tracks/new/prepare-for-launch) - [Find Maintainers](/docs/building/tracks/new/find-maintainers) - [Launch!](/docs/building/tracks/new/launch) -- [Configure tooling](/docs/building/tracks/new/configure-tooling) -- [Implement tooling](/docs/building/tracks/new/implement-tooling) +- [Implement additional tooling](/docs/building/tracks/new/implement-tooling) - [Prepare for open source contributions from strangers](/docs/building/tracks/new/prepare-for-contributions) diff --git a/building/tracks/new/build-test-runner.md b/building/tracks/new/build-test-runner.md new file mode 100644 index 00000000..94907bbf --- /dev/null +++ b/building/tracks/new/build-test-runner.md @@ -0,0 +1,29 @@ +# Build test runner + +The test runner is an essential bit of tooling that allows: + +- The website to automatically verify if an iteration passes all the tests. +- Students to solve exercises using the [in-browser editor](/docs/using/solving-exercises/using-the-online-editor) ([no CLI needed](/docs/using/solving-exercises/working-locally)). + +To get started building a Test Runner, check the [Creating a Test Runner from scratch](/docs/building/tooling/test-runners/creating-from-scratch) document. + +## Implementation + +Track tooling is usually (mostly) written in the track's language. + +```exercism/caution +While you're free to use other languages, each additional language will make it harder to maintain or contribute to the track. +Therefore, we recommend using the track's language where possible, because it makes maintaining or contributing easier. +``` + +## Deployment + +The test runner is packaged and run as a [Docker container](/docs/building/tooling/docker). +Test runner Docker images are deployed automatically using a [GitHub Actions workflow](https://github.com/exercism/generic-test-runner/blob/main/.github/workflows/docker.yml). + +## Testing + +Once a test runner has been built, you could use its Docker image in your track's CI setup to verify the track's exercises. +The main benefit of this approach is that it better mimics the production setup; in other words, you can be more confident that things also work in production. +A possible downside is that it might slow down the CI workflow. +It is up to you, the maintainer, to decide which approach works best for your track. diff --git a/building/tracks/new/configure-tooling.md b/building/tracks/new/configure-tooling.md index 0def4268..873426d7 100644 --- a/building/tracks/new/configure-tooling.md +++ b/building/tracks/new/configure-tooling.md @@ -1,9 +1,12 @@ # Configure tooling -There are three bits of tooling that you can optionally tweak for your track: +There are three bits of tooling that you should tweak for your track: -- The **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)** -- The **[Snippet Extractor](/docs/building/tooling/snippet-extractor)** -- The **[Exercism CLI](/docs/building/tooling/cli)** +- The **[Lines of Code Counter](/docs/building/tooling/lines-of-code-counter)**: this tool is tasked with counting the lines of code for each submitted iteration. +- The **[Snippet Extractor](/docs/building/tooling/snippet-extractor)**: this tool extracts a snippet for each iteration, which is shown on the website. +- The **[Exercism CLI](/docs/building/tooling/cli)**: this tool is used by students to work on a track locally, on their own machine. -While tweaking these tools is optional, doing so can make your track's integration into the website just _that_ bit better. +```exercism/note +Even though tweaking these tools is optional, it'll make your track's integration into the website _much_ better. +Therefore, we highly recommend you tweak all three tools. +``` diff --git a/building/tracks/new/implement-tooling.md b/building/tracks/new/implement-tooling.md index 7febdb6f..e3404230 100644 --- a/building/tracks/new/implement-tooling.md +++ b/building/tracks/new/implement-tooling.md @@ -1,6 +1,6 @@ # Implement tooling -After launching the track with the first 20+ exercises, the focus should shift to implementing the [track tooling](/docs/building/tooling). +After launching the track with the first 20+ exercises and a test runner, the focus should shift to implementing [additional track tooling](/docs/building/tooling). There are two types of track tooling: @@ -24,14 +24,9 @@ To help with track maintenance, one can also build: ## Which tool to implement? The production tools are more important than maintenance tools. -Of the three production tools, the Test Runner should be implemented first as it enables: +At this point, you should have implemented a Test Runner (if not, check the [Build a test runner docs](/docs/building/tracks/new/build-test-runner)). -- Students to solve exercises using the [in-browser editor](/docs/using/solving-exercises/using-the-online-editor) ([no CLI needed](/docs/using/solving-exercises/working-locally)). -- The website to automatically verify if an iteration passes all the tests. - -To get started building a Test Runner, check the [Creating a Test Runner from scratch](/docs/building/tooling/test-runners/creating-from-scratch) document. - -Once a test runner has been built, the next tool to work on is the Representer. +Having built a test runner, the next tool to work on is the Representer. There is some overlap between the goals of the Representer and the Analyzer, but we recommend building the Representer first for the following reasons: - Representers are usually (far) easier to implement @@ -58,6 +53,6 @@ Therefore, we recommend using the track's language where possible, because it ma ## Deployment Production tools are packaged and run as a [Docker container](/docs/building/tooling/docker). -Tooling images are deployed automatically using a [Docker workflow](https://github.com/exercism/generic-test-runner/blob/main/.github/workflows/docker.yml). +Tooling Docker images are deployed automatically using a [GitHub Actions workflow](https://github.com/exercism/generic-test-runner/blob/main/.github/workflows/docker.yml). Maintenance tools are _not_ deployed. From 905e52347639b87fe2256f59556125f4cf881ce3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 24 Jul 2024 16:13:21 +0200 Subject: [PATCH 081/129] Tweak order of building/track menu (#534) --- building/config.json | 439 +++++++++++++++++++++---------------------- 1 file changed, 216 insertions(+), 223 deletions(-) diff --git a/building/config.json b/building/config.json index 866ade12..f3828366 100644 --- a/building/config.json +++ b/building/config.json @@ -79,6 +79,222 @@ "path": "building/tracks/README.md", "title": "Building Tracks" }, + { + "uuid": "2a29f722-e70d-4679-81f8-bd6727fa9013", + "slug": "tracks/config-json", + "path": "building/tracks/config-json.md", + "title": "config.json", + "blurb": "" + }, + { + "uuid": "a4ec5951-c4ee-4f36-bf97-648075e5d514", + "slug": "tracks/docs", + "path": "building/tracks/docs.md", + "title": "Track Docs", + "blurb": "" + }, + { + "uuid": "d951a049-a5ca-4b38-ae06-68b10cfbb2d9", + "slug": "tracks/practice-exercises", + "path": "building/tracks/practice-exercises.md", + "title": "Practice Exercises", + "blurb": "" + }, + { + "uuid": "34387a23-f65c-490e-96ed-6b5e25298db7", + "slug": "tracks/concept-exercises", + "path": "building/tracks/concept-exercises.md", + "title": "Concept Exercises", + "blurb": "" + }, + { + "uuid": "038e26ee-891f-4592-92bd-c54cbd58d8bd", + "slug": "tracks/syllabus", + "path": "building/tracks/syllabus/README.md", + "title": "Syllabus", + "blurb": "" + }, + { + "uuid": "af70ff3e-d4a4-4968-bcfe-1f197dc43380", + "slug": "tracks/syllabus/first-exercise", + "path": "building/tracks/syllabus/first-exercise.md", + "title": "First Exercise", + "blurb": "" + }, + { + "uuid": "6f8d7376-70db-4f25-928c-4778bdf8a686", + "slug": "tracks/syllabus/next-exercises", + "path": "building/tracks/syllabus/next-exercises.md", + "title": "Next Exercises", + "blurb": "" + }, + { + "uuid": "cb430e09-7710-43b8-ab0e-2f0ee3785cf9", + "slug": "tracks/syllabus/expanding", + "path": "building/tracks/syllabus/expanding.md", + "title": "Expanding", + "blurb": "" + }, + { + "uuid": "ed318b17-01be-4868-92df-50a644adfdc7", + "slug": "tracks/concepts", + "path": "building/tracks/concepts.md", + "title": "Concepts", + "blurb": "" + }, + { + "uuid": "0807db01-ce0e-4ac4-817b-7fe0a2366d01", + "slug": "tracks/concept-map", + "path": "building/tracks/concept-map.md", + "title": "Concept Map", + "blurb": "" + }, + { + "uuid": "e5b94b50-cff3-4fcb-8fbf-4a247202da97", + "slug": "tracks/approaches", + "path": "building/tracks/approaches.md", + "title": "Approaches", + "blurb": "Learn how to write approaches for exercises" + }, + { + "uuid": "362853a0-dc4a-43ee-abbb-d4a14cb5b5bb", + "slug": "tracks/articles", + "path": "building/tracks/articles.md", + "title": "Articles", + "blurb": "Learn how to write articles for exercises" + }, + { + "uuid": "e48b8809-2211-4b52-b984-9b6555103e41", + "slug": "tracks/new", + "path": "building/tracks/new/README.md", + "title": "New Track", + "blurb": "Learn how to build a new Track from scratch" + }, + { + "uuid": "251d76bb-945a-4754-bf84-30a2fbf55654", + "slug": "tracks/new/request-new", + "path": "building/tracks/new/request-new.md", + "title": "Request a new Track" + }, + { + "uuid": "def02ef0-a5ba-44b0-832c-7616d931da0c", + "slug": "tracks/new/join-our-community", + "path": "building/tracks/new/join-our-community.md", + "title": "Join our community" + }, + { + "uuid": "83c73090-7641-4fa9-9038-9198778cf1a4", + "slug": "tracks/new/select-programming-language-variant", + "path": "building/tracks/new/select-programming-language-variant.md", + "title": "Select programming language variant" + }, + { + "uuid": "879fb183-ba23-4956-a9d4-e4abea00d630", + "slug": "tracks/new/select-testing-framework", + "path": "building/tracks/new/select-testing-framework.md", + "title": "Select testing framework" + }, + { + "uuid": "712ef100-f288-40d0-8911-11eb60b0b033", + "slug": "tracks/new/add-first-exercise", + "path": "building/tracks/new/add-first-exercise.md", + "title": "Add the first exercise" + }, + { + "uuid": "975a3a67-5d5e-4022-b363-a40661095c93", + "slug": "tracks/new/setup-continuous-integration", + "path": "building/tracks/new/setup-continuous-integration.md", + "title": "Setup Continuous Integration" + }, + { + "uuid": "c8f1ff4d-55f8-447e-b5e7-4a87f1e341f7", + "slug": "tracks/new/add-initial-exercises", + "path": "building/tracks/new/add-initial-exercises.md", + "title": "Add initial exercises" + }, + { + "uuid": "ae9c8c65-2c39-48d5-b647-4189cff77862", + "slug": "tracks/new/build-test-runner", + "path": "building/tracks/new/build-test-runner.md", + "title": "Build test runner" + }, + { + "uuid": "0113174d-314d-4255-a300-32150dc32179", + "slug": "tracks/new/configure-tooling", + "path": "building/tracks/new/configure-tooling.md", + "title": "Configure tooling" + }, + { + "uuid": "3bc94082-8fa3-4129-bca7-c1892cba3ed4", + "slug": "tracks/new/prepare-for-launch", + "path": "building/tracks/new/prepare-for-launch.md", + "title": "Prepare for launch" + }, + { + "uuid": "296dbe06-3384-409b-b2de-be52593c76d3", + "slug": "tracks/new/find-maintainers", + "path": "building/tracks/new/find-maintainers.md", + "title": "Find maintainers" + }, + { + "uuid": "d9f356fd-4fb2-4ec7-be1b-ec65bfda4904", + "slug": "tracks/new/launch", + "path": "building/tracks/new/launch.md", + "title": "Launch!" + }, + { + "uuid": "f697a7c1-ad33-460c-8458-18cd6d149920", + "slug": "tracks/new/implement-tooling", + "path": "building/tracks/new/implement-tooling.md", + "title": "Implement additional tooling" + }, + { + "uuid": "65fe2eb3-bd67-4312-9e9d-d7334cdcbdcf", + "slug": "tracks/new/prepare-for-contributions", + "path": "building/tracks/new/prepare-for-contributions.md", + "title": "Prepare for open source contributions from strangers" + }, + { + "uuid": "8db530bb-e11b-4497-b088-5b4c997e09a2", + "slug": "tracks/presentation", + "path": "building/tracks/presentation.md", + "title": "Presentation", + "blurb": "" + }, + { + "uuid": "0b0996c5-459c-4145-8876-09d7664d7b5c", + "slug": "tracks/shared-files", + "path": "building/tracks/shared-files.md", + "title": "Shared files", + "blurb": "" + }, + { + "uuid": "2eadb0af-4b2f-4598-a5b2-6b9cdf4a5c1b", + "slug": "tracks/ci", + "path": "building/tracks/ci/README.md", + "title": "Continuous Integration", + "blurb": "Learn about how Exercism handles Continuous Integration through GitHub Actions" + }, + { + "uuid": "bbb766b6-4b28-4a19-bf57-48495b465c86", + "slug": "tracks/ci/workflow-templates", + "path": "building/tracks/ci/workflow-templates.md", + "title": "Workflow Templates" + }, + { + "uuid": "4883e54d-b863-44b2-a2d9-5e77ef6148a1", + "slug": "tracks/deprecated-exercises", + "path": "building/tracks/deprecated-exercises.md", + "title": "Deprecated Exercises", + "blurb": "Learn why and how to deprecate an Exercism exercise" + }, + { + "uuid": "16e47fd6-2faf-4c2b-bc69-285a1fcb9fce", + "slug": "tracks/icons", + "path": "building/tracks/icons.md", + "title": "Icons", + "blurb": "" + }, { "uuid": "3945aaa4-60c4-4ae5-ae7c-aefc1718ce5c", "slug": "tracks/stories", @@ -421,19 +637,6 @@ "path": "building/tracks/stories/tuples.santas-helper.md", "title": "Santa's Helper" }, - { - "uuid": "2eadb0af-4b2f-4598-a5b2-6b9cdf4a5c1b", - "slug": "tracks/ci", - "path": "building/tracks/ci/README.md", - "title": "Continuous Integration", - "blurb": "Learn about how Exercism handles Continuous Integration through GitHub Actions" - }, - { - "uuid": "bbb766b6-4b28-4a19-bf57-48495b465c86", - "slug": "tracks/ci/workflow-templates", - "path": "building/tracks/ci/workflow-templates.md", - "title": "Workflow Templates" - }, { "uuid": "191b0fa1-96e2-48a6-ad2e-c34f57443799", "slug": "tracks/ci/migrating-from-travis", @@ -490,216 +693,6 @@ "title": "Tooling Docker Setup", "blurb": "" }, - { - "uuid": "34387a23-f65c-490e-96ed-6b5e25298db7", - "slug": "tracks/concept-exercises", - "path": "building/tracks/concept-exercises.md", - "title": "Concept Exercises", - "blurb": "" - }, - { - "uuid": "038e26ee-891f-4592-92bd-c54cbd58d8bd", - "slug": "tracks/syllabus", - "path": "building/tracks/syllabus/README.md", - "title": "Syllabus", - "blurb": "" - }, - { - "uuid": "af70ff3e-d4a4-4968-bcfe-1f197dc43380", - "slug": "tracks/syllabus/first-exercise", - "path": "building/tracks/syllabus/first-exercise.md", - "title": "First Exercise", - "blurb": "" - }, - { - "uuid": "6f8d7376-70db-4f25-928c-4778bdf8a686", - "slug": "tracks/syllabus/next-exercises", - "path": "building/tracks/syllabus/next-exercises.md", - "title": "Next Exercises", - "blurb": "" - }, - { - "uuid": "cb430e09-7710-43b8-ab0e-2f0ee3785cf9", - "slug": "tracks/syllabus/expanding", - "path": "building/tracks/syllabus/expanding.md", - "title": "Expanding", - "blurb": "" - }, - { - "uuid": "ed318b17-01be-4868-92df-50a644adfdc7", - "slug": "tracks/concepts", - "path": "building/tracks/concepts.md", - "title": "Concepts", - "blurb": "" - }, - { - "uuid": "0807db01-ce0e-4ac4-817b-7fe0a2366d01", - "slug": "tracks/concept-map", - "path": "building/tracks/concept-map.md", - "title": "Concept Map", - "blurb": "" - }, - { - "uuid": "2a29f722-e70d-4679-81f8-bd6727fa9013", - "slug": "tracks/config-json", - "path": "building/tracks/config-json.md", - "title": "config.json", - "blurb": "" - }, - { - "uuid": "a4ec5951-c4ee-4f36-bf97-648075e5d514", - "slug": "tracks/docs", - "path": "building/tracks/docs.md", - "title": "Track Docs", - "blurb": "" - }, - { - "uuid": "16e47fd6-2faf-4c2b-bc69-285a1fcb9fce", - "slug": "tracks/icons", - "path": "building/tracks/icons.md", - "title": "Icons", - "blurb": "" - }, - { - "uuid": "d951a049-a5ca-4b38-ae06-68b10cfbb2d9", - "slug": "tracks/practice-exercises", - "path": "building/tracks/practice-exercises.md", - "title": "Practice Exercises", - "blurb": "" - }, - { - "uuid": "4883e54d-b863-44b2-a2d9-5e77ef6148a1", - "slug": "tracks/deprecated-exercises", - "path": "building/tracks/deprecated-exercises.md", - "title": "Deprecated Exercises", - "blurb": "Learn why and how to deprecate an Exercism exercise" - }, - { - "uuid": "e5b94b50-cff3-4fcb-8fbf-4a247202da97", - "slug": "tracks/approaches", - "path": "building/tracks/approaches.md", - "title": "Approaches", - "blurb": "Learn how to write approaches for exercises" - }, - { - "uuid": "362853a0-dc4a-43ee-abbb-d4a14cb5b5bb", - "slug": "tracks/articles", - "path": "building/tracks/articles.md", - "title": "Articles", - "blurb": "Learn how to write articles for exercises" - }, - { - "uuid": "8db530bb-e11b-4497-b088-5b4c997e09a2", - "slug": "tracks/presentation", - "path": "building/tracks/presentation.md", - "title": "Presentation", - "blurb": "" - }, - { - "uuid": "e48b8809-2211-4b52-b984-9b6555103e41", - "slug": "tracks/new", - "path": "building/tracks/new/README.md", - "title": "New Track", - "blurb": "Learn how to build a new Track from scratch" - }, - { - "uuid": "251d76bb-945a-4754-bf84-30a2fbf55654", - "slug": "tracks/new/request-new", - "path": "building/tracks/new/request-new.md", - "title": "Request a new Track" - }, - { - "uuid": "def02ef0-a5ba-44b0-832c-7616d931da0c", - "slug": "tracks/new/join-our-community", - "path": "building/tracks/new/join-our-community.md", - "title": "Join our community" - }, - { - "uuid": "83c73090-7641-4fa9-9038-9198778cf1a4", - "slug": "tracks/new/select-programming-language-variant", - "path": "building/tracks/new/select-programming-language-variant.md", - "title": "Select programming language variant" - }, - { - "uuid": "879fb183-ba23-4956-a9d4-e4abea00d630", - "slug": "tracks/new/select-testing-framework", - "path": "building/tracks/new/select-testing-framework.md", - "title": "Select testing framework" - }, - { - "uuid": "712ef100-f288-40d0-8911-11eb60b0b033", - "slug": "tracks/new/add-first-exercise", - "path": "building/tracks/new/add-first-exercise.md", - "title": "Add the first exercise" - }, - { - "uuid": "975a3a67-5d5e-4022-b363-a40661095c93", - "slug": "tracks/new/setup-continuous-integration", - "path": "building/tracks/new/setup-continuous-integration.md", - "title": "Setup Continuous Integration" - }, - { - "uuid": "c8f1ff4d-55f8-447e-b5e7-4a87f1e341f7", - "slug": "tracks/new/add-initial-exercises", - "path": "building/tracks/new/add-initial-exercises.md", - "title": "Add initial exercises" - }, - { - "uuid": "ae9c8c65-2c39-48d5-b647-4189cff77862", - "slug": "tracks/new/build-test-runner", - "path": "building/tracks/new/build-test-runner.md", - "title": "Build test runner" - }, - { - "uuid": "0113174d-314d-4255-a300-32150dc32179", - "slug": "tracks/new/configure-tooling", - "path": "building/tracks/new/configure-tooling.md", - "title": "Configure tooling" - }, - { - "uuid": "3bc94082-8fa3-4129-bca7-c1892cba3ed4", - "slug": "tracks/new/prepare-for-launch", - "path": "building/tracks/new/prepare-for-launch.md", - "title": "Prepare for launch" - }, - { - "uuid": "296dbe06-3384-409b-b2de-be52593c76d3", - "slug": "tracks/new/find-maintainers", - "path": "building/tracks/new/find-maintainers.md", - "title": "Find maintainers" - }, - { - "uuid": "d9f356fd-4fb2-4ec7-be1b-ec65bfda4904", - "slug": "tracks/new/launch", - "path": "building/tracks/new/launch.md", - "title": "Launch!" - }, - { - "uuid": "f697a7c1-ad33-460c-8458-18cd6d149920", - "slug": "tracks/new/implement-tooling", - "path": "building/tracks/new/implement-tooling.md", - "title": "Implement additional tooling" - }, - { - "uuid": "65fe2eb3-bd67-4312-9e9d-d7334cdcbdcf", - "slug": "tracks/new/prepare-for-contributions", - "path": "building/tracks/new/prepare-for-contributions.md", - "title": "Prepare for open source contributions from strangers" - }, - { - "uuid": "11894561-be89-471f-ac4e-581449add2bd", - "slug": "tracks", - "path": "building/tracks/README.md", - "title": "Track", - "blurb": "" - }, - { - "uuid": "0b0996c5-459c-4145-8876-09d7664d7b5c", - "slug": "tracks/shared-files", - "path": "building/tracks/shared-files.md", - "title": "Shared files", - "blurb": "" - }, { "uuid": "b080e814-d3b9-4027-a25e-907f5505cf8d", "slug": "tooling/analyzers", From 3246766edade1b2e112096fcd2530ba12b0922f4 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 25 Jul 2024 16:17:01 +0200 Subject: [PATCH 082/129] Add syntax highlighting docs (#548) * Add syntax highlighting documents * More static * More dynamic stuff * More dynamic * Simplify --- building/config.json | 18 +++++ building/tracks/new/README.md | 1 + .../tracks/new/dynamic-syntax-highlighting.md | 71 +++++++++++++++++ .../tracks/new/static-syntax-highlighting.md | 78 +++++++++++++++++++ building/tracks/new/syntax-highlighting.md | 12 +++ 5 files changed, 180 insertions(+) create mode 100644 building/tracks/new/dynamic-syntax-highlighting.md create mode 100644 building/tracks/new/static-syntax-highlighting.md create mode 100644 building/tracks/new/syntax-highlighting.md diff --git a/building/config.json b/building/config.json index f3828366..bfa37a3a 100644 --- a/building/config.json +++ b/building/config.json @@ -242,6 +242,24 @@ "path": "building/tracks/new/launch.md", "title": "Launch!" }, + { + "uuid": "81169858-41f0-44da-875c-b1bb432f90b0", + "slug": "tracks/new/syntax-highlighting", + "path": "building/tracks/new/syntax-highlighting.md", + "title": "Enable syntax highlighting" + }, + { + "uuid": "374e2596-db7d-433d-b3c0-0f3dc75c453b", + "slug": "tracks/new/syntax-highlighting/static", + "path": "building/tracks/new/static-syntax-highlighting.md", + "title": "Static syntax highlighting" + }, + { + "uuid": "a443c67c-fb01-48f0-ab44-8710c065b3ea", + "slug": "tracks/new/syntax-highlighting/dynamic", + "path": "building/tracks/new/dynamic-syntax-highlighting.md", + "title": "Dynamic syntax highlighting" + }, { "uuid": "f697a7c1-ad33-460c-8458-18cd6d149920", "slug": "tracks/new/implement-tooling", diff --git a/building/tracks/new/README.md b/building/tracks/new/README.md index 6a92b590..2370be57 100644 --- a/building/tracks/new/README.md +++ b/building/tracks/new/README.md @@ -17,5 +17,6 @@ Once you've completed that step, the next steps are: - [Prepare for launch](/docs/building/tracks/new/prepare-for-launch) - [Find Maintainers](/docs/building/tracks/new/find-maintainers) - [Launch!](/docs/building/tracks/new/launch) +- [Enable syntax highlighting](/docs/building/tracks/new/syntax-highlighting) - [Implement additional tooling](/docs/building/tracks/new/implement-tooling) - [Prepare for open source contributions from strangers](/docs/building/tracks/new/prepare-for-contributions) diff --git a/building/tracks/new/dynamic-syntax-highlighting.md b/building/tracks/new/dynamic-syntax-highlighting.md new file mode 100644 index 00000000..e75b514f --- /dev/null +++ b/building/tracks/new/dynamic-syntax-highlighting.md @@ -0,0 +1,71 @@ +# Dynamic syntax highlighting + +Dynamic syntax highlighting is highlighting of code that the user can change. +There is only one place where this happens, and that is the online editor. + +```exercism/note +Code snippets, iterations, and the like are _static_ as the user can't change their code on the fly. +If you'd like to know more of how we handle static syntax highlighting, check the [static syntax highlighting docs](/docs/building/tracks/new/syntax-highlighting/static). +``` + +## Implementation + +Dynamic syntax highlighting is done using the [CodeMirror library](https://codemirror.net/). + +When adding support for your language, there are three options: + +1. The language is supported _out of the box_ by CodeMirror (i.e. listed as a [supported language](https://codemirror.net/5/mode/)). + If so, continue to the [Enable language](#enable-language) section. +2. The language is supported via an existing CodeMirror plugin. + If so, continue to the [Using an existing plugin](#using-an-existing-plugin) section. +3. The language is _not_ supported. + There are now three options: + 1. Write a CodeMirror plugin from scratch, as described in the [Create a new plugin](#create-a-new-plugin) section. + 2. Your language's syntax (closely) resembles another language's syntax (e.g. Unison's syntax resembles Haskell), in which case you could consider using the syntax highlighting of that language for your language. + See the [Enable language](#enable-language) section for more information. + 3. Don't have dynamic syntax highlighting. + +### Enable language + +To enable CodeMirror support for your language, start a topic on the forum (https://forum.exercism.org/c/exercism/building-exercism/125). +We (Exercism) will then create a Pull Request that enables CodeMirror support for your language on the website. + +### Using an existing plugin + +To use an existing plugin, it needs to be published on [NPM](https://www.npmjs.com/). + +If the plugin isn't published on NPM, you can either: + +1. Ask the plugin author if they want to publish on NPM. +2. Fork the repository and publish it yourself. +3. Have us (Exercism) fork the repository and we publish it. + To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + +```exercism/note +The CodeMirror website has a [list of community-built language plugins](https://codemirror.net/docs/community/#language). +``` + +The next step is to [Enable language](#enable-language). + +### Create a new plugin + +If you'd like to create plugin, you have two options for hosting: + +1. Create a repository on your personal account and publish it yourself. +2. Have us (Exercism) create a repository and let us publish it. + To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + +```exercism/note +You could consider forking the [codemirror/lang-example repository](https://github.com/codemirror/lang-example) which implements CodeMirror support for a simple language. +``` + +Once you have a repo, follow the [language package instructions](https://codemirror.net/examples/lang-package/) to implement the plugin. + +You'll then need to publish the plugin on [NPM](https://www.npmjs.com/). +The next step is to [Enable the language](#enable-language). + +### Use a different language + +Your language's syntax (closely) resembles another language's syntax, in which case you could consider using the syntax highlighting of that language for your language. +To do so, configure the track using the other language's CodeMirror plugin. +See the [Enable language](#enable-language) section for more information. diff --git a/building/tracks/new/static-syntax-highlighting.md b/building/tracks/new/static-syntax-highlighting.md new file mode 100644 index 00000000..66e1d950 --- /dev/null +++ b/building/tracks/new/static-syntax-highlighting.md @@ -0,0 +1,78 @@ +# Static syntax highlighting + +Static syntax highlighting is highlighting of code that the user _can't_ change. +This includes code snippets, iterations, and more. + +```exercism/note +The online editor does _not_ use static syntax highlighting as the user can change the code on the fly. +If you'd like to know more of how we handle syntax highlighting in the online editor, check the [dynamic syntax highlighting docs](/docs/building/tracks/new/syntax-highlighting/dynamic). +``` + +## Implementation + +Static syntax highlighting is done using the [highlightjs library](https://highlightjs.org/). + +When adding support for your language, there are three options: + +1. The language is supported _out of the box_ by highlightjs (i.e. listed as a [supported language](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)). + If so, continue to the [Configuring track](#configuring-track) section. +2. The language is supported via an existing highlightjs plugin. + If so, continue to the [Using an existing plugin](#using-an-existing-plugin) section. +3. The language is _not_ supported. + There are now three options: + 1. Write a highlightjs plugin from scratch, as described in the [Create a new plugin](#create-a-new-plugin) section. + 2. Your language's syntax (closely) resembles another language's syntax (e.g. Unison's syntax resembles Haskell), in which case you could consider using the syntax highlighting of that language for your language. + See the [Configuring track](#configuring-track) section for more information. + 3. Don't have static syntax highlighting. + +### Configuring track + +To enable highlightjs support for your track's language, you'll need to modify the track's [config.json file](/docs/building/tracks/config-json). +Within the `config.json` file, add/set the `online_editor.highlightjs_language` key to the appropriate highlightjs language identifier (which can be found in the documentation). + +#### Example + +```json +{ + "online_editor": { + "highlightjs_language": "csharp" + } +} +``` + +### Using an existing plugin + +To use an existing plugin, it needs to be published on [NPM](https://www.npmjs.com/). +If the plugin isn't published on NPM, you can either: + +1. Ask the plugin author if they want to publish on NPM. +2. Fork the repository and publish it yourself. +3. Have us (Exercism) fork the repository and we publish it. + To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + +The next step is to [Enable the plugin](#enable-plugin). + +### Enable plugin + +To enable a plugin (which must be published on [NPM](https://www.npmjs.com/)), start a topic on the forum requesting us to add support for the plugin to the website (https://forum.exercism.org/c/exercism/building-exercism/125). +We (Exercism) will then create a Pull Request that adds the plugin to the website. +Once the PR is merged, you can enable highlightjs support by following the instructions in the [Configuring track](#configuring-track) section. + +### Create a new plugin + +If you'd like to create plugin, you have two options for hosting: + +1. Create a repository on your personal account and publish it yourself. +2. Have us (Exercism) create a repository and let us publish it. + To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + +Once you have a repo, follow the [language contribution instructions](https://highlightjs.readthedocs.io/en/latest/language-contribution.html) to implement the plugin. + +You'll then need to publish the plugin on [NPM](https://www.npmjs.com/). +The next step is to [Enable the plugin](#enable-plugin). + +### Use a different language + +Your language's syntax (closely) resembles another language's syntax, in which case you could consider using the syntax highlighting of that language for your language. +To do so, configure the track to use the other language's highlightjs language identifier. +See the [Configuring track](#configuring-track) section for more information. diff --git a/building/tracks/new/syntax-highlighting.md b/building/tracks/new/syntax-highlighting.md new file mode 100644 index 00000000..3785bded --- /dev/null +++ b/building/tracks/new/syntax-highlighting.md @@ -0,0 +1,12 @@ +# Syntax highlighting + +There are two types of syntax highlighting on the website: + +1. Highlighting _static_ code (code snippets, iterations, and such). + Check [static syntax highlighting docs](/docs/building/tracks/new/syntax-highlighting/static) for more information. +2. Highlighting _dynamic_ code (the online editor). + Check [dynamic syntax highlighting docs](/docs/building/tracks/new/syntax-highlighting/dynamic) for more information. + +```exercism/note +The requirements for static and dynamic syntax highlighting are _very_ different, which is why they use different libraries. +``` From 60eb60472a79a12e415dc6f93fb40a494674f76e Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 2 Aug 2024 12:23:52 +0200 Subject: [PATCH 083/129] Add test track doc (#549) --- building/config.json | 6 ++++++ building/tracks/new/README.md | 1 + building/tracks/new/test-track.md | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 building/tracks/new/test-track.md diff --git a/building/config.json b/building/config.json index bfa37a3a..22dee80c 100644 --- a/building/config.json +++ b/building/config.json @@ -230,6 +230,12 @@ "path": "building/tracks/new/prepare-for-launch.md", "title": "Prepare for launch" }, + { + "uuid": "3d4a4f07-7229-4910-9ba5-b7d931ff9177", + "slug": "tracks/new/test-track", + "path": "building/tracks/new/test-track.md", + "title": "Test track" + }, { "uuid": "296dbe06-3384-409b-b2de-be52593c76d3", "slug": "tracks/new/find-maintainers", diff --git a/building/tracks/new/README.md b/building/tracks/new/README.md index 2370be57..96beaf3b 100644 --- a/building/tracks/new/README.md +++ b/building/tracks/new/README.md @@ -15,6 +15,7 @@ Once you've completed that step, the next steps are: - [Build test runner](/docs/building/tracks/new/build-test-runner) - [Configure tooling](/docs/building/tracks/new/configure-tooling) - [Prepare for launch](/docs/building/tracks/new/prepare-for-launch) +- [Test track](/docs/building/tracks/new/test-track) - [Find Maintainers](/docs/building/tracks/new/find-maintainers) - [Launch!](/docs/building/tracks/new/launch) - [Enable syntax highlighting](/docs/building/tracks/new/syntax-highlighting) diff --git a/building/tracks/new/test-track.md b/building/tracks/new/test-track.md new file mode 100644 index 00000000..159edd8c --- /dev/null +++ b/building/tracks/new/test-track.md @@ -0,0 +1,20 @@ +# Test track + +_Before_ launching your track (meaning, the `"active"` key is still `false` in the track's `config.json`), it is important the track is tested. +This will help find (common) errors and allows them to be fixed _before_ the track is launched, which means that students won't ever encounter them. + +## How to enable the track for testing + +To enable the track for testing, it needs to be added to our database, which is something only site admins can do. +Please open a topic on the forum requesting this at: https://forum.exercism.org/c/exercism/building-exercism/125. + +Note that adding the track to the database will _not_ launch the track, but it _will_ make it accessible on the website for maintainer users. + +## How to find testers + +The ideal users to test the track are other track maintainers, as they: + +- Have experience building a track (and thus know what should have been built) +- Are able to access track's that are not yet active + +To get maintainers to test your track, open a topic on the forum requesting this at: https://forum.exercism.org/c/exercism/building-exercism/125. From ab4b05351dbc1e5bd0ae81c61899f0a63ba01680 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 2 Aug 2024 12:32:33 +0200 Subject: [PATCH 084/129] Add forum links (#550) --- building/tracks/new/dynamic-syntax-highlighting.md | 6 +++--- building/tracks/new/static-syntax-highlighting.md | 6 +++--- building/tracks/new/test-track.md | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/building/tracks/new/dynamic-syntax-highlighting.md b/building/tracks/new/dynamic-syntax-highlighting.md index e75b514f..3f41698b 100644 --- a/building/tracks/new/dynamic-syntax-highlighting.md +++ b/building/tracks/new/dynamic-syntax-highlighting.md @@ -27,7 +27,7 @@ When adding support for your language, there are three options: ### Enable language -To enable CodeMirror support for your language, start a topic on the forum (https://forum.exercism.org/c/exercism/building-exercism/125). +To enable CodeMirror support for your language, start a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125). We (Exercism) will then create a Pull Request that enables CodeMirror support for your language on the website. ### Using an existing plugin @@ -39,7 +39,7 @@ If the plugin isn't published on NPM, you can either: 1. Ask the plugin author if they want to publish on NPM. 2. Fork the repository and publish it yourself. 3. Have us (Exercism) fork the repository and we publish it. - To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + To do so, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. ```exercism/note The CodeMirror website has a [list of community-built language plugins](https://codemirror.net/docs/community/#language). @@ -53,7 +53,7 @@ If you'd like to create plugin, you have two options for hosting: 1. Create a repository on your personal account and publish it yourself. 2. Have us (Exercism) create a repository and let us publish it. - To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + To do so, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. ```exercism/note You could consider forking the [codemirror/lang-example repository](https://github.com/codemirror/lang-example) which implements CodeMirror support for a simple language. diff --git a/building/tracks/new/static-syntax-highlighting.md b/building/tracks/new/static-syntax-highlighting.md index 66e1d950..b3b6511d 100644 --- a/building/tracks/new/static-syntax-highlighting.md +++ b/building/tracks/new/static-syntax-highlighting.md @@ -48,13 +48,13 @@ If the plugin isn't published on NPM, you can either: 1. Ask the plugin author if they want to publish on NPM. 2. Fork the repository and publish it yourself. 3. Have us (Exercism) fork the repository and we publish it. - To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + To do so, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. The next step is to [Enable the plugin](#enable-plugin). ### Enable plugin -To enable a plugin (which must be published on [NPM](https://www.npmjs.com/)), start a topic on the forum requesting us to add support for the plugin to the website (https://forum.exercism.org/c/exercism/building-exercism/125). +To enable a plugin (which must be published on [NPM](https://www.npmjs.com/)), start a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting us to add support for the plugin to the website. We (Exercism) will then create a Pull Request that adds the plugin to the website. Once the PR is merged, you can enable highlightjs support by following the instructions in the [Configuring track](#configuring-track) section. @@ -64,7 +64,7 @@ If you'd like to create plugin, you have two options for hosting: 1. Create a repository on your personal account and publish it yourself. 2. Have us (Exercism) create a repository and let us publish it. - To do so, open a topic on the forum requesting this (https://forum.exercism.org/c/exercism/building-exercism/125). + To do so, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. Once you have a repo, follow the [language contribution instructions](https://highlightjs.readthedocs.io/en/latest/language-contribution.html) to implement the plugin. diff --git a/building/tracks/new/test-track.md b/building/tracks/new/test-track.md index 159edd8c..aa50a807 100644 --- a/building/tracks/new/test-track.md +++ b/building/tracks/new/test-track.md @@ -6,7 +6,7 @@ This will help find (common) errors and allows them to be fixed _before_ the tra ## How to enable the track for testing To enable the track for testing, it needs to be added to our database, which is something only site admins can do. -Please open a topic on the forum requesting this at: https://forum.exercism.org/c/exercism/building-exercism/125. +Please open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. Note that adding the track to the database will _not_ launch the track, but it _will_ make it accessible on the website for maintainer users. @@ -17,4 +17,4 @@ The ideal users to test the track are other track maintainers, as they: - Have experience building a track (and thus know what should have been built) - Are able to access track's that are not yet active -To get maintainers to test your track, open a topic on the forum requesting this at: https://forum.exercism.org/c/exercism/building-exercism/125. +To get maintainers to test your track, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. From 3d692cfc86dd97c220661784c197f72220edb9db Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 7 Aug 2024 10:27:05 +0200 Subject: [PATCH 085/129] Document library support (#552) * Add forum links (#550) * Add dod * Work * Work --- building/config.json | 7 +++++ building/tooling/test-runners/README.md | 1 + building/tooling/test-runners/libraries.md | 33 ++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 building/tooling/test-runners/libraries.md diff --git a/building/config.json b/building/config.json index 22dee80c..86e3f49b 100644 --- a/building/config.json +++ b/building/config.json @@ -829,6 +829,13 @@ "title": "The Test Runner Interface", "blurb": "" }, + { + "uuid": "0ab39b58-771e-42dc-aac9-e531bf43034e", + "slug": "tooling/test-runners/libraries", + "path": "building/tooling/test-runners/libraries.md", + "title": "Libraries", + "blurb": "" + }, { "uuid": "a9cf4712-9022-4bd2-83aa-55cbdfd62374", "slug": "tooling/lines-of-code-counter", diff --git a/building/tooling/test-runners/README.md b/building/tooling/test-runners/README.md index 8c333a44..e020064e 100644 --- a/building/tooling/test-runners/README.md +++ b/building/tooling/test-runners/README.md @@ -23,3 +23,4 @@ You can use the following documents to learn more about building a test runner: - [creating a Test Runner from scratch](/docs/building/tooling/test-runners/creating-from-scratch) - [The Test Runner interface](/docs/building/tooling/test-runners/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/test-runners/docker) +- [When and how to support libraries](/docs/building/tooling/test-runners/libraries) diff --git a/building/tooling/test-runners/libraries.md b/building/tooling/test-runners/libraries.md new file mode 100644 index 00000000..138bae70 --- /dev/null +++ b/building/tooling/test-runners/libraries.md @@ -0,0 +1,33 @@ +# Libraries + +Exercism aims to teach [fluency](/docs/using/product/fluency) in the syntax, idioms, and the standard library of the language. +This explicitly does _not_ mention external (non built-in) libraries, as that falls under _proficiency_. +Exercises must thus, with few exceptions, be solvable without using libraries. + +## Reasons to support libraries + +There are couple of reasons why a track might still want to support libraries: + +1. The language has a (very) minimal standard library. + An example of such a language is Rust. +2. The library adds testing functionality. + An example is a library that adds support for property-based testing. +3. The exercise can only be solved using a library. + An example of such an exercise is the [lens-person exercise](https://exercism.org/exercises/lens-person), which in most languages can only be solved using a library. + +## Supporting libraries in the test runner + +As the test runner does not have access to the internet whilst running, it is not possible to download libraries at run time. +The **only** solution to this problem is to install/download libraries at build time, where you _do_ have access to the internet. +In practice, this means you'll need to install/download libraries within the Dockerfile. + +As an example, the [Prolog test runner's Dockerfile](https://github.com/exercism/prolog-test-runner/blob/ed7447a7518ede6ee3405e649f50aaec828e318b/Dockerfile) installs the `date_time` library: + +```dockerfile +RUN swipl pack install date_time -y +``` + +## Documentation + +If your track supports libraries, this should be documented in a [track doc](/docs/building/tracks/docs). +Please also link to this document (using its full URL) from the [`exercises/shared/.docs/help.md` document](/docs/building/tracks/shared-files#file-help-md). From 6ca912f2b47d793c424030cc8ef5fae5d9a06e59 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 7 Aug 2024 11:18:36 +0200 Subject: [PATCH 086/129] Add best practices for tooling (#551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update docker * Lots of work * More best practice * Apply suggestions from code review Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com> * Fix copy-paste error * Add run command * Fix timeout --------- Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com> --- building/config.json | 7 + building/tooling/analyzers/README.md | 1 + .../analyzers/creating-from-scratch.md | 4 +- building/tooling/best-practices.md | 291 ++++++++++++++++++ building/tooling/docker.md | 23 +- building/tooling/representers/README.md | 1 + .../representers/creating-from-scratch.md | 4 +- building/tooling/test-runners/README.md | 3 +- .../test-runners/creating-from-scratch.md | 4 +- building/tooling/test-runners/interface.md | 2 +- 10 files changed, 320 insertions(+), 20 deletions(-) create mode 100644 building/tooling/best-practices.md diff --git a/building/config.json b/building/config.json index 86e3f49b..9cee04c6 100644 --- a/building/config.json +++ b/building/config.json @@ -717,6 +717,13 @@ "title": "Tooling Docker Setup", "blurb": "" }, + { + "uuid": "83dc65b9-cc74-4a2a-9c70-472f6416c19d", + "slug": "tooling/best-practices", + "path": "building/tooling/best-practices.md", + "title": "Best Practices", + "blurb": "" + }, { "uuid": "b080e814-d3b9-4027-a25e-907f5505cf8d", "slug": "tooling/analyzers", diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index 22069087..3454dd82 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -21,3 +21,4 @@ You can use the following documents to learn more about building an analyzer: - [Writing Analyzer comments](/docs/building/tooling/analyzers/comments) - [Tagging solutions](/docs/building/tooling/analyzers/tags) - [Guidance for building an Analyzer](/docs/building/tooling/analyzers/guidance) +- [Best practices](/docs/building/tooling/best-practices) diff --git a/building/tooling/analyzers/creating-from-scratch.md b/building/tooling/analyzers/creating-from-scratch.md index 268f474e..a95dc1a0 100644 --- a/building/tooling/analyzers/creating-from-scratch.md +++ b/building/tooling/analyzers/creating-from-scratch.md @@ -4,9 +4,9 @@ Firstly, thank you for your interest in creating an Analyzer! These are the steps to get going: -1. Check [our repository list for an existing `...-analyzer`](https://github.com/exercism?q=-analyzer) to ensure that one doesn't already exist. +1. Check [our repository list for an existing `...-analyzer`](https://github.com/search?q=org%3Aexercism+analyzer&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/analyzers) to ensure you are comfortable with the idea of creating an Analyzer. -3. Open an issue at [exercism/exercism][exercism-repo] introducing yourself and telling us which language you'd like to create a Analyzer for. +3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create an Analyzer for. 4. Once an Analyzer repo has been created, use [the Analyzer interface document](/docs/building/tooling/analyzers/interface) to help guide your implementation. We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md new file mode 100644 index 00000000..afe50647 --- /dev/null +++ b/building/tooling/best-practices.md @@ -0,0 +1,291 @@ +# Best Practices + +## Follow official best practices + +The official [Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) have lots of great content on how to improve your Dockerfiles. + +## Performance + +You should primarily optimize for performance (especially for test runners). +This will ensure your tooling runs as fast as possible and does not time-out. + +### Experiment with different Base images + +Try experimenting with different base images (e.g. Alpine instead of Ubuntu), to see if one (significantly) outperforms the other. +If performance is relatively equal, go for the image that is smallest. + +### Try Internal Network + +Check if using the `internal` network instead of `none` improves performance. +See the [network docs](/docs/building/tooling/docker#network) for more information. + +### Prefer build-time commands over run-time commands + +Tooling runs as one-off, short-lived Docker container: + +1. A Docker container is created +2. The Docker container is run with the correct arguments +3. The Docker container is destroyed + +Therefore, code that runs in step 2 runs for _every single tooling run_. +For this reason, reducing the amount of code that runs in step 2 is a great way to improve performance +One way of doing this is to move code from _run-time_ to _build-time_. +Whilst run-time code runs on every single tooling run, build-time code only runs once (when the Docker image is built). + +Build-time code runs once as part of a GitHub Actions workflow. +Therefore, its fine if the code that runs at build-time is (relatively) slow. + +#### Example: pre-compile libraries + +When running tests in the Haskell test runner, it requires some base libraries to be compiled. +As each test run happens in a fresh container, this means that this compilation was done _in every single test run_! +To circumvent this, the [Haskell test runner's Dockerfile](https://github.com/exercism/haskell-test-runner/blob/5264c460054649fc672c3d5932c2f3cb082e2405/Dockerfile) has the following two commands: + +```dockerfile +COPY pre-compiled/ . +RUN stack build --resolver lts-20.18 --no-terminal --test --no-run-tests +``` + +First, the `pre-compiled` directory is copied into the image. +This directory is setup as a sort of fake exercise and depends on the same base libraries that the actual exercise depend on. +Then we run the tests on that directory, which is similar to how tests are run for an actual exercise. +Running the tests will result in the base being compiled, but the difference is that this happens at _build time_. +The resulting Docker image will thus have its base libraries already compiled, which means that no longer has to happen at _run time_, resulting in (much) faster execution times. + +#### Example: pre-compile binaries + +Some languages allow code to be compiled ahead-of-time or just-in-time. +This is a build time vs. run time tradeoff, and again, we favor build time execution for performance reasons. + +The [C# test runner's Dockerfile](https://github.com/exercism/csharp-test-runner/blob/b54122ef76cbf86eff0691daa33c8e50bc83979f/Dockerfile) uses this approach, where the test runner is compiled to a binary ahead-of-time (at build time) instead of just-in-time compiling the code (at run time). +This means that there is less work to do at run-time, which should help increase performance. + +## Size + +You should try to reduce the image's size, which means that it'll: + +- Be faster to deploy +- Reduce costs for us +- Improve startup time of each container + +### Try different distributions + +Different distribution images will have different sizes. +For example, the `alpine:3.20.2` image is **ten times** smaller than the `ubuntu:24.10` image: + +``` +REPOSITORY TAG SIZE +alpine 3.20.2 8.83MB +ubuntu 24.10 101MB +``` + +In general, Alpine-based images are amongst the smallest images, so many tooling images are based on Alpine. + +### Try slimmed-down images + +Some images have special "slim" variants, in which some features will have been removed resulting in smaller image sizes. +For example, the `node:20.16.0-slim` image is **five times** smaller than the `node:20.16.0` image: + +``` +REPOSITORY TAG SIZE +node 20.16.0 1.09GB +node 20.16.0-slim 219MB +``` + +The reason "slim" variants are smaller is that they'll have less features. +Your image might not need the additional features, and if not, consider using the "slim" variant. + +### Removing unneeded bits + +An obvious, but great, way to reduce the size of your image is to remove anything you don't need. +These can include things like: + +- Source files that are no longer needed after building a binary from them +- Files targeting different architectures from the Docker image +- Documentation + +#### Remove package manager files + +Most Docker images need to install additional packages, which is usually done via a package manager. +These packages must be installed at _build time_ (as no internet connection is available at _run time_). +Therefore, any package manager caching/bookkeeping files should be removed after installing the additional packages. + +##### apk + +Distributions that uses the `apk` package manager (such as Alpine) should use the `--no-cache` flag when using `apk add` to install packages: + +```dockerfile +RUN apk add --no-cache curl +``` + +##### apt-get/apt + +Distributions that use the `apt-get`/`apk` package manager (such as Ubuntu) should run the `apt-get autoremove -y` and `rm -rf /var/lib/apt/lists/*` commands _after_ installing the packages and in the same `RUN` command: + +```dockerfile +RUN apt-get update && \ + apt-get install curl -y && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* +``` + +### Use multi-stage builds + +Docker has a feature called [multi-stage builds](https://docs.docker.com/build/building/multi-stage/). +These allow you to partition your Dockerfile into separate _stages_, with only the last stage ending up in the produced Docker image (the rest is only there to support building the last stage). +You can think of each stage as its own mini Dockerfile; stages can use different base images. + +Multi-stage builds are particularly useful when your Dockerfile requires packages to be installed that are _only_ needed at build time. +In this situation, the general structure of your Dockerfile looks like this: + +1. Define a new stage (we'll call this the "build" stage). + This stage will _only_ be used at build time. +2. Install the required additional packages (into the "build" stage). +3. Run the commands that require the additional packages (within the "build" stage). +4. Define a new stage (we'll call this the "runtime" stage). + This stage will make up the resulting Docker image and executed at run time. +5. Copy the result(s) from the commands run in step 3 (in the "build" stage) into this stage (the "runtime" stage). + +With this setup, the additional packages are _only_ installed in the "build" stage and _not_ in the "runtime" stage, which means that they won't end up in the Docker image that is produced. + +#### Example: downloading files + +The Fortran test runner requires `curl` to download some files. +However, its run time image does _not_ need `curl`, which makes this a perfect use case for a multi-stage build. + +First, its [Dockerfile](https://github.com/exercism/fortran-test-runner/blob/783e228d8449143d2040e68b95128bb791833a27/Dockerfile) defines a stage (named "build") in which the `curl` package is installed. +It then uses curl to download files into that stage. + +```dockerfile +FROM alpine:3.15 AS build + +RUN apk add --no-cache curl + +WORKDIR /opt/test-runner +COPY bust_cache . + +WORKDIR /opt/test-runner/testlib +RUN curl -R -O https://raw.githubusercontent.com/exercism/fortran/main/testlib/CMakeLists.txt +RUN curl -R -O https://raw.githubusercontent.com/exercism/fortran/main/testlib/TesterMain.f90 + +WORKDIR /opt/test-runner +RUN curl -R -O https://raw.githubusercontent.com/exercism/fortran/main/config/CMakeLists.txt +``` + +The second part of the Dockerfile defines a new stage and copies the downloaded files from the "build" stage into its own stage using the `COPY` command: + +```dockerfile +FROM alpine:3.15 + +RUN apk add --no-cache coreutils jq gfortran libc-dev cmake make + +WORKDIR /opt/test-runner +COPY --from=build /opt/test-runner/ . + +COPY . . +ENTRYPOINT ["/opt/test-runner/bin/run.sh"] +``` + +##### Example: installing libraries + +The Ruby test runner needs the `git`, `openssh`, `build-base`, `gcc` and `wget` packages to be installed before its required libraries (gems) can be installed. +Its [Dockerfile](https://github.com/exercism/ruby-test-runner/blob/e57ed45b553d6c6411faeea55efa3a4754d1cdbf/Dockerfile) starts with a stage (given the name `build`) that install those packages (via `apk add`) and then installs the libaries (via `bundle install`): + +```dockerfile +FROM ruby:3.2.2-alpine3.18 AS build + +RUN apk update && apk upgrade && \ + apk add --no-cache git openssh build-base gcc wget git + +COPY Gemfile Gemfile.lock . + +RUN gem install bundler:2.4.18 && \ + bundle config set without 'development test' && \ + bundle install +``` + +It then defines the stage that will form the resulting Docker image. +This stage does _not_ install the dependencies the previous stage installed, instead it uses the `COPY` command to copy the installed libraries from the build stage into its own stage: + +```dockerfile +FROM ruby:3.2.2-alpine3.18 + +RUN apk add --no-cache bash + +WORKDIR /opt/test-runner + +COPY --from=build /usr/local/bundle /usr/local/bundle + +COPY . . + +ENTRYPOINT [ "sh", "/opt/test-runner/bin/run.sh" ] +``` + +```exercism/note +The [C# test runner's Dockerfile](https://github.com/exercism/csharp-test-runner/blob/b54122ef76cbf86eff0691daa33c8e50bc83979f/Dockerfile) does something similar, only in this case the build stage can use an existing Docker image that has pre-installed the additional packages required to install libraries. +``` + +## Safety + +Safety is a main reason why we're using Docker containers to run our tooling. + +### Prefer official images + +There are many Docker images on [Docker Hub](https://hub.docker.com/), but try to use [official ones](https://hub.docker.com/search?q=&image_filter=official). +These images are curated and have (far) less chance of being unsafe. + +### Pin versions + +To ensure that builds are stable (i.e. they don't suddenly break), you should always pin your base images to specific tags. +That means instead of: + +```dockerfile +FROM alpine:latest +``` + +you should use: + +```dockerfile +FROM alpine:3.20.2 +``` + +With the latter, builds will always use the same version. + +### Run as a non-privileged user + +By default, many images will run with a user that has root privileges. +You should consider running as a non-privileged user. + +```dockerfile +FROM alpine + +RUN groupadd -r myuser && useradd -r -g myuser myuser + +# + +USER myuser +``` + +### Update package repositories to latest version + +It is (almost) always a good idea to install the latest versions + +```dockerfile +RUN apt-get update && \ + apt-get install curl +``` + +### Support read-only filesystem + +We encourage Docker files to be written using a read-only filesystem. +The only directories you should assume to be writeable are: + +- The solution dir (passed in as the second argument) +- The output dir (passed in as the third argument) +- The `/tmp` dir + +```exercism/caution +Our production environment currently does _not_ enforce a read-only filesystem, but we might in the future. +For this reason, the base template for a new test runner/analyzer/representer starts out with a read-only filesystem. +If you can't get things working on a read-only file, feel free to (for now) assume a writeable file system. +``` diff --git a/building/tooling/docker.md b/building/tooling/docker.md index dc13b59d..b4bdcdb1 100644 --- a/building/tooling/docker.md +++ b/building/tooling/docker.md @@ -1,34 +1,32 @@ -# Tooling Docker Setup +# Docker Setup Our various track tooling are deployed as Docker images. -Each piece of tooling requires a Dockerfile, which specifies how the machine is built. +Each piece of tooling requires a [Dockerfile](https://docs.docker.com/reference/dockerfile/), which specifies how the machine is built. It should live at the root directory of your repository and should be called `Dockerfile`. The Dockerfile should create the minimal image needed for the tooling to function correctly and speedily. - -The Dockerfile should produce an image with as a small a size as possible while maximizing (and prioritizing) performance. -Applying the official [Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) can help to create a minimal image. +Our [Best Practices page](/docs/building/tooling/best-practices) has lots of tips to help you achieve this goal. ## Execution ### Timeouts -Each tooling run has a ten-second window in which to execute. -At the end of that period it will be timed out with a 408 error code. +The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. +After 20 seconds, the process is halted and reports a time-out with a 408 error code. ### Stdout/stderr A tooling run may produce up to a maximum of one-megabyte of stdout and stderr. -If it produces more it will be killed with a 413 error code. +If it produces more, it will be killed with a 413 error code. The contents of `stdout` and `stderr` from each run will be stored in files that can be viewed later. -You may write an `results.out` file to the output directory, which contains debugging information you want to later view. +You may write a `results.out` file to the output directory, which contains debugging information you want to view later. ### Results -The results file may be no larger than 500 kilobytes (including any stack traces etc). +The results file may not be larger than 500 kilobytes (including any stack traces etc). If the file is larger than this, the tooling run will be killed with a 460 error code. ## Configuration @@ -37,7 +35,8 @@ Each solution gets 100% machine resources for a twenty second window. After 20 seconds, the process is halted and reports as a time-out. -Configuration can be set in the [`tools.json` file](https://github.com/exercism/tooling-invoker/blob/main/tools.json) in the Tooling Invoker repository. +Some tools require (slight) deviations from the default configuration. +If so, these are configured in the [`tools.json` file](https://github.com/exercism/tooling-invoker/blob/main/tools.json) in the Tooling Invoker repository. ### Network @@ -67,4 +66,4 @@ docker container run -v /path/to/job:/mnt/exercism-iteration --network none -m 1 ## Editing a Dockerfile -All changes to Dockerfiles require a PR review from the @exercism/ops team, in order to avoid the introduction of security exploits. +All changes to Dockerfiles require a PR review from the `@exercism/maintainers-admin` team, in order to avoid the introduction of security exploits. diff --git a/building/tooling/representers/README.md b/building/tooling/representers/README.md index c4d6d4ba..67d3d224 100644 --- a/building/tooling/representers/README.md +++ b/building/tooling/representers/README.md @@ -35,3 +35,4 @@ You can use the following documents to learn more about building a representer: - [The Representer interface](/docs/building/tooling/representers/interface) - [How to normalize representations for the highest efficiency](/docs/building/tooling/representers/normalization) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/representers/docker) +- [Best practices](/docs/building/tooling/best-practices) diff --git a/building/tooling/representers/creating-from-scratch.md b/building/tooling/representers/creating-from-scratch.md index cd1a7fd3..759caab5 100644 --- a/building/tooling/representers/creating-from-scratch.md +++ b/building/tooling/representers/creating-from-scratch.md @@ -4,9 +4,9 @@ Firstly, thank you for your interest in creating a Representer! These are the steps to get going: -1. Check [our repository list for an existing `...-representer`](https://github.com/exercism?q=-representer) to ensure that one doesn't already exist. +1. Check [our repository list for an existing `...-representer`](https://github.com/search?q=org%3Aexercism+representer&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/representers) to ensure you are comfortable with the idea of creating an Representer. -3. Open an issue at [exercism/exercism][exercism-repo] introducing yourself and telling us which language you'd like to create a Representer for. +3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create a Representer for. 4. Once a Representer repo has been created, use [the Representer interface document](/docs/building/tooling/representers/interface) to help guide your implementation. We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 diff --git a/building/tooling/test-runners/README.md b/building/tooling/test-runners/README.md index e020064e..2642d219 100644 --- a/building/tooling/test-runners/README.md +++ b/building/tooling/test-runners/README.md @@ -12,7 +12,7 @@ Test Runners give us two advantages: Each language has its own Test Runner, written in that language. The website acts as the orchestrator between the Test Runners and students' submissions. -Each Test Runner lives in the Exercism GitHub organization in a repository named `$LANG-test-runner` (e.g. `ruby-test-runner`). +Each Test Runner lives in the Exercism GitHub organization in a repository named `$LANG-test-runner` (e.g. [`exercism/ruby-test-runner`](https://github.com/exercism/ruby-test-runner)). You can explore the different Test Runners [here](https://github.com/exercism?q=-test-runner). If you would like to get involved in helping with an existing Test Runner, please open an issue in its repository asking if there is somewhere you can help. @@ -23,4 +23,5 @@ You can use the following documents to learn more about building a test runner: - [creating a Test Runner from scratch](/docs/building/tooling/test-runners/creating-from-scratch) - [The Test Runner interface](/docs/building/tooling/test-runners/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/test-runners/docker) +- [Best practices](/docs/building/tooling/best-practices) - [When and how to support libraries](/docs/building/tooling/test-runners/libraries) diff --git a/building/tooling/test-runners/creating-from-scratch.md b/building/tooling/test-runners/creating-from-scratch.md index c7a10711..cd71dfaa 100644 --- a/building/tooling/test-runners/creating-from-scratch.md +++ b/building/tooling/test-runners/creating-from-scratch.md @@ -4,9 +4,9 @@ Firstly, thank you for your interest in creating a Test Runner! These are the steps to get going: -1. Check [our repository list for an existing `...-test-runner`](https://github.com/exercism?q=-test-runner) to ensure that one doesn't already exist. +1. Check [our repository list for an existing `...-test-runner`](https://github.com/search?q=org%3Aexercism+test-runner&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/test-runners) to ensure you are comfortable with the idea of creating an Test Runner. -3. Open an issue at [exercism/exercism][exercism-repo] introducing yourself and telling us which language you'd like to create a Test Runner for. +3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create a Test Runner for. 4. Once a Test Runner repo has been created, use [the Test Runner interface document](/docs/building/tooling/test-runners/interface) to help guide your implementation. There is a [generic test runner repository template](https://github.com/exercism/generic-test-runner/) that you can use to kick-start development. We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index bfce0b62..83cb1951 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -15,7 +15,7 @@ All interactions with the Exercism website are handled automatically and are not ### Allowed run time -The test runner gets 100% machine resources for a 20 second window per solution. +The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out. ## Output format From aec4d35d5a6d0505d723705198cb6db7f6796bab Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 8 Aug 2024 13:20:03 +0200 Subject: [PATCH 087/129] More CI docs (#554) * More CI docs * Better document setting up CI --- .../new/setup-continuous-integration.md | 294 +++++++++++++++++- 1 file changed, 284 insertions(+), 10 deletions(-) diff --git a/building/tracks/new/setup-continuous-integration.md b/building/tracks/new/setup-continuous-integration.md index 50ff4194..2feb2f15 100644 --- a/building/tracks/new/setup-continuous-integration.md +++ b/building/tracks/new/setup-continuous-integration.md @@ -1,16 +1,290 @@ # Set up Continuous Integration -Setting up Continuous Integration (CI) for your track is very important, as it helps to automatically catch mistakes. +Setting up Continuous Integration (CI) for your track is very important, as it helps automatically catch mistakes. -Our tracks all use [GitHub Actions](https://docs.github.com/en/actions) to run their CI. -GitHub actions uses the concept of _workflows_, which are scripts that are run automatically whenever a specific event occurs (e.g. pushing a commit). +## GitHub Actions -Each workflow corresponds to a file in `.github/workflows`. -Each new track repository comes pre-loaded with three workflows: +Our tracks (and other repositories) use [GitHub Actions](https://docs.github.com/en/actions) to run their CI. +GitHub Actions uses the concept of _workflows_, which are scripts that run automatically whenever a specific event occurs (e.g. pushing a commit). -- `test.yml`: this workflow should run the tests for each exercise the track has implemented -- `configlet.yml`: this workflow runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically. -- `sync-labels.yml`: this workflow automatically syncs the repository's labels from a `labels.yml` file +Each GitHub Actions workflow is defined in a `.yml` file in the `.github/workflows` directory. +For information on workflows, check the following docs: -Of these three workflows, only the first workflow will need some manual work. -To find out what needs to happen, please check the `test.yml` file's contents, which has TODO comments to help you. +- [Workflow syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions) +- [Choosing when your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow) +- [Choosing where your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs) +- [Choose what your workflow does](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does) +- [Writing workflows](https://docs.github.com/en/actions/writing-workflows) +- [Best practices](/docs/building/github/gha-best-practices) + +## Pre-defined workflows + +A track repository contains several pre-defined workflows: + +- `configlet.yml`: runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically +- `no-important-files-changed.yml`: checks if pull requests would cause all existing solutions of one or more changes exercises to be re-run +- `sync-labels.yml`: automatically syncs the repository's labels from a `labels.yml` file +- `test.yml`: verify the track's exercises + +Of these workflows, _only_ the `test.yml` workflow requires manual work. +The other workflows should not be changed (we keep them up-to-date automatically). + +## Test workflow + +The test workflow should verify the track's exercises. +The workflow itself should not do much, except for: + +- Checking out the code (already implemented) +- Installing dependencies (e.g. installing an SDK, optional) +- Running the script to verify the exercises (already implemented) + +### Verify exercises script + +As mentioned, the exercises are verified via a script, namely the `bin/verify-exercises` (bash) script. +This script is _almost_ done, and does the following: + +- Loops over all exercise directories +- For each exercise directory, it then: + - Copies the example/exemplar solution to the (stub) solution files (already implemented) + - Calls the `unskip_tests` function in which you can unskip tests in your test files (optional) + - Calls the `run_tests` function in which you should run the tests (required) + +The `run_tests` and `unskip_tests` functions are the only things that you need to implement. + +### Unskipping tests + +If your track supports skipping tests, we must ensure that no tests are skipped when verifying an exercise's example/exemplar solution. +In general, there are two ways in which tracks support "unskipping" tests: + +1. Removing annotations/code/text from the test files. + For example, changing `test.skip` to `test`. +2. Providing an environment variable. + For example, setting `SKIP_TESTS=false`. + +If skipping tests is file-based (the first option mentioned above), edit the `unskip_tests` function to modify the test files (the existing code already handles the looping over the test files). + +```exercism/note +The `unskip_test` function runs on a copy of an exercise directory, so feel free to modify the files as you see fit. +``` + +If unskipping tests requires an environment variable to be set, make sure that it is set in the `run_tests` function. + +### Running tests + +The `run_tests` function is responsible for running the tests of an exercise. +When the function is called, the example/exemplar files will already have been copied to (stub) solution files, so you only need to call the right command to run the tests. + +The function must return a zero as the exit code if all tests pass, otherwise return a non-zero exit code. + +```exercism/note +The `run_tests` function runs on a copy of an exercise directory, so feel free to modify the files as you see fit. +``` + +### Example: Arturo track + +This is what the [`bin/verify-exercises` file](https://github.com/exercism/arturo/blob/79560f853f5cb8e2f3f0a07cbb8fcce8438ee996/bin/verify-exercises) looks file for the Arturo track: + +```bash +#!/usr/bin/env bash + +# Synopsis: +# Test the track's exercises. + +# Example: verify all exercises +# ./bin/verify-exercises + +# Example: verify single exercise +# ./bin/verify-exercises two-fer + +set -eo pipefail + +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." +} + +required_tool jq + +copy_example_or_examplar_to_solution() { + jq -c '[.files.solution, .files.exemplar // .files.example] | transpose | map({src: .[1], dst: .[0]}) | .[]' .meta/config.json | while read -r src_and_dst; do + cp "$(echo "${src_and_dst}" | jq -r '.src')" "$(echo "${src_and_dst}" | jq -r '.dst')" + done +} + +unskip_tests() { + jq -r '.files.test[]' .meta/config.json | while read -r test_file; do + sed -i 's/test.skip/test/g' "${test_file}" + done +} + +run_tests() { + arturo tester.art +} + +verify_exercise() { + local dir + local slug + local tmp_dir + + dir=$(realpath "${1}") + slug=$(basename "${dir}") + tmp_dir=$(mktemp -d -t "exercism-verify-${slug}-XXXXX") + + echo "Verifying ${slug} exercise..." + + ( + cp -r "${dir}/." "${tmp_dir}" + cd "${tmp_dir}" + + copy_example_or_examplar_to_solution + unskip_tests + run_tests + ) +} + +exercise_slug="${1:-*}" + +shopt -s nullglob +for exercise_dir in ./exercises/{concept,practice}/${exercise_slug}/; do + if [ -d "${exercise_dir}" ]; then + verify_exercise "${exercise_dir}" + fi +done +``` + +It uses `sed` to unskip tests: + +```bash +sed -i 's/test.skip/test/g' "${test_file}" +``` + +and runs the tests via the `arturo` command: + +```bash +arturo tester.art +``` + +## Implement the test workflow + +The goal of the test workflow (defined in `.github/workflows/test.yml`) is to automatically verify that the track's exercises are in proper shape. +The workflow is setup to run automatically (in GitHub Actions terminology: is _triggered_) when a push is made to the `main` branch or to a pull request's branch. + +There are three options when implementing this workflow: + +### Option 1: install track-specific tooling (e.g. an SDK) in the GitHub Actions runner instance + +In this approach, any track-specific tooling (e.g. an SDK) is installed directly in the GitHub Actions runner instance. +Once done, you then run the `bin/verify-exercises` script (which assumes the track tooling is installed). + +For an example, see the [Arturo track's `test.yml` workflow](https://github.com/exercism/arturo/blob/79560f853f5cb8e2f3f0a07cbb8fcce8438ee996/.github/workflows/test.yml): + +```yml +name: Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + ci: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev + + - name: Install Arturo + run: bin/install-arturo + env: + GH_TOKEN: ${{ github.token }} + + - name: Verify all exercises + run: bin/verify-exercises +``` + +#### Option 2: running the verify exercises script within test runner Docker image + +In this option, we're using the fact that each track must have a test runner which has all dependencies already installed +To enable this option, we need to set the workflow's container to the test runner: + +```yml +container: + image: exercism/vimscript-test-runner +``` + +This will then automatically pull the test runner Docker image when the workflow executes, and run the `verify-exercises` script within that Docker container. + +```exercism/note +The main benefit of this approach is that it better mimics how tests are being run in production (on the website). +With the approach, it is less likely that things will fail in production that passed in CI. +The downside of this approach is that it likely is slower, due to having to pull the Docker image and the overhead of Docker. +``` + +For an example, see the [vimscript track's `test.yml` workflow](https://github.com/exercism/vimscript/blob/e599cd6e02cbcab2c38c5112caed8bef6cdb3c38/.github/workflows/test.yml). + +```yml +name: Verify Exercises + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + ci: + runs-on: ubuntu-24.04 + container: + image: exercism/vimscript-test-runner + + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + + - name: Verify all exercises + run: bin/verify-exercises +``` + +### Option 3: download the test runner Docker image and change verify exercises script + +In this option, we're using the fact that each track must have a test runner which already knows how to verify exercises. +To enable this option, we first need to download (pull) the track's test runner Docker image and then run the `bin/verify-exercises` script, which is modified to use the test runner Docker image to run the tests. + +```exercism/note +The main benefit of this approach is that it best mimics how tests are being run in production (on the website). +With the approach, it is less likely that things will fail in production that passed in CI. +The downside of this approach is that it likely is slower, due to having to pull the Docker image and the overhead of Docker. +``` + +For an example, see the [Standard ML track's `test.yml` workflow](https://github.com/exercism/sml/blob/e63e93ee50d8d7f0944ff4b7ad385819b86e1693/.github/workflows/ci.yml). + +```yml +name: sml / ci + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +jobs: + ci: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + + - run: docker pull exercism/sml-test-runner + + - name: Run tests for all exercises + run: sh ./bin/test +``` From f9877daba1c5b1f9ce5e8e17ba876466791d08dc Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 9 Aug 2024 12:45:03 +0200 Subject: [PATCH 088/129] Document inalibity to view tooling files (#557) --- building/tooling/docker.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/building/tooling/docker.md b/building/tooling/docker.md index b4bdcdb1..5cadccc2 100644 --- a/building/tooling/docker.md +++ b/building/tooling/docker.md @@ -20,9 +20,12 @@ After 20 seconds, the process is halted and reports a time-out with a 408 error A tooling run may produce up to a maximum of one-megabyte of stdout and stderr. If it produces more, it will be killed with a 413 error code. -The contents of `stdout` and `stderr` from each run will be stored in files that can be viewed later. +The contents of `stdout` and `stderr` from each run will be stored in files. +You may write a `results.out` file to the output directory, which contains debugging information. -You may write a `results.out` file to the output directory, which contains debugging information you want to view later. +```exercism/caution +Right now, it is not possible for maintainers to view the contents of these files. +``` ### Results From 457862847fbaaa51aadb118e8c0cde847c0dd884 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 13 Aug 2024 09:37:08 +0200 Subject: [PATCH 089/129] Improve CI docs (#555) * Improve CI docs * More dos * More docs * Link --- building/config.json | 6 + building/tracks/ci/README.md | 5 + building/tracks/ci/workflows.md | 41 +++ .../new/setup-continuous-integration.md | 275 +++++++++--------- 4 files changed, 186 insertions(+), 141 deletions(-) create mode 100644 building/tracks/ci/workflows.md diff --git a/building/config.json b/building/config.json index 9cee04c6..2a7de427 100644 --- a/building/config.json +++ b/building/config.json @@ -661,6 +661,12 @@ "path": "building/tracks/stories/tuples.santas-helper.md", "title": "Santa's Helper" }, + { + "uuid": "b99fb54b-a9ce-4a50-bca4-6a928cc77ec6", + "slug": "tracks/ci/workflows", + "path": "building/tracks/ci/workflows.md", + "title": "Workflows" + }, { "uuid": "191b0fa1-96e2-48a6-ad2e-c34f57443799", "slug": "tracks/ci/migrating-from-travis", diff --git a/building/tracks/ci/README.md b/building/tracks/ci/README.md index d50c46aa..5fe63e58 100644 --- a/building/tracks/ci/README.md +++ b/building/tracks/ci/README.md @@ -2,3 +2,8 @@ At Exercism, we use [GitHub Actions](https://github.com/features/actions) to handle our [continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) (CI) and [continuous deployment](https://en.wikipedia.org/wiki/Continuous_deployment) (CD) needs. This includes running tests, formatting things, and deploying things. + +For more information, check: + +- [Workflows](/docs/building/tracks/ci/workflows) +- [Setting up CI for new tracks](/docs/building/tracks/new/setup-continuous-integration) diff --git a/building/tracks/ci/workflows.md b/building/tracks/ci/workflows.md new file mode 100644 index 00000000..93c4d68e --- /dev/null +++ b/building/tracks/ci/workflows.md @@ -0,0 +1,41 @@ +# Workflows + +GitHub Actions uses the concept of _workflows_, which are scripts that run automatically whenever a specific event occurs (e.g. pushing a commit). + +Each GitHub Actions workflow is defined in a `.yml` file in the `.github/workflows` directory. +For information on workflows, check the following docs: + +- [Workflow syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions) +- [Choosing when your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow) +- [Choosing where your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs) +- [Choose what your workflow does](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does) +- [Writing workflows](https://docs.github.com/en/actions/writing-workflows) +- [Best practices](/docs/building/github/gha-best-practices) + +## Shared workflows + +Some workflows are shared across repositories. +These workflows _should not be changed_. + +### General workflows + +- `sync-labels.yml`: automatically syncs the repository's labels from a `labels.yml` file + +### Track-specific workflows + +- `configlet.yml`: runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically +- `no-important-files-changed.yml`: checks if pull requests would cause all existing solutions of one or more changes exercises to be re-run +- `test.yml`: verify the track's exercises + +### Tooling-specific workflows + +- `deploy.yml`: deploy the tooling Docker image to Docker Hub and ECR + +## Custom workflows + +Maintainers are free to add custom workflows to their repos. +Examples of such workflows could be: + +- Linting of shell scripts ([example](https://github.com/exercism/configlet/blob/3baa09608c8ac327315c887608c13a68ae8ac359/.github/workflows/shellcheck.yml)) +- Auto-commenting on pull requests ([example](https://github.com/exercism/elixir/blob/b737f80cc93fcfdec6c53acb7361819834782470/.github/workflows/pr-comment.yml)) +- Etc. diff --git a/building/tracks/new/setup-continuous-integration.md b/building/tracks/new/setup-continuous-integration.md index 2feb2f15..469dbbcf 100644 --- a/building/tracks/new/setup-continuous-integration.md +++ b/building/tracks/new/setup-continuous-integration.md @@ -1,44 +1,27 @@ # Set up Continuous Integration -Setting up Continuous Integration (CI) for your track is very important, as it helps automatically catch mistakes. +Setting up Continuous Integration (CI) for your track is very important, as it helps catch mistakes. ## GitHub Actions -Our tracks (and other repositories) use [GitHub Actions](https://docs.github.com/en/actions) to run their CI. -GitHub Actions uses the concept of _workflows_, which are scripts that run automatically whenever a specific event occurs (e.g. pushing a commit). - -Each GitHub Actions workflow is defined in a `.yml` file in the `.github/workflows` directory. -For information on workflows, check the following docs: - -- [Workflow syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions) -- [Choosing when your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow) -- [Choosing where your workflow runs](https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs) -- [Choose what your workflow does](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does) -- [Writing workflows](https://docs.github.com/en/actions/writing-workflows) -- [Best practices](/docs/building/github/gha-best-practices) - -## Pre-defined workflows - -A track repository contains several pre-defined workflows: - -- `configlet.yml`: runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically -- `no-important-files-changed.yml`: checks if pull requests would cause all existing solutions of one or more changes exercises to be re-run -- `sync-labels.yml`: automatically syncs the repository's labels from a `labels.yml` file -- `test.yml`: verify the track's exercises - -Of these workflows, _only_ the `test.yml` workflow requires manual work. -The other workflows should not be changed (we keep them up-to-date automatically). +Exercism repos (including track repos) use [GitHub Actions](https://docs.github.com/en/actions) to run their CI. +GitHub Actions are based on _workflows_, which define scripts to run automatically whenever a specific event occurs (e.g. pushing a commit). +For more information on GitHub Actions workflows, check the [workflows docs](/docs/building/tracks/ci/workflows). ## Test workflow -The test workflow should verify the track's exercises. +Each track comes with a `test.yml` workflow. +The goal of this workflow is to verify that the track's exercises are in proper shape. +The workflow is setup to run automatically (in GitHub Actions terminology: is _triggered_) when a push is made to the `main` branch or to a pull request's branch. + The workflow itself should not do much, except for: - Checking out the code (already implemented) -- Installing dependencies (e.g. installing an SDK, optional) -- Running the script to verify the exercises (already implemented) +- Installing dependencies (e.g. installing packages, optional) +- Installing tooling (e.g. installing an SDK, optional) +- Running the verify exercises script (already implemented) -### Verify exercises script +## Implement the verify exercises script As mentioned, the exercises are verified via a script, namely the `bin/verify-exercises` (bash) script. This script is _almost_ done, and does the following: @@ -51,7 +34,7 @@ This script is _almost_ done, and does the following: The `run_tests` and `unskip_tests` functions are the only things that you need to implement. -### Unskipping tests +### Unskip tests If your track supports skipping tests, we must ensure that no tests are skipped when verifying an exercise's example/exemplar solution. In general, there are two ways in which tracks support "unskipping" tests: @@ -61,120 +44,135 @@ In general, there are two ways in which tracks support "unskipping" tests: 2. Providing an environment variable. For example, setting `SKIP_TESTS=false`. +#### Removing annotations/code/text from the test files + If skipping tests is file-based (the first option mentioned above), edit the `unskip_tests` function to modify the test files (the existing code already handles the looping over the test files). ```exercism/note The `unskip_test` function runs on a copy of an exercise directory, so feel free to modify the files as you see fit. ``` +##### Example + +The [Arturo track's `bin/verify-exercises file`](https://github.com/exercism/arturo/blob/2393d62933058f011baea3631e9295b7884925e0/bin/verify-exercises) uses `sed` to unskip the tests within the test files: + +```bash +unskip_tests() { + jq -r '.files.test[]' .meta/config.json | while read -r test_file; do + sed -i 's/test.skip/test/g' "${test_file}" + done +} +``` + +#### Providing an environment variable + +```exercism/caution If unskipping tests requires an environment variable to be set, make sure that it is set in the `run_tests` function. +``` -### Running tests +### Run tests The `run_tests` function is responsible for running the tests of an exercise. When the function is called, the example/exemplar files will already have been copied to (stub) solution files, so you only need to call the right command to run the tests. -The function must return a zero as the exit code if all tests pass, otherwise return a non-zero exit code. +The function must return zero as the exit code if all tests pass, otherwise return a non-zero exit code. ```exercism/note The `run_tests` function runs on a copy of an exercise directory, so feel free to modify the files as you see fit. ``` -### Example: Arturo track - -This is what the [`bin/verify-exercises` file](https://github.com/exercism/arturo/blob/79560f853f5cb8e2f3f0a07cbb8fcce8438ee996/bin/verify-exercises) looks file for the Arturo track: - -```bash -#!/usr/bin/env bash +#### Option 1: use language tooling -# Synopsis: -# Test the track's exercises. +The default option for the verify exercises script is to use the language's tooling (SDK/binary/etc.), which is what most tracks use. +Each track will have its own way of running the tests, but usually it is just a single command. -# Example: verify all exercises -# ./bin/verify-exercises +#### Example -# Example: verify single exercise -# ./bin/verify-exercises two-fer +The [Arturo track's `bin/verify-exercises file`](https://github.com/exercism/arturo/blob/2393d62933058f011baea3631e9295b7884925e0/bin/verify-exercises) modifies the `run_tests` function to simply call the `arturo` command on the test file: -set -eo pipefail - -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." +```bash +run_tests() { + arturo tester.art } +``` -required_tool jq +### Option 2: use the test runner Docker image -copy_example_or_examplar_to_solution() { - jq -c '[.files.solution, .files.exemplar // .files.example] | transpose | map({src: .[1], dst: .[0]}) | .[]' .meta/config.json | while read -r src_and_dst; do - cp "$(echo "${src_and_dst}" | jq -r '.src')" "$(echo "${src_and_dst}" | jq -r '.dst')" - done -} +The second option is to verify the exercises by running the track's [test runner](/docs/building/tracks/new/build-test-runner). +This of course depends on the track having a working [test runner](/docs/building/tracks/new/build-test-runner). -unskip_tests() { - jq -r '.files.test[]' .meta/config.json | while read -r test_file; do - sed -i 's/test.skip/test/g' "${test_file}" - done -} +If your track does not yet have a test runner, you can either: -run_tests() { - arturo tester.art -} +- build a working test runner, or +- use option 1 and directly use the language tooling -verify_exercise() { - local dir - local slug - local tmp_dir +The following modifications need to be made to the default `bin/verify-exercises` script: - dir=$(realpath "${1}") - slug=$(basename "${dir}") - tmp_dir=$(mktemp -d -t "exercism-verify-${slug}-XXXXX") +1. Verify that the `docker` command is available +2. Pull (download) the test runner Docker image +3. Use `docker run` to run the test runner Docker image on each exercise +4. Use `jq` to verify that the `results.json` file returned by the Docker container indicates all tests passed +5. Remove the `unskip_test` function and the call to that function - echo "Verifying ${slug} exercise..." +```exercism/note +The main benefit of this approach is that it best mimics how tests are being run in production (on the website). +With this approach, it is less likely that things fail in production that passed in CI. +The downside of this approach is that it usually is slower, due to having to pull the Docker image and the overhead of Docker. +``` - ( - cp -r "${dir}/." "${tmp_dir}" - cd "${tmp_dir}" +#### Example - copy_example_or_examplar_to_solution - unskip_tests - run_tests - ) -} +The [Unison track's `bin/verify-exercises file`](https://github.com/exercism/unison/blob/f39ab0e6bd0d6ac538f343474a01bf9755d4a93c/bin/test) adds the check to verify that the `docker` command is also installed: + +```bash +required_tool docker +``` -exercise_slug="${1:-*}" +Then, it pulls the track's test runner image: -shopt -s nullglob -for exercise_dir in ./exercises/{concept,practice}/${exercise_slug}/; do - if [ -d "${exercise_dir}" ]; then - verify_exercise "${exercise_dir}" - fi -done +```bash +docker pull exercism/unison-test-runner ``` -It uses `sed` to unskip tests: +It then modifies the `run_tests` function to use `docker run` to run the test runner on the current exercise (which is in the working directory), followed by a `jq` command to check for the right status: ```bash -sed -i 's/test.skip/test/g' "${test_file}" +run_tests() { + local slug + + slug="${1}" + + docker run \ + --rm \ + --network none \ + --mount type=bind,src="https://github.com/$%7BPWD%7D",dst=/solution \ + --mount type=bind,src="https://github.com/$%7BPWD%7D",dst=/output \ + --tmpfs /tmp:rw \ + exercism/unison-test-runner "${slug}" "/solution" "/output" + jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1 +} ``` -and runs the tests via the `arturo` command: +Finally, we need to modify the calling of the `run_tests` command, as it now requires the slug: ```bash -arturo tester.art +run_tests "${slug}" ``` ## Implement the test workflow -The goal of the test workflow (defined in `.github/workflows/test.yml`) is to automatically verify that the track's exercises are in proper shape. -The workflow is setup to run automatically (in GitHub Actions terminology: is _triggered_) when a push is made to the `main` branch or to a pull request's branch. +Now that the `verify-exercises` script is finished, it's time to finalize the `test.yml` workflow. +How to do so depends on what option was chosen for the `verify-exercises` script implementation. + +### Option 1: use language tooling -There are three options when implementing this workflow: +If the `verify-exercises` script directly uses the language's tooling, the test workflow will need to install: -### Option 1: install track-specific tooling (e.g. an SDK) in the GitHub Actions runner instance +- Language tooling dependencies, such as openssh or a C/C++ compiler. +- Language tooling, such as an SDK or binary. + If the language tooling installation does _not_ add the installed binary/binaries to the path, make sure to [add it to GitHub Actions' system path](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#adding-a-system-path). -In this approach, any track-specific tooling (e.g. an SDK) is installed directly in the GitHub Actions runner instance. -Once done, you then run the `bin/verify-exercises` script (which assumes the track tooling is installed). +Once that is done, the `verify-exercises` should work as expected, and you've successfully set up CI! For an example, see the [Arturo track's `test.yml` workflow](https://github.com/exercism/arturo/blob/79560f853f5cb8e2f3f0a07cbb8fcce8438ee996/.github/workflows/test.yml): @@ -210,9 +208,44 @@ jobs: run: bin/verify-exercises ``` -#### Option 2: running the verify exercises script within test runner Docker image +### Option 2: use the test runner Docker image + +The second option is to verify the exercises by running the track's [test runner](/docs/building/tracks/new/build-test-runner). +This option requires two things to be true: + +1. The track has a working [test runner](/docs/building/tracks/new/build-test-runner) +2. The `verify-exercises` script use the test runner Docker image to run an exercise's tests + +If your track does not yet have a test runner, you can either: + +- build a working test runner, or +- use option 1 and directly use the language tooling -In this option, we're using the fact that each track must have a test runner which has all dependencies already installed +This approach has a couple of advantages: + +1. You don't need to install any dependencies/tooling within the test workflow (as those will have been installed within the Docker image) +2. The approach best mimics how tests are being run in production (on the website), reducing the likelihood of production issues. + +The main downside is that it likely is slower, due to having to pull the Docker image and the overhead of Docker. + +There a couple of ways in which could pull the test runner Docker image: + +1. Download the image within the `verify-exercises` file. + This is the approach taken by the [Unison track](https://github.com/exercism/unison/blob/f39ab0e6bd0d6ac538f343474a01bf9755d4a93c/bin/test#L32). +2. Download the image within the workflow. + This is the approach taken by the [Standard ML track](https://github.com/exercism/sml/blob/e63e93ee50d8d7f0944ff4b7ad385819b86e1693/.github/workflows/ci.yml#L16). +3. Build the image within the workflow. + This is the approach taken by the [8th track](https://github.com/exercism/8th/blob/9034bcb6aa38540e1a67ba2fa6b76001f50c094b/.github/workflows/test.yml#L18-L40). + +So which approach to use? +We recommend _at least_ implementing option number 1, to make the `verify-exercises` script be _standalone_. +If your image is particularly large, it might be beneficial to also implement option 3, which will store the built Docker image into the GitHub Actions cache. +Subsequent runs can then just read the Docker image from cache, instead of downloading it, which might be better for performance (please measure to be sure). + +### Option 3: running the verify exercises script within test runner Docker image + +A third, alternative option is a hybrid of the previous two options. +Here, we're also using the test runner Docker image, only this time we run the `verify-exercises` script _within that Docker image_. To enable this option, we need to set the workflow's container to the test runner: ```yml @@ -220,15 +253,11 @@ container: image: exercism/vimscript-test-runner ``` -This will then automatically pull the test runner Docker image when the workflow executes, and run the `verify-exercises` script within that Docker container. +We can then skip the dependencies and tooling installation steps (as those will have been installed within the test runner Docker image) and proceed with running the `bin/verify-exercises` script. -```exercism/note -The main benefit of this approach is that it better mimics how tests are being run in production (on the website). -With the approach, it is less likely that things will fail in production that passed in CI. -The downside of this approach is that it likely is slower, due to having to pull the Docker image and the overhead of Docker. -``` +#### Example -For an example, see the [vimscript track's `test.yml` workflow](https://github.com/exercism/vimscript/blob/e599cd6e02cbcab2c38c5112caed8bef6cdb3c38/.github/workflows/test.yml). +The [vimscript track's `test.yml` workflow](https://github.com/exercism/vimscript/blob/e599cd6e02cbcab2c38c5112caed8bef6cdb3c38/.github/workflows/test.yml) uses this option: ```yml name: Verify Exercises @@ -252,39 +281,3 @@ jobs: - name: Verify all exercises run: bin/verify-exercises ``` - -### Option 3: download the test runner Docker image and change verify exercises script - -In this option, we're using the fact that each track must have a test runner which already knows how to verify exercises. -To enable this option, we first need to download (pull) the track's test runner Docker image and then run the `bin/verify-exercises` script, which is modified to use the test runner Docker image to run the tests. - -```exercism/note -The main benefit of this approach is that it best mimics how tests are being run in production (on the website). -With the approach, it is less likely that things will fail in production that passed in CI. -The downside of this approach is that it likely is slower, due to having to pull the Docker image and the overhead of Docker. -``` - -For an example, see the [Standard ML track's `test.yml` workflow](https://github.com/exercism/sml/blob/e63e93ee50d8d7f0944ff4b7ad385819b86e1693/.github/workflows/ci.yml). - -```yml -name: sml / ci - -on: - pull_request: - push: - branches: [main] - workflow_dispatch: - -jobs: - ci: - runs-on: ubuntu-22.04 - - steps: - - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - - run: docker pull exercism/sml-test-runner - - - name: Run tests for all exercises - run: sh ./bin/test -``` From a0a967003d9dd881389d557057ce65cc4a155c5e Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 13 Aug 2024 14:39:04 +0200 Subject: [PATCH 090/129] Tests (#558) * Add tooling testing * Add more --- building/tooling/best-practices.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md index afe50647..bf148dfc 100644 --- a/building/tooling/best-practices.md +++ b/building/tooling/best-practices.md @@ -225,6 +225,28 @@ ENTRYPOINT [ "sh", "/opt/test-runner/bin/run.sh" ] The [C# test runner's Dockerfile](https://github.com/exercism/csharp-test-runner/blob/b54122ef76cbf86eff0691daa33c8e50bc83979f/Dockerfile) does something similar, only in this case the build stage can use an existing Docker image that has pre-installed the additional packages required to install libraries. ``` +## Testing + +### Use integration tests + +Unit tests can be very useful, but we recommend focusing on writing [integration tests](https://en.wikipedia.org/wiki/Integration_testing). +Their main benefit is that they better test how tooling runs in production, and thus help increase confidence in your tooling's implementation. + +#### Use Docker + +To best mimic the production environment, the integration tests should run the tooling _like the production environment_. +This means building the Docker image and then running the built image on a solution to verify its output. + +#### Use golden tests + +Integration tests should be defined as [golden tests](https://ro-che.info/articles/2017-12-04-golden-tests), which are tests where the expected output is stored in a file. +This is perfect for track-tooling integration tests, as the output of tooling are also files. + +##### Example: test runner + +When running the test runner on a solution, its output is a `results.json` file. +We can then compare this file against a "known good" (i.e. "expected") output file (named `expected_results.json`) to check if the test runner works as intended. + ## Safety Safety is a main reason why we're using Docker containers to run our tooling. From 120a4e2271081b19e5be7786cb4c487285a2c0cf Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 14 Aug 2024 14:42:48 +0200 Subject: [PATCH 091/129] Fix outdated Docker instructions (#559) --- building/tooling/analyzers/docker.md | 43 +++++++++++++++++++++---- building/tooling/representers/docker.md | 41 ++++++++++++++++++++--- building/tooling/test-runners/docker.md | 41 ++++++++++++++++++++--- 3 files changed, 109 insertions(+), 16 deletions(-) diff --git a/building/tooling/analyzers/docker.md b/building/tooling/analyzers/docker.md index 71a357a0..6cc82433 100644 --- a/building/tooling/analyzers/docker.md +++ b/building/tooling/analyzers/docker.md @@ -2,12 +2,43 @@ Our Analyzers are deployed as Docker images. -Please read the [general tooling Docker information](/docs/building/tooling/docker) to familiarize yourself with how these work. +Please read the [general Tooling docker information](/docs/building/tooling/docker) to familiarize yourself with how these work. -When we run the Analyzer's container we execute a `run.sh` script. -To ensure this works properly the following rules must be following: +## Integration +When we run an Analyzer to analyze a solution, we run a Docker command that looks like this: + +```shell +docker run \ + --rm \ + --network none \ + --mount type=bind,src="https://github.com/$%7Bsolution_dir%7D",dst=/solution \ + --mount type=bind,src="https://github.com/$%7Boutput_dir%7D",dst=/output \ + --tmpfs /tmp:rw \ + "exercism/${track_slug}-analyzer" \ + "${exercise_slug}" "/solution" "/output" +``` + +You can see that we pass three arguments to the Docker image: + +1. The exercise slug +2. The path to the solution's directory +3. The path to the output directory + +## Conventions + +All our Analyzers use the following conventions in their implementation: + +- The `Dockerfile` is in the repo's root directory. - The working directory should be `/opt/analyzer`. -- There should be a `/opt/analyzer/bin/run.sh` script that can be called with 3 parameters: - the `exercise slug`, the path to the `solution folder`, and the path to the `output folder`. - For more information see [The Interface](/docs/building/tooling/analyzers/interface). +- The entrypoint is `/opt/analyzer/bin/run.sh` +- The `/opt/analyzer/bin/run.sh` script takes 3 arguments: + 1. The exercise slug + 2. The path to the solution's directory + 3. The path to the output directory + +For more information see [The Interface](/docs/building/tooling/analyzer/interface). + +```exercism/note +New analyzer repos will use these conventions by default. +``` diff --git a/building/tooling/representers/docker.md b/building/tooling/representers/docker.md index 91bd0f93..0c377747 100644 --- a/building/tooling/representers/docker.md +++ b/building/tooling/representers/docker.md @@ -4,10 +4,41 @@ Our Representers are deployed as Docker images. Please read the [general Tooling docker information](/docs/building/tooling/docker) to familiarize yourself with how these work. -When we run the Representer's container we execute a `run.sh` script. -To ensure this works properly the following rules must be following: +## Integration +When we run a Representer to create a solution representation, we run a Docker command that looks like this: + +```shell +docker run \ + --rm \ + --network none \ + --mount type=bind,src="https://github.com/$%7Bsolution_dir%7D",dst=/solution \ + --mount type=bind,src="https://github.com/$%7Boutput_dir%7D",dst=/output \ + --tmpfs /tmp:rw \ + "exercism/${track_slug}-representer" \ + "${exercise_slug}" "/solution" "/output" +``` + +You can see that we pass three arguments to the Docker image: + +1. The exercise slug +2. The path to the solution's directory +3. The path to the output directory + +## Conventions + +All our Representers use the following conventions in their implementation: + +- The `Dockerfile` is in the repo's root directory. - The working directory should be `/opt/representer`. -- There should be a `/opt/representer/bin/run.sh` script that can be called with 3 parameters: - the `exercise slug`, the path to the `solution folder`, and the path to the `output folder`. - For more information see [The Interface](/docs/building/tooling/representers/interface). +- The entrypoint is `/opt/representer/bin/run.sh` +- The `/opt/representer/bin/run.sh` script takes 3 arguments: + 1. The exercise slug + 2. The path to the solution's directory + 3. The path to the output directory + +For more information see [The Interface](/docs/building/tooling/test-runners/interface). + +```exercism/note +New representer repos will use these conventions by default. +``` diff --git a/building/tooling/test-runners/docker.md b/building/tooling/test-runners/docker.md index f1c25452..c55e7390 100644 --- a/building/tooling/test-runners/docker.md +++ b/building/tooling/test-runners/docker.md @@ -4,10 +4,41 @@ Our Test Runners are deployed as Docker images. Please read the [general Tooling docker information](/docs/building/tooling/docker) to familiarize yourself with how these work. -When we run the Test Runner's container we execute a `run.sh` script. -To ensure this works properly the following rules must be following: +## Integration +When we run a Test Runner to test a solution, we run a Docker command that looks like this: + +```shell +docker run \ + --rm \ + --network none \ + --mount type=bind,src="https://github.com/$%7Bsolution_dir%7D",dst=/solution \ + --mount type=bind,src="https://github.com/$%7Boutput_dir%7D",dst=/output \ + --tmpfs /tmp:rw \ + "exercism/${track_slug}-test-runner" \ + "${exercise_slug}" "/solution" "/output" +``` + +You can see that we pass three arguments to the Docker image: + +1. The exercise slug +2. The path to the solution's directory +3. The path to the output directory + +## Conventions + +All our test runners use the following conventions in their implementation: + +- The `Dockerfile` is in the repo's root directory. - The working directory should be `/opt/test-runner`. -- There should be a `/opt/test-runner/bin/run.sh` script that can be called with 3 parameters: - the `exercise slug`, the path to the `solution folder`, and the path to the `output folder`. - For more information see [The Interface](/docs/building/tooling/test-runners/interface). +- The entrypoint is `/opt/test-runner/bin/run.sh` +- The `/opt/test-runner/bin/run.sh` script takes 3 arguments: + 1. The exercise slug + 2. The path to the solution's directory + 3. The path to the output directory + +For more information see [The Interface](/docs/building/tooling/test-runners/interface). + +```exercism/note +New test runner repos will use these conventions by default. +``` From e2dc03e5edd9293c88f02db70a2a31e7c23456c3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 14 Aug 2024 14:48:22 +0200 Subject: [PATCH 092/129] Improve heading --- building/tracks/new/prepare-for-launch.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/tracks/new/prepare-for-launch.md b/building/tracks/new/prepare-for-launch.md index d23e0521..df5be687 100644 --- a/building/tracks/new/prepare-for-launch.md +++ b/building/tracks/new/prepare-for-launch.md @@ -14,9 +14,9 @@ In order to design an icon for the site, open a new issue in the [exercism/websi - please include a link to an example - please list the attribution requirements of that logo -## Add docs +## Add track docs -Add the following markdown documents: +Add the following track documents: - `docs/ABOUT.md`: a short introduction to the language. - `docs/INSTALLATION.md`: describe what the student needs to install to allow working on the track on their local system using the CLI. From 3951d074cd58c32235050aa4748de3c85b672118 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 15 Aug 2024 12:29:01 +0200 Subject: [PATCH 093/129] Start documenting workflows (#560) --- building/config.json | 6 ++++ building/configlet/README.md | 8 ++++- building/configlet/format.md | 9 ++++-- building/tracks/ci/workflows.md | 8 +++-- building/tracks/ci/workflows/configlet.md | 32 +++++++++++++++++++ building/tracks/new/add-first-exercise.md | 30 +++++++++++++++++ .../new/setup-continuous-integration.md | 8 +++-- 7 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 building/tracks/ci/workflows/configlet.md diff --git a/building/config.json b/building/config.json index 2a7de427..85726175 100644 --- a/building/config.json +++ b/building/config.json @@ -667,6 +667,12 @@ "path": "building/tracks/ci/workflows.md", "title": "Workflows" }, + { + "uuid": "753dfe68-af5f-466e-b9a1-5797b6088ab0", + "slug": "tracks/ci/workflows/configlet", + "path": "building/tracks/ci/workflows/configlet.md", + "title": "configlet workflow" + }, { "uuid": "191b0fa1-96e2-48a6-ad2e-c34f57443799", "slug": "tracks/ci/migrating-from-travis", diff --git a/building/configlet/README.md b/building/configlet/README.md index 03ba944c..bf295621 100644 --- a/building/configlet/README.md +++ b/building/configlet/README.md @@ -51,7 +51,13 @@ How to generate UUIDs can be found [here](/docs/building/configlet/uuid). ## Formatting Configlet has a `fmt` command to help with consistent formatting of the JSON files in the track repo. -The `fmt` command currently only operates on the exercise `.meta/config.json` files but it is likely to operate on all the track JSON files in the future. +The `fmt` command formats the following files: + +- `config.json` +- `exercises/{concept,practice}/*/.approaches/config.json` +- `exercises/{concept,practice}/*/.articles/config.json` +- `exercises/{concept,practice}/*/.meta/config.json` + You can learn more about the format command [here](/docs/building/configlet/format). ## Installation diff --git a/building/configlet/format.md b/building/configlet/format.md index d9f444d5..a939becf 100644 --- a/building/configlet/format.md +++ b/building/configlet/format.md @@ -3,14 +3,17 @@ An Exercism track repo has many JSON files, including: - The track `config.json` file. - - For each concept, a `.meta/config.json` and `links.json` file. - - For each Concept Exercise or Practice Exercise, a `.meta/config.json` file. These files are more readable if they have a consistent formatting Exercism-wide, and so configlet has a `fmt` command for rewriting a track's JSON files in a canonical form. -The `fmt` command currently operates on the exercise `.meta/config.json` files and the track `config.json` file, but it is likely to operate on all the track JSON files in the future. +The `fmt` command formats the following files: + +- `config.json` +- `exercises/{concept,practice}/*/.approaches/config.json` +- `exercises/{concept,practice}/*/.articles/config.json` +- `exercises/{concept,practice}/*/.meta/config.json` ## Usage diff --git a/building/tracks/ci/workflows.md b/building/tracks/ci/workflows.md index 93c4d68e..4e81eebd 100644 --- a/building/tracks/ci/workflows.md +++ b/building/tracks/ci/workflows.md @@ -15,7 +15,11 @@ For information on workflows, check the following docs: ## Shared workflows Some workflows are shared across repositories. -These workflows _should not be changed_. + +```exercism/caution +The shared workflows are automatically synced (from the [org-wide-files repo](https://github.com/exercism/org-wide-files/)). +You should thus not manually change their contents. +``` ### General workflows @@ -23,7 +27,7 @@ These workflows _should not be changed_. ### Track-specific workflows -- `configlet.yml`: runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically +- [`configlet.yml`](/docs/building/tracks/ci/workflows/configlet): runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically - `no-important-files-changed.yml`: checks if pull requests would cause all existing solutions of one or more changes exercises to be re-run - `test.yml`: verify the track's exercises diff --git a/building/tracks/ci/workflows/configlet.md b/building/tracks/ci/workflows/configlet.md new file mode 100644 index 00000000..dca0813e --- /dev/null +++ b/building/tracks/ci/workflows/configlet.md @@ -0,0 +1,32 @@ +# configlet workflow + +The `configlet` workflow is defined in the `.github/workflows/configlet.yml` file. +It uses the [configlet tool](/docs/building/configlet) to check if a track's (configuration) files are properly structured - both syntactically and semantically. +It does this by running [`configlet lint`](/docs/building/configlet/lint). + +## Enable checking file formatting + +Tracks can use [`configlet fmt`](/docs/building/configlet/format) to format the track's various configuration files. +The same command can also be used to check if all configuration files are formatted correctly. + +The `configlet` workflow supports verifying whether the configuration files are formatted correctly, but this is optional and disabled by default. +To opt-into verifying configuration files' formatting, follow these steps: + +1. Create a `.github/org-wide-files-config.toml` file with the following contents + +```toml +[configlet] +fmt = true +``` + +2. After this file is merged into `main`, a PR will automatically be opened that modifies the `configlet.yml` workflow as follows: + +```diff + jobs: + configlet: + uses: exercism/github-actions/.github/workflows/configlet.yml@main ++ with: ++ fmt: true +``` + +3. Once this PR is merged, the `configlet` workflow will also verify the track's configuration files' formatting. diff --git a/building/tracks/new/add-first-exercise.md b/building/tracks/new/add-first-exercise.md index 00a61935..d7c28f12 100644 --- a/building/tracks/new/add-first-exercise.md +++ b/building/tracks/new/add-first-exercise.md @@ -113,5 +113,35 @@ Tip: just can copy-paste-modify the example solution. Once you're done with the exercise, please add your your GitHub username to the `"authors"` array in the exercise's `.meta/config.json` file. This will ensure we correctly credit you with having created the exercise. +### Linting + +To verify that the exercise is setup correctly, you can use the [configlet tool](/docs/building/configlet)'s built-in linting functionality. + +The first step is to fetch the `configlet` tool, for which we've created two scripts: + +- `bin/fetch-configlet`: run this when using \*nix or macOS +- `bin/fetch-configlet.ps1`: run this when using Windows + +Running one of these scripts from the root directory of the track's repo will download the `bin/configlet` respectively `bin/configlet.exe` binary. + +You can then check the exercise for correctness by running [`bin/configlet lint`](/docs/building/configlet/lint). + +````exercism/note +It is likely that `configlet` will report the following error: +```shell +The `tags` array is empty: +/path/to/track/config.json +``` + +This error will be fixed in the [Prepare for launch](/docs/building/tracks/new/prepare-for-launch#h-update-metadata) step, so either: + +- ignore the error (for now), or +- fix the error by adding tags +```` + +```exercism/note +The [`configlet` workflow](/docs/building/tracks/ci/workflows/configlet) will automatically runs `configlet lint` whenever something is pushed to `main` or to a pull request. +``` + [configlet]: /docs/building/configlet [canonical-data.json]: https://github.com/exercism/problem-specifications/blob/main/exercises/hello-world/canonical-data.json diff --git a/building/tracks/new/setup-continuous-integration.md b/building/tracks/new/setup-continuous-integration.md index 469dbbcf..a3055bae 100644 --- a/building/tracks/new/setup-continuous-integration.md +++ b/building/tracks/new/setup-continuous-integration.md @@ -8,10 +8,14 @@ Exercism repos (including track repos) use [GitHub Actions](https://docs.github. GitHub Actions are based on _workflows_, which define scripts to run automatically whenever a specific event occurs (e.g. pushing a commit). For more information on GitHub Actions workflows, check the [workflows docs](/docs/building/tracks/ci/workflows). +## Pre-installed workflows + +Tracks come pre-installed with a number of workflows, most of which you should _not_ modify (they're called _shared workflows_). +There is one workflow that you _should_ change though, which is the `test.yml` workflow. + ## Test workflow -Each track comes with a `test.yml` workflow. -The goal of this workflow is to verify that the track's exercises are in proper shape. +The goal of the `test.yml` workflow is to verify that the track's exercises are in proper shape. The workflow is setup to run automatically (in GitHub Actions terminology: is _triggered_) when a push is made to the `main` branch or to a pull request's branch. The workflow itself should not do much, except for: From 74db5fdea6479f55b98d48b50fae45ecbfe9b738 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 15 Aug 2024 12:43:06 +0200 Subject: [PATCH 094/129] Add sync-labels workflow --- building/config.json | 6 ++++++ building/tracks/ci/workflows.md | 2 +- building/tracks/ci/workflows/sync-labels.md | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 building/tracks/ci/workflows/sync-labels.md diff --git a/building/config.json b/building/config.json index 85726175..b19ba555 100644 --- a/building/config.json +++ b/building/config.json @@ -673,6 +673,12 @@ "path": "building/tracks/ci/workflows/configlet.md", "title": "configlet workflow" }, + { + "uuid": "fd512058-4525-4e14-a912-9df870025989", + "slug": "tracks/ci/workflows/sync-labels", + "path": "building/tracks/ci/workflows/sync-labels.md", + "title": "Sync labels workflow" + }, { "uuid": "191b0fa1-96e2-48a6-ad2e-c34f57443799", "slug": "tracks/ci/migrating-from-travis", diff --git a/building/tracks/ci/workflows.md b/building/tracks/ci/workflows.md index 4e81eebd..772bef4c 100644 --- a/building/tracks/ci/workflows.md +++ b/building/tracks/ci/workflows.md @@ -23,7 +23,7 @@ You should thus not manually change their contents. ### General workflows -- `sync-labels.yml`: automatically syncs the repository's labels from a `labels.yml` file +- [`sync-labels.yml`](/docs/building/tracks/ci/workflows/sync-labels): automatically syncs the repository's labels from a `labels.yml` file ### Track-specific workflows diff --git a/building/tracks/ci/workflows/sync-labels.md b/building/tracks/ci/workflows/sync-labels.md new file mode 100644 index 00000000..1c3f501c --- /dev/null +++ b/building/tracks/ci/workflows/sync-labels.md @@ -0,0 +1,21 @@ +# Sync labels workflow + +The sync labels workflow is defined in the `.github/workflows/sync-labels.yml` file. +The goal of this workflow is to add/update/delete the repository's [GitHub labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels). +The labels themselves are defined in the `.github/labels.yml` file. +Whenever the `.github/labels.yml` file changes, the sync labels workflow will automatically update the repository's labels. + +## Customizing labels + +The labels defined in `.github/labels.yml` come in two forms: + +- Shared labels: these are labels used by _all_ Exercism repositories, and they're defined in the [org-wide-files repo](https://github.com/exercism/org-wide-files/blob/main/global-files/.github/labels.yml) +- Track-specific labels: these are defined in an `.appends/.github/labels.yml` file + +To add track-specific labels, you'll need to add them to the `.appends/.github/labels.yml` file (see [this example](https://github.com/exercism/python/blob/main/.appends/.github/labels.yml)). +Once you've merged the changes to `main`, a pull request will automatically be opened to change the `.github/labels.yml` file. +After merging that PR, the labels will be automatically updated (see description above). + +```exercism/caution +Never manually edit the `.github/labels.yml` file, as those changes will be overwritten the next time labels are synced. +``` From 5b4989933b1b64a02eaf02d2ee0feeb2968b65c5 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 15 Aug 2024 14:34:52 +0200 Subject: [PATCH 095/129] Document workflows (#561) --- building/config.json | 90 +++++++++++-------- building/tracks/README.md | 2 +- building/tracks/ci/workflows.md | 20 +++-- building/tracks/ci/workflows/configlet.md | 7 +- building/tracks/ci/workflows/deploy.md | 12 +++ .../workflows/no-important-files-changed.md | 13 +++ .../pause-community-contributions.md | 18 ++++ building/tracks/ci/workflows/sync-labels.md | 8 +- 8 files changed, 121 insertions(+), 49 deletions(-) create mode 100644 building/tracks/ci/workflows/deploy.md create mode 100644 building/tracks/ci/workflows/no-important-files-changed.md create mode 100644 building/tracks/ci/workflows/pause-community-contributions.md diff --git a/building/config.json b/building/config.json index b19ba555..1784fece 100644 --- a/building/config.json +++ b/building/config.json @@ -673,6 +673,24 @@ "path": "building/tracks/ci/workflows/configlet.md", "title": "configlet workflow" }, + { + "uuid": "d7e3f615-3796-43a5-a4a2-632200a8cd47", + "slug": "tracks/ci/workflows/deploy", + "path": "building/tracks/ci/workflows/deploy.md", + "title": "Deploy workflow" + }, + { + "uuid": "3a3bea49-9bc0-444c-a95d-93ecc8b0659e", + "slug": "tracks/ci/workflows/no-important-files-changed", + "path": "building/tracks/ci/workflows/no-important-files-changed.md", + "title": "No important files changed workflow" + }, + { + "uuid": "b93743e9-4069-46f6-83e9-f873ab8c80c3", + "slug": "tracks/ci/workflows/pause-community-contributions", + "path": "building/tracks/ci/workflows/pause-community-contributions.md", + "title": "Pause community contributions workflow" + }, { "uuid": "fd512058-4525-4e14-a912-9df870025989", "slug": "tracks/ci/workflows/sync-labels", @@ -685,42 +703,6 @@ "path": "building/tracks/ci/migrating-from-travis.md", "title": "Migrating from Travis" }, - { - "uuid": "40e765ac-3fd1-4c17-8236-7a6e718cfe64", - "slug": "markdown", - "path": "building/markdown/README.md", - "title": "Markdown", - "blurb": "" - }, - { - "uuid": "dd132018-e53d-4347-a30d-aadfe6c1743b", - "slug": "markdown/internal-linking", - "path": "building/markdown/internal-linking.md", - "title": "Internal Linking", - "blurb": "" - }, - { - "uuid": "2ce5a5bf-229f-4757-9ae0-d6826494dbbf", - "slug": "markdown/markdown", - "path": "building/markdown/markdown.md", - "title": "Markdown Specification", - "blurb": "" - }, - { - "uuid": "b2f8687f-77e1-4044-a964-35fd2bd3f1b3", - "slug": "markdown/style-guide", - "path": "building/markdown/style-guide.md", - "title": "Exercism's Style Guide", - "blurb": "" - }, - { - "uuid": "d34ced45-63ff-41c7-967d-3ac72e1b5d8e", - "slug": "markdown/widgets", - "path": "building/markdown/widgets.md", - "title": "Widgets", - "blurb": "" - }, - { "uuid": "00b76de0-1e0e-47d0-b293-950d12b1a4c0", "slug": "tooling", @@ -889,6 +871,42 @@ "title": "Test Generators", "blurb": "" }, + + { + "uuid": "40e765ac-3fd1-4c17-8236-7a6e718cfe64", + "slug": "markdown", + "path": "building/markdown/README.md", + "title": "Markdown", + "blurb": "" + }, + { + "uuid": "dd132018-e53d-4347-a30d-aadfe6c1743b", + "slug": "markdown/internal-linking", + "path": "building/markdown/internal-linking.md", + "title": "Internal Linking", + "blurb": "" + }, + { + "uuid": "2ce5a5bf-229f-4757-9ae0-d6826494dbbf", + "slug": "markdown/markdown", + "path": "building/markdown/markdown.md", + "title": "Markdown Specification", + "blurb": "" + }, + { + "uuid": "b2f8687f-77e1-4044-a964-35fd2bd3f1b3", + "slug": "markdown/style-guide", + "path": "building/markdown/style-guide.md", + "title": "Exercism's Style Guide", + "blurb": "" + }, + { + "uuid": "d34ced45-63ff-41c7-967d-3ac72e1b5d8e", + "slug": "markdown/widgets", + "path": "building/markdown/widgets.md", + "title": "Widgets", + "blurb": "" + }, { "uuid": "3423667a-3b30-4590-9a88-5a30f712f382", "slug": "product", diff --git a/building/tracks/README.md b/building/tracks/README.md index ac2d64fc..61735c16 100644 --- a/building/tracks/README.md +++ b/building/tracks/README.md @@ -102,7 +102,7 @@ csharp ### Avoiding triggering unnecessary test runs -When you merge a track PR that touches an exercise, it triggers the latest published iteration of students' solutions to be re-tested. +When you merge a track PR that touches an exercise, it triggers _all_ the latest published iteration of students' solutions to be re-tested. For popular exercises, this is a _very_ expensive operation (70,000 test runs for Python Hello World as an extreme!). **We encourage you to try and avoid doing this unnecessarily.** diff --git a/building/tracks/ci/workflows.md b/building/tracks/ci/workflows.md index 772bef4c..f79e5639 100644 --- a/building/tracks/ci/workflows.md +++ b/building/tracks/ci/workflows.md @@ -23,23 +23,29 @@ You should thus not manually change their contents. ### General workflows -- [`sync-labels.yml`](/docs/building/tracks/ci/workflows/sync-labels): automatically syncs the repository's labels from a `labels.yml` file +- [Sync labels](/docs/building/tracks/ci/workflows/sync-labels): sync the repository's labels from file ### Track-specific workflows -- [`configlet.yml`](/docs/building/tracks/ci/workflows/configlet): runs the [configlet tool](/docs/building/configlet), which checks if a track's (configuration) files are properly structured - both syntactically and semantically -- `no-important-files-changed.yml`: checks if pull requests would cause all existing solutions of one or more changes exercises to be re-run -- `test.yml`: verify the track's exercises +- [configlet](/docs/building/tracks/ci/workflows/configlet): checks if the track's (configuration) files are properly structured - both syntactically and semantically +- [No important files changed](/docs/building/tracks/ci/workflows/no-important-files-changed): comments on pull requests that would cause re-testing of existing solutions +- [Test](/docs/building/tracks/new/setup-continuous-integration#h-test-workflow): verify the track's exercises ### Tooling-specific workflows -- `deploy.yml`: deploy the tooling Docker image to Docker Hub and ECR +- [Deploy](/docs/building/tracks/ci/workflows/deploy): deploy the track tooling Docker image to Docker Hub and ECR + +### Optional workflows + +There are also some workflows that tracks might opt-into: + +- [Pause community contributions](/docs/building/tracks/ci/workflows/pause-community-contributions): auto-comment on newly issues and PRs created by users who are not member of the Exercism GitHub organisation ## Custom workflows Maintainers are free to add custom workflows to their repos. Examples of such workflows could be: -- Linting of shell scripts ([example](https://github.com/exercism/configlet/blob/3baa09608c8ac327315c887608c13a68ae8ac359/.github/workflows/shellcheck.yml)) -- Auto-commenting on pull requests ([example](https://github.com/exercism/elixir/blob/b737f80cc93fcfdec6c53acb7361819834782470/.github/workflows/pr-comment.yml)) +- [Linting of shell scripts](https://github.com/exercism/configlet/blob/3baa09608c8ac327315c887608c13a68ae8ac359/.github/workflows/shellcheck.yml) +- [Auto-commenting on pull requests](https://github.com/exercism/elixir/blob/b737f80cc93fcfdec6c53acb7361819834782470/.github/workflows/pr-comment.yml) - Etc. diff --git a/building/tracks/ci/workflows/configlet.md b/building/tracks/ci/workflows/configlet.md index dca0813e..2e1a8910 100644 --- a/building/tracks/ci/workflows/configlet.md +++ b/building/tracks/ci/workflows/configlet.md @@ -1,7 +1,6 @@ # configlet workflow -The `configlet` workflow is defined in the `.github/workflows/configlet.yml` file. -It uses the [configlet tool](/docs/building/configlet) to check if a track's (configuration) files are properly structured - both syntactically and semantically. +The configlet uses the [configlet tool](/docs/building/configlet) to check if a track's (configuration) files are properly structured - both syntactically and semantically. It does this by running [`configlet lint`](/docs/building/configlet/lint). ## Enable checking file formatting @@ -30,3 +29,7 @@ fmt = true ``` 3. Once this PR is merged, the `configlet` workflow will also verify the track's configuration files' formatting. + +## Source + +The workflow is defined in the `.github/workflows/configlet.yml` file. diff --git a/building/tracks/ci/workflows/deploy.md b/building/tracks/ci/workflows/deploy.md new file mode 100644 index 00000000..8b5da98f --- /dev/null +++ b/building/tracks/ci/workflows/deploy.md @@ -0,0 +1,12 @@ +# Deploy workflow + +The deploy workflow is used to deploy [track tooling](/docs/building/tooling) to: + +1. [Docker Hub](https://hub.docker.com/): used in scripts or for local testing. + Publicly available. +2. [ECR](https://aws.amazon.com/ecr/): used in the production environment to test submitted solutions. + Private. + +## Source + +The workflow is defined in the `.github/workflows/deploy.yml` file. diff --git a/building/tracks/ci/workflows/no-important-files-changed.md b/building/tracks/ci/workflows/no-important-files-changed.md new file mode 100644 index 00000000..f44589e5 --- /dev/null +++ b/building/tracks/ci/workflows/no-important-files-changed.md @@ -0,0 +1,13 @@ +# No important files changed workflow + +When a track PR is merged that touches an exercise, it triggers _all_ the latest published iteration of students' solutions to be re-tested. +For popular exercises, this is a _very_ expensive operation (70,000 test runs for Python Hello World as an extreme!). + +This workflow checks if the changes in a PR would trigger the re-testing of solutions, and if so, it adds a comment explaining the risk of merging the PR _as is_. +It also explains how to merge the PR without re-testing solutions. + +For more information, check the [Avoiding triggering unnecessary test runs](https://exercism.org/docs/building/tracks#h-avoiding-triggering-unnecessary-test-runs) documentation. + +## Source + +The workflow is defined in the `.github/workflows/no-important-files-changed.yml` file. diff --git a/building/tracks/ci/workflows/pause-community-contributions.md b/building/tracks/ci/workflows/pause-community-contributions.md new file mode 100644 index 00000000..4b2ec6b4 --- /dev/null +++ b/building/tracks/ci/workflows/pause-community-contributions.md @@ -0,0 +1,18 @@ +# Pause community contributions + +Some repositories don't have the resources to triage/review issues or pull requests from non-maintainer users. +The pause community solutions +If a repository has enabled this workflow, it will automatically comment on a newly opened issue or pull request if the author is _not_ member of Exercism's GitHub organisation. +The comment will suggest the user to first open a topic on the Exercism forum. + +## Enabling the workflow + +Add the [.github/workflows/pause-community-contributions.yml file](https://github.com/exercism/github-actions/blob/b5424c17f661f5529493258a1ad480013351aa9e/.github/workflows/pause-community-contributions.yml) into your repository. + +## Disabling the workflow + +Remove the workflow file from your repository. + +## Source + +The workflow is defined in the `.github/workflows/pause-community-contributions.yml` file. diff --git a/building/tracks/ci/workflows/sync-labels.md b/building/tracks/ci/workflows/sync-labels.md index 1c3f501c..b697c2c9 100644 --- a/building/tracks/ci/workflows/sync-labels.md +++ b/building/tracks/ci/workflows/sync-labels.md @@ -1,8 +1,6 @@ # Sync labels workflow -The sync labels workflow is defined in the `.github/workflows/sync-labels.yml` file. -The goal of this workflow is to add/update/delete the repository's [GitHub labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels). -The labels themselves are defined in the `.github/labels.yml` file. +The sync labels workflow synchronizes a repository's [GitHub labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels) with the contents of the `.github/labels.yml` file. Whenever the `.github/labels.yml` file changes, the sync labels workflow will automatically update the repository's labels. ## Customizing labels @@ -19,3 +17,7 @@ After merging that PR, the labels will be automatically updated (see description ```exercism/caution Never manually edit the `.github/labels.yml` file, as those changes will be overwritten the next time labels are synced. ``` + +## Source + +The workflow is defined in the `.github/workflows/sync-labels.yml` file. From dd4edbc52032cc42da74a614b63b0388abb7909a Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 15 Aug 2024 14:37:13 +0200 Subject: [PATCH 096/129] Tweak title of Docker setup --- building/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/config.json b/building/config.json index 1784fece..206b8d67 100644 --- a/building/config.json +++ b/building/config.json @@ -714,7 +714,7 @@ "uuid": "fb41198b-90ac-4148-afcc-f012418c6396", "slug": "tooling/docker", "path": "building/tooling/docker.md", - "title": "Tooling Docker Setup", + "title": "Docker Setup", "blurb": "" }, { From 9ab126b733f7e16cb85acee0b4b82f9985f0a8cb Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 16 Aug 2024 12:28:36 +0200 Subject: [PATCH 097/129] Add measuring section to performance (#562) --- building/tooling/best-practices.md | 23 +++++++++++++++++++++++ building/tooling/docker.md | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md index bf148dfc..84feb825 100644 --- a/building/tooling/best-practices.md +++ b/building/tooling/best-practices.md @@ -9,6 +9,29 @@ The official [Dockerfile best practices](https://docs.docker.com/develop/develop You should primarily optimize for performance (especially for test runners). This will ensure your tooling runs as fast as possible and does not time-out. +### Measure + +Measuring execution time often is a great way to get a feel for the performance of tooling. +Make it a habit to measure execution time both after _and_ before a change. +Even when you feel "certain" that a change will improve performance, you should still measure execution time. + +#### Scripts + +When possible, create scripts to automatically measure performance (also known as _benchmarking_). +A very helpful command-line tool is [hyperfine](https://github.com/sharkdp/hyperfine), but feel free to use whatever makes the most sense for your tooling. + +Newer track tooling repos will have access to the following two scripts: + +1. `./bin/benchmark.sh`: benchmark the track tooling code ([source code](https://github.com/exercism/generic-test-runner/blob/a6886f4d84d2a2030f766a658c334bbfbe97b79c/bin/benchmark.sh)) +2. `./bin/benchmark-in-docker.sh`: benchmark the track tooling Docker image ([source code](https://github.com/exercism/generic-test-runner/blob/a6886f4d84d2a2030f766a658c334bbfbe97b79c/bin/benchmark-in-docker.sh)) + +If you're working on a track tooling repo without these files, feel free to copy them into your repo. + +```exercism/caution +Benchmarking scripts can help estimate the tooling's performance. +Bear in mind though that the performance on Exercism's production servers is often lower. +``` + ### Experiment with different Base images Try experimenting with different base images (e.g. Alpine instead of Ubuntu), to see if one (significantly) outperforms the other. diff --git a/building/tooling/docker.md b/building/tooling/docker.md index 5cadccc2..bc4060cd 100644 --- a/building/tooling/docker.md +++ b/building/tooling/docker.md @@ -15,6 +15,8 @@ Our [Best Practices page](/docs/building/tooling/best-practices) has lots of tip The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out with a 408 error code. +We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeout occuring. + ### Stdout/stderr A tooling run may produce up to a maximum of one-megabyte of stdout and stderr. @@ -35,7 +37,6 @@ If the file is larger than this, the tooling run will be killed with a 460 error ## Configuration Each solution gets 100% machine resources for a twenty second window. - After 20 seconds, the process is halted and reports as a time-out. Some tools require (slight) deviations from the default configuration. From a788b4e3a2ed34452f0487c7d20857daec5296a6 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 16 Aug 2024 12:38:24 +0200 Subject: [PATCH 098/129] Link to best practices more (#563) --- building/tooling/analyzers/README.md | 2 +- building/tooling/analyzers/creating-from-scratch.md | 5 +++-- building/tooling/analyzers/interface.md | 4 ++++ building/tooling/docker.md | 4 +++- building/tooling/representers/README.md | 2 +- building/tooling/representers/creating-from-scratch.md | 5 +++-- building/tooling/representers/interface.md | 8 +++++++- building/tooling/test-runners/creating-from-scratch.md | 5 +++-- building/tooling/test-runners/interface.md | 4 ++++ 9 files changed, 29 insertions(+), 10 deletions(-) diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index 3454dd82..c12fa575 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -18,7 +18,7 @@ You can use the following documents to learn more about building an analyzer: - [Creating a Analyzer from scratch](/docs/building/tooling/analyzers/creating-from-scratch) - [The Analyzer interface.](/docs/building/tooling/analyzers/interface) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/analyzers/docker) +- [Best practices](/docs/building/tooling/best-practices) - [Writing Analyzer comments](/docs/building/tooling/analyzers/comments) - [Tagging solutions](/docs/building/tooling/analyzers/tags) - [Guidance for building an Analyzer](/docs/building/tooling/analyzers/guidance) -- [Best practices](/docs/building/tooling/best-practices) diff --git a/building/tooling/analyzers/creating-from-scratch.md b/building/tooling/analyzers/creating-from-scratch.md index a95dc1a0..aac9c114 100644 --- a/building/tooling/analyzers/creating-from-scratch.md +++ b/building/tooling/analyzers/creating-from-scratch.md @@ -7,9 +7,10 @@ These are the steps to get going: 1. Check [our repository list for an existing `...-analyzer`](https://github.com/search?q=org%3Aexercism+analyzer&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/analyzers) to ensure you are comfortable with the idea of creating an Analyzer. 3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create an Analyzer for. -4. Once an Analyzer repo has been created, use [the Analyzer interface document](/docs/building/tooling/analyzers/interface) to help guide your implementation. +4. Once an Analyzer repo has been created for you, use [the Analyzer interface](/docs/building/tooling/analyzers/interface) and [Best practices](/docs/building/tooling/best-practices) documents to help guide your implementation. -We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 +We have an incredibly friendly and supportive community who will be happy to help you as you work through this! +If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 [building-exercism]: https://forum.exercism.org/c/exercism/building-exercism/125 [exercism-repo]: https://github.com/exercism/exercism diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index f4f69660..45ffaf78 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -17,6 +17,10 @@ All interactions with the Exercism website are handled automatically. Analyzers The analyzer gets 100% machine resources for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out. +```exercism/note +We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeouts. +``` + ## Output format ### analysis.json diff --git a/building/tooling/docker.md b/building/tooling/docker.md index bc4060cd..d024242c 100644 --- a/building/tooling/docker.md +++ b/building/tooling/docker.md @@ -15,7 +15,9 @@ Our [Best Practices page](/docs/building/tooling/best-practices) has lots of tip The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out with a 408 error code. -We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeout occuring. +```exercism/note +We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeouts. +``` ### Stdout/stderr diff --git a/building/tooling/representers/README.md b/building/tooling/representers/README.md index 67d3d224..fe9b77db 100644 --- a/building/tooling/representers/README.md +++ b/building/tooling/representers/README.md @@ -33,6 +33,6 @@ You can use the following documents to learn more about building a representer: - [Creating a Representer from scratch](/docs/building/tooling/representers/creating-from-scratch) - [The Representer interface](/docs/building/tooling/representers/interface) -- [How to normalize representations for the highest efficiency](/docs/building/tooling/representers/normalization) - [How to build a Docker image with Docker for local testing and deployment](/docs/building/tooling/representers/docker) - [Best practices](/docs/building/tooling/best-practices) +- [How to normalize representations for the highest efficiency](/docs/building/tooling/representers/normalization) diff --git a/building/tooling/representers/creating-from-scratch.md b/building/tooling/representers/creating-from-scratch.md index 759caab5..de523b4b 100644 --- a/building/tooling/representers/creating-from-scratch.md +++ b/building/tooling/representers/creating-from-scratch.md @@ -7,9 +7,10 @@ These are the steps to get going: 1. Check [our repository list for an existing `...-representer`](https://github.com/search?q=org%3Aexercism+representer&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/representers) to ensure you are comfortable with the idea of creating an Representer. 3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create a Representer for. -4. Once a Representer repo has been created, use [the Representer interface document](/docs/building/tooling/representers/interface) to help guide your implementation. +4. Once a Representer repo has been created for you, use [the Representer interface](/docs/building/tooling/representers/interface) and [Best practices](/docs/building/tooling/best-practices) documents to help guide your implementation. -We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 +We have an incredibly friendly and supportive community who will be happy to help you as you work through this! +If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 [building-exercism]: https://forum.exercism.org/c/exercism/building-exercism/125 [exercism-repo]: https://github.com/exercism/exercism diff --git a/building/tooling/representers/interface.md b/building/tooling/representers/interface.md index ff3824a3..760bed87 100644 --- a/building/tooling/representers/interface.md +++ b/building/tooling/representers/interface.md @@ -1,6 +1,8 @@ # The Representer Interface -All interactions with the Exercism website are handled automatically. Representers have the single responsibility of taking a solution and returning a representation of it. See the [introduction](/docs/building/tooling/representers#introduction) for more information. +All interactions with the Exercism website are handled automatically. +Representers have the single responsibility of taking a solution and returning a representation of it. +See the [introduction](/docs/building/tooling/representers#introduction) for more information. ## Execution @@ -18,6 +20,10 @@ All interactions with the Exercism website are handled automatically. Represente The representer gets 100% machine resources for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out. +```exercism/note +We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeouts. +``` + ## Output format ### representation.txt diff --git a/building/tooling/test-runners/creating-from-scratch.md b/building/tooling/test-runners/creating-from-scratch.md index cd71dfaa..6a9ec693 100644 --- a/building/tooling/test-runners/creating-from-scratch.md +++ b/building/tooling/test-runners/creating-from-scratch.md @@ -7,9 +7,10 @@ These are the steps to get going: 1. Check [our repository list for an existing `...-test-runner`](https://github.com/search?q=org%3Aexercism+test-runner&type=repositories) to ensure that one doesn't already exist. 2. Scan the [contents of this directory](/docs/building/tooling/test-runners) to ensure you are comfortable with the idea of creating an Test Runner. 3. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create a Test Runner for. -4. Once a Test Runner repo has been created, use [the Test Runner interface document](/docs/building/tooling/test-runners/interface) to help guide your implementation. There is a [generic test runner repository template](https://github.com/exercism/generic-test-runner/) that you can use to kick-start development. +4. Once a Test Runner repo has been created for you, use [the Test Runner interface](/docs/building/tooling/test-runners/interface) and [Best practices](/docs/building/tooling/best-practices) documents to help guide your implementation. -We have an incredibly friendly and supportive community who will be happy to help you as you work through this! If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 +We have an incredibly friendly and supportive community who will be happy to help you as you work through this! +If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 [building-exercism]: https://forum.exercism.org/c/exercism/building-exercism/125 [exercism-repo]: https://github.com/exercism/exercism diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index 83cb1951..04e080a9 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -18,6 +18,10 @@ All interactions with the Exercism website are handled automatically and are not The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out. +```exercism/note +We highly recommend following our [Performance Best Practices document](/docs/building/tooling/best-practices#h-performance) to reduce the chance of timeouts. +``` + ## Output format The following fields are supported in `results.json` files: From af14a71c92015ba567fc30db8790c7674d3f6dee Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 16 Aug 2024 12:42:35 +0200 Subject: [PATCH 099/129] Minor grammar improvement --- building/tooling/best-practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md index 84feb825..2f91d2a6 100644 --- a/building/tooling/best-practices.md +++ b/building/tooling/best-practices.md @@ -306,7 +306,7 @@ FROM alpine RUN groupadd -r myuser && useradd -r -g myuser myuser -# +# RUN USER myuser ``` From 1a35eea731feb147a49b2a25960fca731b84439f Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 16 Aug 2024 12:50:25 +0200 Subject: [PATCH 100/129] Fix URL for track tooling repo overview (#564) --- building/tooling/analyzers/README.md | 4 ++-- building/tooling/representers/README.md | 4 ++-- building/tooling/test-runners/README.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/building/tooling/analyzers/README.md b/building/tooling/analyzers/README.md index c12fa575..f070bfc0 100644 --- a/building/tooling/analyzers/README.md +++ b/building/tooling/analyzers/README.md @@ -7,8 +7,8 @@ Exercism's analyzers automatically assess student's submissions and provide ment Each language has its own Analyzer, written in that language. The website acts as the orchestrator between the Analyzers and students' submissions. -Each Analyzer lives in the Exercism GitHub organization in a repository named `$LANG-analyzer` (e.g. `ruby-analyzer`). -You can explore the different Analyzers [here](https://github.com/exercism?q=-analyzer). +Each Analyzer lives in the Exercism GitHub organization in a repository named `-analyzer` (e.g. [`exercism/ruby-analyzer`](https://github.com/exercism/ruby-analyzer)). +You can check out the existing Analyzers [here](https://github.com/search?q=topic%3Aexercism-analyzer&type=repositories). If you would like to get involved in helping with an existing Analyzer, please open an issue in its repository asking if there is somewhere you can help. If you would like to create an Analyzer for a language that currently does not have one, please follow the [creating a Analyzer](/docs/building/tooling/analyzers/creating-from-scratch) instructions. diff --git a/building/tooling/representers/README.md b/building/tooling/representers/README.md index fe9b77db..a2f78f1b 100644 --- a/building/tooling/representers/README.md +++ b/building/tooling/representers/README.md @@ -23,8 +23,8 @@ A notification will be sent for old solutions with a matching representation. Each language has its own representer, written in that language. The website acts as the orchestrator between the representer and students' submissions. -Each representer lives in the Exercism GitHub organization in a repository named `$LANG-representer` (e.g. `ruby-representer`). -You can explore the different representers [here](https://github.com/exercism?q=-representer). +Each representer lives in the Exercism GitHub organization in a repository named `-representer` (e.g. [`exercism/ruby-representer`](https://github.com/exercism/ruby-representer)). +You can check out the existing representers [here](https://github.com/search?q=topic%3Aexercism-representer&type=repositories). If you would like to get involved in helping with an existing Representer, please open an issue in its repository asking if there is somewhere you can help. If you would like to create a Representer for a language that currently does not have one, please follow the [creating a Representer](/docs/building/tooling/representers/creating-from-scratch) instructions. diff --git a/building/tooling/test-runners/README.md b/building/tooling/test-runners/README.md index 2642d219..2fb9bb17 100644 --- a/building/tooling/test-runners/README.md +++ b/building/tooling/test-runners/README.md @@ -12,8 +12,8 @@ Test Runners give us two advantages: Each language has its own Test Runner, written in that language. The website acts as the orchestrator between the Test Runners and students' submissions. -Each Test Runner lives in the Exercism GitHub organization in a repository named `$LANG-test-runner` (e.g. [`exercism/ruby-test-runner`](https://github.com/exercism/ruby-test-runner)). -You can explore the different Test Runners [here](https://github.com/exercism?q=-test-runner). +Each Test Runner lives in the Exercism GitHub organization in a repository named `-test-runner` (e.g. [`exercism/ruby-test-runner`](https://github.com/exercism/ruby-test-runner)). +You can check out the existing Test Runners [here](https://github.com/search?q=topic%3Aexercism-test-runner&type=repositories). If you would like to get involved in helping with an existing Test Runner, please open an issue in its repository asking if there is somewhere you can help. If you would like to create a Test Runner for a language that currently does not have one, please follow the [creating a Test Runner](/docs/building/tooling/test-runners/creating-from-scratch) instructions. From 5f4352e153bb9cef20a74a2b375422a34b7370fe Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Tue, 20 Aug 2024 10:35:04 +0200 Subject: [PATCH 101/129] Fix broken link (#556) --- building/tooling/analyzers/interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tooling/analyzers/interface.md b/building/tooling/analyzers/interface.md index 45ffaf78..d8207a41 100644 --- a/building/tooling/analyzers/interface.md +++ b/building/tooling/analyzers/interface.md @@ -127,7 +127,7 @@ You may write an `analysis.out` file that contains debugging information you wan Before building an analyzer, please read our [Analyzer Guidance][analyzer-guidance]. -[website-copy-repo]: https://github.com/exercise/website-copy +[website-copy-repo]: https://github.com/exercism/website-copy [writing-analyzer-comments]: /docs/building/tooling/analyzers/comments [tagging-solutions]: /docs/building/tooling/analyzers/tags [analyzer-guidance]: /docs/building/tooling/analyzers/guidance From 361a016531af556a8d65546d69ff45700f3777d3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 20 Aug 2024 10:35:17 +0200 Subject: [PATCH 102/129] Document adding docs (#553) --- building/tracks/docs.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/building/tracks/docs.md b/building/tracks/docs.md index 4f994e12..a8d04b37 100644 --- a/building/tracks/docs.md +++ b/building/tracks/docs.md @@ -368,3 +368,24 @@ Check [this page](https://exercism.org/docs/tracks/fsharp/tests) to see what thi ] } ``` + +## Adding documents + +It is possible to add additional documents should you so desire. +To do so, you need to do two things: + +1. Create the document within the `docs` directory (e.g. `docs/PACKAGES.md`) +2. Add an entry to the `docs/config.json` file for the new document. + For example: + +```json +{ + "uuid": "6268fd29-55e2-44e6-afc2-f86671fe799f", + "slug": "packages", + "path": "docs/PACKAGES.md", + "title": "Supported packages", + "blurb": "List of the supported packages" +} +``` + +Once merged to `main`, the document can be found at `https://exercism.org/docs/tracks//` (e.g. `https://exercism.org/docs/tracks/fsharp/packages`) From 8f0290dff385a4d80878e403fdaff90f9e621f70 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 20 Aug 2024 10:53:22 +0200 Subject: [PATCH 103/129] Add script to add practice exercise (#565) --- building/tooling/best-practices.md | 8 ++++-- building/tracks/new/add-first-exercise.md | 29 ++++++++++++++++++-- building/tracks/new/add-initial-exercises.md | 15 +++++++--- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md index 2f91d2a6..2193c474 100644 --- a/building/tooling/best-practices.md +++ b/building/tooling/best-practices.md @@ -22,10 +22,12 @@ A very helpful command-line tool is [hyperfine](https://github.com/sharkdp/hyper Newer track tooling repos will have access to the following two scripts: -1. `./bin/benchmark.sh`: benchmark the track tooling code ([source code](https://github.com/exercism/generic-test-runner/blob/a6886f4d84d2a2030f766a658c334bbfbe97b79c/bin/benchmark.sh)) -2. `./bin/benchmark-in-docker.sh`: benchmark the track tooling Docker image ([source code](https://github.com/exercism/generic-test-runner/blob/a6886f4d84d2a2030f766a658c334bbfbe97b79c/bin/benchmark-in-docker.sh)) +1. `./bin/benchmark.sh`: benchmark the track tooling code ([source code](https://github.com/exercism/generic-test-runner/blob/main/bin/benchmark.sh)) +2. `./bin/benchmark-in-docker.sh`: benchmark the track tooling Docker image ([source code](https://github.com/exercism/generic-test-runner/blob/main/bin/benchmark-in-docker.sh)) -If you're working on a track tooling repo without these files, feel free to copy them into your repo. +```exercism/note +If you're working on a track tooling repo without these files, feel free to copy them into your repo using the above source links. +``` ```exercism/caution Benchmarking scripts can help estimate the tooling's performance. diff --git a/building/tracks/new/add-first-exercise.md b/building/tracks/new/add-first-exercise.md index d7c28f12..4d62bbb0 100644 --- a/building/tracks/new/add-first-exercise.md +++ b/building/tracks/new/add-first-exercise.md @@ -79,6 +79,33 @@ bin/fetch-configlet bin/configlet create --practice-exercise hello-world ``` +### Set author + +To have the website list you as the exercise's author, follow these steps: + +Within the exercise's `.meta/config.json` file: + +- Add your GitHub username to the `authors` key + +For this to work, you'll need link your Exercism account to GitHub. +You can do this on the website in the [Settings page's Integrations section](https://exercism.org/settings/integrations). + +```exercism/note +Exercise authors are also awarded [reputation](/docs/using/product/reputation) +``` + +### Use script + +Newer track repos can use the `bin/add-practice-exercise` script ([source](https://github.com/exercism/generic-track/blob/main/bin/add-practice-exercise)) to add new exercises: + +```shell +bin/add-exercise -a two-fer +``` + +```exercism/note +If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. +``` + ### Implement exercise Once the scaffolded files have been created, you'll then have to: @@ -86,8 +113,6 @@ Once the scaffolded files have been created, you'll then have to: - Add tests to the tests file - Add an example implementation - Define the stub file's contents -- Within the exercise's `.meta/config.json` file: - - Add the GitHub username of the exercise's authors to the `authors` key #### Add tests diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index 22ed938b..bc0555c5 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -106,14 +106,21 @@ To make this all a bit more concrete, this is what a sample selection of initial ### Scaffold exercise Having selected the exercises you want include in your track, the next step is to implement them. -You can quickly scaffold a new Practice Exercise by running the following commands from the track's root directory: +You can quickly scaffold a new Practice Exercise by running the `bin/add-practice-exercise` script ([source](https://github.com/exercism/generic-track/blob/main/bin/add-practice-exercise)) from the track's root directory: ```shell -bin/fetch-configlet -bin/configlet create --practice-exercise +bin/add-exercise ``` -For more information, check the [`configlet create` docs](/docs/building/configlet/create) +Optionally, you can also specify the exercise's difficulty (via `-d`) and/or author's GitHub username (via `-a`): + +```shell +bin/add-practice-exercise -d 3 -a foobar +``` + +```exercism/note +If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. +``` ### Implement exercise From 164d06d558fe6b9a5e86258ce3461d5d6b6542c6 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 5 Sep 2024 13:19:43 +0200 Subject: [PATCH 104/129] Document maintainers repo permissions (#566) --- building/config.json | 6 +++ building/github/README.md | 2 + .../github/maintainers-repo-permissions.md | 54 +++++++++++++++++++ building/tracks/README.md | 4 ++ 4 files changed, 66 insertions(+) create mode 100644 building/github/maintainers-repo-permissions.md diff --git a/building/config.json b/building/config.json index 206b8d67..a69e9d5c 100644 --- a/building/config.json +++ b/building/config.json @@ -67,6 +67,12 @@ "path": "building/github/maintainers-pull-request-guide.md", "title": "Maintainers Pull Request Guide" }, + { + "uuid": "3598be40-752a-4223-8828-c08dc3320a20", + "slug": "github/maintainers-repo-permissions", + "path": "building/github/maintainers-repo-permissions.md", + "title": "Maintainers Repo Permissions" + }, { "uuid": "7cc6cb11-2502-49ea-b7c1-6e501720c9ae", "slug": "github/gha-best-practices", diff --git a/building/github/README.md b/building/github/README.md index 924038b4..65e0010e 100644 --- a/building/github/README.md +++ b/building/github/README.md @@ -20,6 +20,7 @@ We also have several Exercism-specific Pull Request guides: - [How to make a great Pull Request][how-to-make-a-great-pr] - [The Contributors' Guide to Pull Requests][contributors-pr-guide] - [The Maintainers' Guide to Pull Requests][maintainers-pr-guide] +- [Maintainers Repo Permissions][maintainers-repo-permissions] ## Tooling @@ -40,6 +41,7 @@ There is also GitHub-specific tooling you can use: [great-pr]: https://exercism.org/docs/community/being-a-good-community-member/pull-requests [contributors-pr-guide]: /docs/building/github/contributors-pull-request-guide [maintainers-pr-guide]: /docs/building/github/maintainers-pull-request-guide +[maintainers-repo-permissions]: /docs/building/github/maintainers-repo-permissions [how-to-make-a-great-pr]: /docs/community/being-a-good-community-member/pull-requests [pro-git]: https://git-scm.com/book/en/v2 [git]: https://git-scm.com/ diff --git a/building/github/maintainers-repo-permissions.md b/building/github/maintainers-repo-permissions.md new file mode 100644 index 00000000..00980f2b --- /dev/null +++ b/building/github/maintainers-repo-permissions.md @@ -0,0 +1,54 @@ +# Maintainers Repo Permissions + +A track maintainer is someone who is a member of the track's GitHub team. +Becoming a member of a track team is _invitation only_. + +```exercism/note +If you'd like to become a track maintainer, please open a topic on the [forum](https://forum.exercism.org/c/exercism/building-exercism/125). +``` + +## Maintenance category + +The are five maintenance categories: + +1. `wip-track` +2. `unmaintained` +3. `maintained-solitary` +4. `maintained-autonomous` +5. `maintained` + +A tracks' maintenance category is determined by three variables: + +1. Whether the track is active (i.e. students can join the track on the website) +2. The number of track maintainers +3. The number of track maintainers who are also in the `cross-track-maintainers` GitHub team + +To determine the maintenance category, find the first category that matches the track from this table: + +| Category | Active? | Number of maintainers | Number of cross-track maintainers | +| ----------------------- | ------- | --------------------- | --------------------------------- | +| `wip-track` | No | Any | Any | +| `unmaintained` | Yes | 0 | 0 | +| `maintained-solitary` | Yes | 1 | 0 | +| `maintained-autonomous` | Yes | > 0 | = Number of maintainers | +| `maintained` | Yes | > 0 | < Number of maintainers | + +## Repo permissions + +The maintenance category is used to set the track's GitHub repo(s) permission(s). + +| Category | Requires PR | Requires PR approval | Cross-track team reviews | +| ----------------------- | ----------- | -------------------- | ------------------------ | +| `wip-track` | No | No | No | +| `unmaintained` | Yes | Yes | Yes | +| `maintained-solitary` | Yes | Yes | Yes | +| `maintained-autonomous` | Yes | No | No | +| `maintained` | Yes | No | No | + +```exercism/caution +The `wip-track` category is the only category that allows maintainers to push to `main`. +``` + +```exercism/caution +Tooling repos will _always_ require PR approval, as their contents are protected via a [CODEOWNERS file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners). +``` diff --git a/building/tracks/README.md b/building/tracks/README.md index 61735c16..96b16c1c 100644 --- a/building/tracks/README.md +++ b/building/tracks/README.md @@ -100,6 +100,10 @@ csharp ## Maintenance +### Repo permissions + +Each track is (automatically) assigned a [maintenance category](/docs/building/github/maintainers-repo-permissions#maintenance-category), which determines the [track maintainer's GitHub repo permissions](/docs/building/github/maintainers-repo-permissions#repo-permissions). + ### Avoiding triggering unnecessary test runs When you merge a track PR that touches an exercise, it triggers _all_ the latest published iteration of students' solutions to be re-tested. From 92ea3f9618a957666dcbdc1abd58909a6e98eca2 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 5 Sep 2024 13:30:25 +0200 Subject: [PATCH 105/129] Update maintainers repo permissions doc (#567) --- .../github/maintainers-repo-permissions.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/building/github/maintainers-repo-permissions.md b/building/github/maintainers-repo-permissions.md index 00980f2b..9b19507e 100644 --- a/building/github/maintainers-repo-permissions.md +++ b/building/github/maintainers-repo-permissions.md @@ -25,25 +25,25 @@ A tracks' maintenance category is determined by three variables: To determine the maintenance category, find the first category that matches the track from this table: -| Category | Active? | Number of maintainers | Number of cross-track maintainers | -| ----------------------- | ------- | --------------------- | --------------------------------- | -| `wip-track` | No | Any | Any | -| `unmaintained` | Yes | 0 | 0 | -| `maintained-solitary` | Yes | 1 | 0 | -| `maintained-autonomous` | Yes | > 0 | = Number of maintainers | -| `maintained` | Yes | > 0 | < Number of maintainers | +| Category | Active? | Number of maintainers | Number of cross-track maintainers | +| --------------------- | ------- | --------------------- | --------------------------------- | +| wip-track | No | Any | Any | +| unmaintained | Yes | 0 | 0 | +| maintained-solitary | Yes | 1 | 0 | +| maintained-autonomous | Yes | > 0 | = Number of maintainers | +| maintained | Yes | > 0 | < Number of maintainers | ## Repo permissions The maintenance category is used to set the track's GitHub repo(s) permission(s). -| Category | Requires PR | Requires PR approval | Cross-track team reviews | -| ----------------------- | ----------- | -------------------- | ------------------------ | -| `wip-track` | No | No | No | -| `unmaintained` | Yes | Yes | Yes | -| `maintained-solitary` | Yes | Yes | Yes | -| `maintained-autonomous` | Yes | No | No | -| `maintained` | Yes | No | No | +| Category | Requires PR | Requires PR approval | Cross-track team reviews | +| --------------------- | ----------- | -------------------- | ------------------------ | +| wip-track | No | No | No | +| unmaintained | Yes | Yes | Yes | +| maintained-solitary | Yes | Yes | Yes | +| maintained-autonomous | Yes | No | No | +| maintained | Yes | No | No | ```exercism/caution The `wip-track` category is the only category that allows maintainers to push to `main`. From 0fac9fb5aaf1a538cfcd8e9511e53fc60e8fdb2e Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Thu, 5 Sep 2024 13:42:18 +0200 Subject: [PATCH 106/129] Minor tweak --- building/github/maintainers-repo-permissions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/github/maintainers-repo-permissions.md b/building/github/maintainers-repo-permissions.md index 9b19507e..bcca4c5a 100644 --- a/building/github/maintainers-repo-permissions.md +++ b/building/github/maintainers-repo-permissions.md @@ -29,9 +29,9 @@ To determine the maintenance category, find the first category that matches the | --------------------- | ------- | --------------------- | --------------------------------- | | wip-track | No | Any | Any | | unmaintained | Yes | 0 | 0 | -| maintained-solitary | Yes | 1 | 0 | | maintained-autonomous | Yes | > 0 | = Number of maintainers | -| maintained | Yes | > 0 | < Number of maintainers | +| maintained-solitary | Yes | 1 | 0 | +| maintained | Yes | > 1 | < Number of maintainers | ## Repo permissions From 067f7dcb8341113a4713a35258b2ec4876fad68c Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 6 Sep 2024 11:04:35 +0200 Subject: [PATCH 107/129] Remove duplicate FAQs mention --- using/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/using/README.md b/using/README.md index 5f906cc2..87ede62d 100644 --- a/using/README.md +++ b/using/README.md @@ -7,7 +7,6 @@ These documents describe how to make the most of your experience on Exercism. - [Solving Exercises](/docs/using/solving-exercises) - [Getting Feedback](/docs/using/feedback) - [Product](/docs/using/product) -- [FAQs](/docs/using/faqs) - [Contact](/docs/using/contact) - [Report Abuse](/docs/using/report-abuse) - [Legal Policies](/docs/using/legal) From 5f494554ba2bf5a7d56a951114c282848148b7b5 Mon Sep 17 00:00:00 2001 From: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> Date: Sat, 7 Sep 2024 13:11:11 +0300 Subject: [PATCH 108/129] Fix typo in articles.md --- building/tracks/articles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/articles.md b/building/tracks/articles.md index 3706acdf..7d28bda8 100644 --- a/building/tracks/articles.md +++ b/building/tracks/articles.md @@ -22,7 +22,7 @@ Any exercise, as long as there is something interesting to explore. Each article must add the following two files: -- `.articles//content.md`: description of the articleh (see [the docs](/docs/building/tracks/practice-exercises#file-articles-article-slug-content-md)) +- `.articles//content.md`: description of the article (see [the docs](/docs/building/tracks/practice-exercises#file-articles-article-slug-content-md)) - `.articles//snippet.md`: snippet showcasing the article (see [the docs](/docs/building/tracks/practice-exercises#file-articles-article-slug-snippet-txt)) You'll then need to add or update: From a6a2c377006d82900c02016c98f1b71cf83adb74 Mon Sep 17 00:00:00 2001 From: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:28:10 +0300 Subject: [PATCH 109/129] fix typos in reputation.md --- using/product/reputation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/using/product/reputation.md b/using/product/reputation.md index 1e939389..c2f4de80 100644 --- a/using/product/reputation.md +++ b/using/product/reputation.md @@ -69,7 +69,7 @@ Depending on the content of the pull request, a maintainer can award more (or le | --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `x:rep/tiny` | 3 |
  • Fixing a single typo or link
  • Removing a blank line or adding a line break
  • Changing/adding a single code comment
| | `x:rep/small` | 5 |
  • Fixing a single test case, task or example
  • Fixing multiple typos or links in a single file
  • Clarifying content by adding a few lines to a file
| -| `x:rep/medium` | 12 |
  • Syncing an exercise with problem-specifications (incl. edits)
  • Adding one or more test cases from scratch
  • Improving multiple files in an exercise
  • Adding mentor notes for an exercise from scratch
  • Fixing a small bug in a test runner/analyzer/representer
  • Adding analyzer comments for a single exericse
| +| `x:rep/medium` | 12 |
  • Syncing an exercise with problem-specifications (incl. edits)
  • Adding one or more test cases from scratch
  • Improving multiple files in an exercise
  • Adding mentor notes for an exercise from scratch
  • Fixing a small bug in a test runner/analyzer/representer
  • Adding analyzer comments for a single exercise
| | `x:rep/large` | 30 |
  • Adding a new concept or practice exercise
  • Adding new concept documentation
  • Substantial re-writing of an existing concept or exercise
  • Adding new CI scripts or other automation
| | `x:rep/massive` | 100 |
  • Creating a test-runner, analyzer, representer or generator from scratch
  • Major refactors to those tools
  • Creating major documentation from scratch (e.g. contribution or testing guides)
| @@ -82,7 +82,7 @@ The examples above can serve as rough orientation when to apply which label but _For backwards compatibility purposes, we also support using the `x:size` labels to determine the awarded reputation._ -### Reviewing a pull requests +### Reviewing a pull request For each merged or closed pull request reviewed by the user, `5` reputation is awarded. From 7cdebb7f0009f307818321e97d529c37e62b6597 Mon Sep 17 00:00:00 2001 From: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:38:19 +0300 Subject: [PATCH 110/129] Revise best-practices.md (#569) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix grammar issues in best-practices.md * add suggestions from review * Apply suggestions from code review Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com> * Apply final suggestion * Change second 'libraries' reference to 'dependencies' Co-authored-by: Victor Goff * Apply suggestions from code review Co-authored-by: Isaac Good --------- Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com> Co-authored-by: Victor Goff Co-authored-by: Isaac Good --- building/tooling/best-practices.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/building/tooling/best-practices.md b/building/tooling/best-practices.md index 2193c474..e8c68e1b 100644 --- a/building/tooling/best-practices.md +++ b/building/tooling/best-practices.md @@ -46,19 +46,19 @@ See the [network docs](/docs/building/tooling/docker#network) for more informati ### Prefer build-time commands over run-time commands -Tooling runs as one-off, short-lived Docker container: +The track tooling runs a one-off, short-lived Docker container which executes the following steps. -1. A Docker container is created -2. The Docker container is run with the correct arguments -3. The Docker container is destroyed +1. A Docker container is created. +2. The Docker container is run with the correct arguments. +3. The Docker container is destroyed. Therefore, code that runs in step 2 runs for _every single tooling run_. -For this reason, reducing the amount of code that runs in step 2 is a great way to improve performance +For this reason, reducing the amount of code that runs in step 2 is a great way to improve performance. One way of doing this is to move code from _run-time_ to _build-time_. Whilst run-time code runs on every single tooling run, build-time code only runs once (when the Docker image is built). Build-time code runs once as part of a GitHub Actions workflow. -Therefore, its fine if the code that runs at build-time is (relatively) slow. +Therefore, it's fine if the code that runs at build-time is (relatively) slow. #### Example: pre-compile libraries @@ -72,10 +72,11 @@ RUN stack build --resolver lts-20.18 --no-terminal --test --no-run-tests ``` First, the `pre-compiled` directory is copied into the image. -This directory is setup as a sort of fake exercise and depends on the same base libraries that the actual exercise depend on. +This directory is set up as a test exercise and depends on the same base libraries that the actual exercise depends on. Then we run the tests on that directory, which is similar to how tests are run for an actual exercise. Running the tests will result in the base being compiled, but the difference is that this happens at _build time_. -The resulting Docker image will thus have its base libraries already compiled, which means that no longer has to happen at _run time_, resulting in (much) faster execution times. +The resulting Docker image will thus have its base libraries already compiled. +This means compiling is not needed at _run time_, resulting in a (much) faster execution. #### Example: pre-compile binaries @@ -117,7 +118,7 @@ node 20.16.0 1.09GB node 20.16.0-slim 219MB ``` -The reason "slim" variants are smaller is that they'll have less features. +The reason "slim" variants are smaller is that they have fewer features. Your image might not need the additional features, and if not, consider using the "slim" variant. ### Removing unneeded bits @@ -137,7 +138,7 @@ Therefore, any package manager caching/bookkeeping files should be removed after ##### apk -Distributions that uses the `apk` package manager (such as Alpine) should use the `--no-cache` flag when using `apk add` to install packages: +Distributions that use the `apk` package manager (such as Alpine) should use the `--no-cache` flag when using `apk add` to install packages: ```dockerfile RUN apk add --no-cache curl @@ -214,7 +215,7 @@ ENTRYPOINT ["/opt/test-runner/bin/run.sh"] ##### Example: installing libraries The Ruby test runner needs the `git`, `openssh`, `build-base`, `gcc` and `wget` packages to be installed before its required libraries (gems) can be installed. -Its [Dockerfile](https://github.com/exercism/ruby-test-runner/blob/e57ed45b553d6c6411faeea55efa3a4754d1cdbf/Dockerfile) starts with a stage (given the name `build`) that install those packages (via `apk add`) and then installs the libaries (via `bundle install`): +Its [Dockerfile](https://github.com/exercism/ruby-test-runner/blob/e57ed45b553d6c6411faeea55efa3a4754d1cdbf/Dockerfile) starts with a stage (given the name `build`) that installs those packages (via `apk add`) and then installs the dependencies (via `bundle install`): ```dockerfile FROM ruby:3.2.2-alpine3.18 AS build From bd9fbb3ff05e893665d3c6ad664fab8ef85622e3 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 12 Nov 2024 11:58:07 +0100 Subject: [PATCH 111/129] Add stack-oriented tag --- building/tracks/config-json.md | 1 + 1 file changed, 1 insertion(+) diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index ea0f6fdb..f65cff3a 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -381,6 +381,7 @@ Tags are specified in the top-level `tags` field which is defined as an array of - `paradigm/logic`: the language supports a logic-based style of programming - `paradigm/object_oriented`: the language supports an object-oriented style of programming - `paradigm/procedural`: the language supports a procedural style of programming +- `paradigm/stack-oriented`: the language supports a stack-oriented style of programming ### Typing From c6ffcbbc39e571b9a3f818f4b1848113d0209c61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:27:42 +0000 Subject: [PATCH 112/129] Bump cross-spawn from 7.0.3 to 7.0.6 Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9e329abf..a2f017d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -148,9 +148,9 @@ cosmiconfig@^7.0.0: yaml "^1.10.0" cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" From 754d5e037a1c231621c0563f2ae7f92f6a4ec182 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 4 Dec 2024 08:51:04 +0100 Subject: [PATCH 113/129] Add more docs to add initial exercises doc (#571) * Add example implementation document * Add lint exercise docs --- building/tracks/new/add-initial-exercises.md | 49 +++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index bc0555c5..7145b7e6 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -109,7 +109,7 @@ Having selected the exercises you want include in your track, the next step is t You can quickly scaffold a new Practice Exercise by running the `bin/add-practice-exercise` script ([source](https://github.com/exercism/generic-track/blob/main/bin/add-practice-exercise)) from the track's root directory: ```shell -bin/add-exercise +bin/add-practice-exercise ``` Optionally, you can also specify the exercise's difficulty (via `-d`) and/or author's GitHub username (via `-a`): @@ -149,6 +149,53 @@ Keep in mind, though, that you should tweak the implementation to best fit your As an example, some tracks do not use classes but only work with functions. If your track usually works with objects though, you should adapt the implementation to what best fits your track. +#### Add example implementation + +To ensure that it is possible to write code that passes the tests, an example implementation needs to be added. + +```exercism/note +The code does _not_ have to be idiomatic, it only has to pass the tests. +``` + +You can verify the example implementation passes all the tests by running the `bin/verify-exercises` script ([source](https://github.com/exercism/generic-track/blob/main/bin/verify-exercises)) from the track's root directory: + +```shell +bin/verify-exercises +``` + +Use the output to verify that the example implementation passes all the tests. + +```exercism/note +If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. +``` + +```exercism/advanced +Under the hood, the `bin/verify-exercises` script does several things: + +- Copy the exercise to a temporary directory +- Overwrite the stub file(s) with the example implementation file(s) +- If the test file has skipped tests, they will be "unskipped" +- Run the tests +``` + +### Lint exercise + +The final step is to run [the linter](/docs/building/configlet/lint) to check if the track's (configuration) files are properly structured - both syntactically and semantically. + +First, make sure you have the latest version of [`configlet`](/docs/building/configlet/) by running: + +```shell +bin/fetch-configlet +``` + +The run [the linter](/docs/building/configlet/lint) by running: + +```shell +bin/configlet lint +``` + +Use the output to verify that all is well. + [problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises/ [allergies]: https://github.com/exercism/problem-specifications/tree/main/exercises/allergies [alphametics]: https://github.com/exercism/problem-specifications/tree/main/exercises/alphametics From aa8ad67e08fcb8c3cbc814b778551bec5049f5ae Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 4 Dec 2024 09:31:29 +0100 Subject: [PATCH 114/129] Create separate add practice exercise doc (#572) --- building/config.json | 9 +- building/tracks/new/add-initial-exercises.md | 92 +----------- .../README.md} | 2 +- building/tracks/practice-exercises/add.md | 140 ++++++++++++++++++ 4 files changed, 150 insertions(+), 93 deletions(-) rename building/tracks/{practice-exercises.md => practice-exercises/README.md} (99%) create mode 100644 building/tracks/practice-exercises/add.md diff --git a/building/config.json b/building/config.json index a69e9d5c..b5bc92fd 100644 --- a/building/config.json +++ b/building/config.json @@ -102,10 +102,17 @@ { "uuid": "d951a049-a5ca-4b38-ae06-68b10cfbb2d9", "slug": "tracks/practice-exercises", - "path": "building/tracks/practice-exercises.md", + "path": "building/tracks/practice-exercises/README.md", "title": "Practice Exercises", "blurb": "" }, + { + "uuid": "531f0e8a-0207-42a8-bf68-26ca42f8872f", + "slug": "tracks/practice-exercises/add", + "path": "building/tracks/practice-exercises/add.md", + "title": "Add a Practice Exercise", + "blurb": "" + }, { "uuid": "34387a23-f65c-490e-96ed-6b5e25298db7", "slug": "tracks/concept-exercises", diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index 7145b7e6..fd344c86 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -103,98 +103,8 @@ To make this all a bit more concrete, this is what a sample selection of initial ## Implement exercises -### Scaffold exercise - Having selected the exercises you want include in your track, the next step is to implement them. -You can quickly scaffold a new Practice Exercise by running the `bin/add-practice-exercise` script ([source](https://github.com/exercism/generic-track/blob/main/bin/add-practice-exercise)) from the track's root directory: - -```shell -bin/add-practice-exercise -``` - -Optionally, you can also specify the exercise's difficulty (via `-d`) and/or author's GitHub username (via `-a`): - -```shell -bin/add-practice-exercise -d 3 -a foobar -``` - -```exercism/note -If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. -``` - -### Implement exercise - -Once the scaffolded files have been created, you'll then have to: - -- Add tests to the tests file -- Add an example implementation -- Define the stub file's contents -- Within the exercise's `.meta/config.json` file: - - Add the GitHub username of the exercise's authors to the `authors` key -- Within the track's `config.json` file: - - Check/update the exercise's difficulty - - Add concepts to the `practices` key (only required when the track has concept exercises) - - Add concepts to the `prerequisites` key (only required when the track has concept exercises) - -#### Add tests - -A key part of adding an exercise is adding tests. -Rougly speaking, there are two options when adding tests for one of the above exercises: - -1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo][problem-specifications-exercises]. -2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). - -The second option can be particularly appealing, as it can give you results quickly. -Keep in mind, though, that you should tweak the implementation to best fit your track. -As an example, some tracks do not use classes but only work with functions. -If your track usually works with objects though, you should adapt the implementation to what best fits your track. - -#### Add example implementation - -To ensure that it is possible to write code that passes the tests, an example implementation needs to be added. - -```exercism/note -The code does _not_ have to be idiomatic, it only has to pass the tests. -``` - -You can verify the example implementation passes all the tests by running the `bin/verify-exercises` script ([source](https://github.com/exercism/generic-track/blob/main/bin/verify-exercises)) from the track's root directory: - -```shell -bin/verify-exercises -``` - -Use the output to verify that the example implementation passes all the tests. - -```exercism/note -If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. -``` - -```exercism/advanced -Under the hood, the `bin/verify-exercises` script does several things: - -- Copy the exercise to a temporary directory -- Overwrite the stub file(s) with the example implementation file(s) -- If the test file has skipped tests, they will be "unskipped" -- Run the tests -``` - -### Lint exercise - -The final step is to run [the linter](/docs/building/configlet/lint) to check if the track's (configuration) files are properly structured - both syntactically and semantically. - -First, make sure you have the latest version of [`configlet`](/docs/building/configlet/) by running: - -```shell -bin/fetch-configlet -``` - -The run [the linter](/docs/building/configlet/lint) by running: - -```shell -bin/configlet lint -``` - -Use the output to verify that all is well. +The [Add Practice Exercise docs](/docs/building/tracks/practice-exercises/add) have detailed instructions on how to add a Practic Exercise. [problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises/ [allergies]: https://github.com/exercism/problem-specifications/tree/main/exercises/allergies diff --git a/building/tracks/practice-exercises.md b/building/tracks/practice-exercises/README.md similarity index 99% rename from building/tracks/practice-exercises.md rename to building/tracks/practice-exercises/README.md index 983de55d..0d43c4f9 100644 --- a/building/tracks/practice-exercises.md +++ b/building/tracks/practice-exercises/README.md @@ -2,7 +2,7 @@ [Practice Exercises](/docs/building/product/practice-exercises) are exercises designed to allow students to solve an arbitrary problem, with the aim of them making use of the concepts they have learned so far. -Interested in adding your first Practice Exercise to a track? Watch our walkthrough video 👇 +Interested in adding your first Practice Exercise to a track? Check the [Add Practice Exercise docs](/docs/building/tracks/practice-exercises/add) or watch our walkthrough video 👇 [video:vimeo/906101866?h=2954ad331e]() diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md new file mode 100644 index 00000000..b3918e88 --- /dev/null +++ b/building/tracks/practice-exercises/add.md @@ -0,0 +1,140 @@ +# Add Practice Exercise + +This document will explain how to a new [Practice Exercise](/docs/building/tracks/practice-exercises). + +## Select exercise + +The simplest way to check what Practice Exercises have not yet been implemented is to go to the track's build page (e.g. https://exercism.org/tracks/csharp/build) and check the "Practice Exercises" section. + +```exercism/caution +The data on the build page is updated once a day. +``` + +## Scaffold exercise + +You can quickly scaffold a new Practice Exercise by running the `bin/add-practice-exercise` script ([source](https://github.com/exercism/generic-track/blob/main/bin/add-practice-exercise)) from the track's root directory: + +```shell +bin/add-practice-exercise +``` + +Optionally, you can also specify the exercise's difficulty (via `-d`) and/or author's GitHub username (via `-a`): + +```shell +bin/add-practice-exercise -d 3 -a foobar +``` + +```exercism/note +If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. +``` + +## Implement exercise + +Once the scaffolded files have been created, you'll then have to: + +- Add tests to the tests file +- Add an example implementation +- Define the stub file's contents +- Within the exercise's `.meta/config.json` file: + - Add the GitHub username of the exercise's authors to the `authors` key +- Within the track's `config.json` file: + - Check/update the exercise's difficulty + - Add concepts to the `practices` key (only required when the track has concept exercises) + - Add concepts to the `prerequisites` key (only required when the track has concept exercises) + +### Add tests + +A key part of adding an exercise is adding tests. +Rougly speaking, there are two options when adding tests for one of the above exercises: + +1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo][problem-specifications-exercises]. +2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). + +The second option can be particularly appealing, as it can give you results quickly. +Keep in mind, though, that you should tweak the implementation to best fit your track. +As an example, some tracks do not use classes but only work with functions. +If your track usually works with objects though, you should adapt the implementation to what best fits your track. + +### Add example implementation + +To ensure that it is possible to write code that passes the tests, an example implementation needs to be added. + +```exercism/note +The code does _not_ have to be idiomatic, it only has to pass the tests. +``` + +You can verify the example implementation passes all the tests by running the `bin/verify-exercises` script ([source](https://github.com/exercism/generic-track/blob/main/bin/verify-exercises)) from the track's root directory: + +```shell +bin/verify-exercises +``` + +Use the output to verify that the example implementation passes all the tests. + +```exercism/note +If you're working on a track repo without this file, feel free to copy them into your repo using the above source link. +``` + +```exercism/advanced +Under the hood, the `bin/verify-exercises` script does several things: + +- Copy the exercise to a temporary directory +- Overwrite the stub file(s) with the example implementation file(s) +- If the test file has skipped tests, they will be "unskipped" +- Run the tests +``` + +### Add stub file(s) + +The stub implementation file(s) provide a starting point for students. + +We recommend stub files to have the minimal amount of code such that: + +- The student can immediately start implementing the logic to pass the tests +- The student is not presented with "weird" syntax errors + +In practice, this means defining the functions/methods that are tested by the test suite. +Tracks are free as to how they setup this code, as long as they ensure that the stub code initially fails all the tests. + +#### Examples + +Python: + +```python +def two_fer(name): + pass +``` + +Kotlin: + +```kotlin +fun twofer(name: String): String { + TODO("Implement the function to complete the task") +} +``` + +## Lint exercise + +The final step is to run [the linter](/docs/building/configlet/lint) to check if the track's (configuration) files are properly structured - both syntactically and semantically. + +First, make sure you have the latest version of [`configlet`](/docs/building/configlet/) by running: + +```shell +bin/fetch-configlet +``` + +The run [the linter](/docs/building/configlet/lint) by running: + +```shell +bin/configlet lint +``` + +Use the output to verify that all is well. + +## Submit Pull Request + +Once all is well, you can then [Submit a Pull Request](/docs/building/github/contributors-pull-request-guide) to the track's repository. + +Before submitting, please read the [Contributors Pull Request Guide](/docs/building/github/contributors-pull-request-guide) and [Pull Request Guide](/docs/community/being-a-good-community-member/pull-requests). + +Ensure the PR description lists the exercise being added. From 299c699f68caa24e13144e540d14d99b9314a26d Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 4 Dec 2024 10:06:58 -0800 Subject: [PATCH 115/129] [Practice Exercise Documentation]: Update add.md (#573) Added the word "add" to line 3. --- building/tracks/practice-exercises/add.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md index b3918e88..fa2525a8 100644 --- a/building/tracks/practice-exercises/add.md +++ b/building/tracks/practice-exercises/add.md @@ -1,6 +1,6 @@ # Add Practice Exercise -This document will explain how to a new [Practice Exercise](/docs/building/tracks/practice-exercises). +This document will explain how to add a new [Practice Exercise](/docs/building/tracks/practice-exercises). ## Select exercise From c7a0397af590ab28b8b85eda1c3142ce799ecbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:00:24 -0800 Subject: [PATCH 116/129] Fix broken markdown in add.md (#574) --- building/tracks/practice-exercises/add.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md index fa2525a8..9e4c8567 100644 --- a/building/tracks/practice-exercises/add.md +++ b/building/tracks/practice-exercises/add.md @@ -47,7 +47,7 @@ Once the scaffolded files have been created, you'll then have to: A key part of adding an exercise is adding tests. Rougly speaking, there are two options when adding tests for one of the above exercises: -1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo][problem-specifications-exercises]. +1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo](https://github.com/exercism/problem-specifications). 2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). The second option can be particularly appealing, as it can give you results quickly. From c4f5971fdd0ccff91c53571313cb59986c4aef67 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Fri, 6 Dec 2024 14:18:12 +0100 Subject: [PATCH 117/129] Move test generator note (#575) --- building/configlet/create.md | 27 +------------------- building/tracks/new/add-initial-exercises.md | 2 +- building/tracks/practice-exercises/add.md | 5 ++++ 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/building/configlet/create.md b/building/configlet/create.md index 3ab9ff5b..3b6fd137 100644 --- a/building/configlet/create.md +++ b/building/configlet/create.md @@ -22,32 +22,7 @@ Options for create: ## Create Practice Exercise -To create a practice exercise, one has to specify its slug: - -```shell -configlet create --practice-exercise collatz-conjecture -``` - -This will create the practice exercise's required files, as specified in the [Practice Exercises docs](/docs/building/tracks/practice-exercises). -If the practice exercise is defined in the [Problem Specifications repo](https://github.com/exercism/problem-specifications/), configlet will sync the docs and metadata from there. - -Of course, this is just the first step towards creating an exercise. -You'll then have to: - -- Add tests to the tests file -- Add an example implementation -- Define the stub file's contents -- Within the exercise's `.meta/config.json` file: - - Add the GitHub username of the exercise's authors to the `authors` key -- Within the track's `config.json` file: - - Check/update the exercise's difficulty - - Add concepts to the `practices` key (only required when the track has concept exercises) - - Add concepts to the `prerequisites` key (only required when the track has concept exercises) - -```exercism/note -Some tracks have implemented an exercise/test _generator_, which is a tool that can generate the test file's contents based on the exercise's `canonical-data.json` found in the [Problem Specifications repo](https://github.com/exercism/problem-specifications/). -Make sure to read the track's documentation to see if there is a generator that you can use. -``` +The [Add Practice Exercise docs](/docs/building/tracks/practice-exercises/add) have detailed instructions on how to add a Practice Exercise. ## Create Concept Exercise diff --git a/building/tracks/new/add-initial-exercises.md b/building/tracks/new/add-initial-exercises.md index fd344c86..5b0c2488 100644 --- a/building/tracks/new/add-initial-exercises.md +++ b/building/tracks/new/add-initial-exercises.md @@ -104,7 +104,7 @@ To make this all a bit more concrete, this is what a sample selection of initial ## Implement exercises Having selected the exercises you want include in your track, the next step is to implement them. -The [Add Practice Exercise docs](/docs/building/tracks/practice-exercises/add) have detailed instructions on how to add a Practic Exercise. +The [Add Practice Exercise docs](/docs/building/tracks/practice-exercises/add) have detailed instructions on how to add a Practice Exercise. [problem-specifications-exercises]: https://github.com/exercism/problem-specifications/tree/main/exercises/ [allergies]: https://github.com/exercism/problem-specifications/tree/main/exercises/allergies diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md index 9e4c8567..adca4003 100644 --- a/building/tracks/practice-exercises/add.md +++ b/building/tracks/practice-exercises/add.md @@ -55,6 +55,11 @@ Keep in mind, though, that you should tweak the implementation to best fit your As an example, some tracks do not use classes but only work with functions. If your track usually works with objects though, you should adapt the implementation to what best fits your track. +```exercism/note +Some tracks use a [test _generator_](/docs/building/tooling/test-generators) to automatically (re-)generate an exercise's test file(s). +Please check the track documentation to see if there is a test generator and if so, how to use it. +``` + ### Add example implementation To ensure that it is possible to write code that passes the tests, an example implementation needs to be added. From 742ee0f6b66dca1843a6d4828685f4562b4008eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sat, 7 Dec 2024 04:17:01 -0800 Subject: [PATCH 118/129] Fix typo in add.md (#576) --- building/tracks/practice-exercises/add.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md index adca4003..fde74654 100644 --- a/building/tracks/practice-exercises/add.md +++ b/building/tracks/practice-exercises/add.md @@ -45,7 +45,7 @@ Once the scaffolded files have been created, you'll then have to: ### Add tests A key part of adding an exercise is adding tests. -Rougly speaking, there are two options when adding tests for one of the above exercises: +Roughly speaking, there are two options when adding tests for one of the above exercises: 1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo](https://github.com/exercism/problem-specifications). 2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). From 9facbf85a8350b4c714843532f7aebe881324d75 Mon Sep 17 00:00:00 2001 From: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:11:48 +0200 Subject: [PATCH 119/129] Refine add.md for Practice Exercises and fix minor typo (#577) --- building/tracks/practice-exercises/add.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/tracks/practice-exercises/add.md b/building/tracks/practice-exercises/add.md index fde74654..6f31fdd4 100644 --- a/building/tracks/practice-exercises/add.md +++ b/building/tracks/practice-exercises/add.md @@ -45,7 +45,7 @@ Once the scaffolded files have been created, you'll then have to: ### Add tests A key part of adding an exercise is adding tests. -Roughly speaking, there are two options when adding tests for one of the above exercises: +Roughly speaking, there are two options when adding tests for a Practice Exercise: 1. Implement the tests from scratch, using the test cases from the exercise's `canonical-data.json` file as found in the [problem-specifications repo](https://github.com/exercism/problem-specifications). 2. Port the tests from another track's implementation (tip: go to `https://exercism.org/exercises/` to get an overview of which tracks have implemented a specific exercise). @@ -128,7 +128,7 @@ First, make sure you have the latest version of [`configlet`](/docs/building/con bin/fetch-configlet ``` -The run [the linter](/docs/building/configlet/lint) by running: +Then run [the linter](/docs/building/configlet/lint) by running: ```shell bin/configlet lint From 2a8ff5fc3bdc349135224dbb9e47dc5ca8286523 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Sun, 5 Jan 2025 11:55:08 +0100 Subject: [PATCH 120/129] Add codemirror repo instructions --- .../tracks/new/dynamic-syntax-highlighting.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/building/tracks/new/dynamic-syntax-highlighting.md b/building/tracks/new/dynamic-syntax-highlighting.md index 3f41698b..f4e7b26e 100644 --- a/building/tracks/new/dynamic-syntax-highlighting.md +++ b/building/tracks/new/dynamic-syntax-highlighting.md @@ -49,17 +49,18 @@ The next step is to [Enable language](#enable-language). ### Create a new plugin -If you'd like to create plugin, you have two options for hosting: +These are the steps to get going: -1. Create a repository on your personal account and publish it yourself. -2. Have us (Exercism) create a repository and let us publish it. - To do so, open a topic on [the forum](https://forum.exercism.org/c/exercism/building-exercism/125) requesting this. +1. Check [our repository list for an existing `codemirror-lang-...`](https://github.com/search?q=org%3Acodemirror-lang&type=repositories) to ensure that one doesn't already exist. +2. Start a new topic on [the Exercism forum][building-exercism] telling us which language you'd like to create a CodeMirror plugin for. +3. Once a CodeMirror plugin repo has been created for you, implement the grammar for your track. ```exercism/note -You could consider forking the [codemirror/lang-example repository](https://github.com/codemirror/lang-example) which implements CodeMirror support for a simple language. +To help you get started, the repo created for you contains a minimal sample grammar that you can build on/tweak. ``` -Once you have a repo, follow the [language package instructions](https://codemirror.net/examples/lang-package/) to implement the plugin. +We have an incredibly friendly and supportive community who will be happy to help you as you work through this! +If you get stuck, please start a new topic on [the Exercism forum][building-exercism] or create new issues at [exercism/exercism][exercism-repo] as needed 🙂 You'll then need to publish the plugin on [NPM](https://www.npmjs.com/). The next step is to [Enable the language](#enable-language). @@ -69,3 +70,6 @@ The next step is to [Enable the language](#enable-language). Your language's syntax (closely) resembles another language's syntax, in which case you could consider using the syntax highlighting of that language for your language. To do so, configure the track using the other language's CodeMirror plugin. See the [Enable language](#enable-language) section for more information. + +[building-exercism]: https://forum.exercism.org/c/exercism/building-exercism/125 +[exercism-repo]: https://github.com/exercism/exercism From 8d0a0fa83794b39ebf41a4e546039e88ddc2c2aa Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Sun, 19 Jan 2025 18:06:44 +0100 Subject: [PATCH 121/129] Add formatting notes on test generator --- building/tooling/test-generators.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/building/tooling/test-generators.md b/building/tooling/test-generators.md index 14d8d43a..294e9b24 100644 --- a/building/tooling/test-generators.md +++ b/building/tooling/test-generators.md @@ -77,6 +77,10 @@ While you're free to use other languages, each additional language will make it Therefore, we recommend using the track's language where possible, because it makes maintaining or contributing easier. ``` +### Formatting + +If your track has tooling to format code, consider running this as a post-processing step _after_ rendering your template. + ### Canonical data The core data the Test Generator works with is an exercise's [`canonical-data.json` file](https://github.com/exercism/problem-specifications?tab=readme-ov-file#test-data-canonical-datajson). From 6a35295712885b00fd09eb578aa0b862c6e917f2 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Fri, 24 Jan 2025 15:45:24 +0000 Subject: [PATCH 122/129] Remove instructions to open issues --- using/faqs.md | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/using/faqs.md b/using/faqs.md index 1c7748ae..cfb26c5b 100644 --- a/using/faqs.md +++ b/using/faqs.md @@ -77,7 +77,7 @@ The new site has a brand new command-line client, and also needs some extra meta Read about how to [upgrade your command-line client and migrate your solutions for the new site][upgrade-cli]. If that doesn't help, or you didn't use the old version of the command-line client, please read through the [command-line client Walkthrough][cli-walkthrough]. -If that doesn't help either, [open an issue][new-cli-issue] and we will help you get it sorted out. +If that doesn't help either, [please post on the forum][forum] and we will help you get it sorted out. ## How do I check the version of my command-line client? @@ -147,20 +147,16 @@ You can see more on the [How to Contribute page][contribute]. A new language track gets created when a member of the community takes the lead on it and becomes a maintainer of the track. If you'd like to get involved in helping set one up, there are [instructions here][new-language-request]. -## Opening an Issue +## Telling us when something is wrong -Before submitting an issue, be sure to check the relevant GitHub issue tracker to see if it has already been reported or resolved: +If you find something that's not wrong, please let us know by creating a thread on [the forum][forum]. -1. [The Exercism Website or Product][website-copy] -2. [The Command-Line Interface (CLI) Client][cli-client-issues] -3. [Exercises][languages] - Select your language and then look at the issues tab - -You can search through issues (remove the `is:open` filter to include closed/resolved issues). +Before creating a thread, be sure to check for existing discussions. Try a few different keywords. -## What if my issue is not listed here or in GitHub? +## What if my issue is not listed here or on the forum? -If your problem hasn't been resolved or reported, then create an issue in the appropriate repository by selecting the green **New issue** button. +If your problem hasn't been resolved or reported, then create a new [forum post][forum]. Make sure to include the following information: @@ -174,7 +170,7 @@ Make sure to include the following information: If your issue pertains to an exercise in your language track, then please find the correct language track [from this list][from-this-list] and submit an issue there. Please specify if the issue is with the instructions or something language specific, using the template below. -If you have spotted a typo or if you have a suggestion for clearer language or instructions on the general website, then [create an issue for Exercism Website Copy][website-copy-new-issue] with the following information: +If you have spotted a typo or if you have a suggestion for clearer language or instructions on the general website, then please create a new thread on [the forum][forum] with the following information: 1. Link to the page where the issue is 1. Explanation of what the mistake is or what is unclear @@ -197,7 +193,7 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [learning-mode]: /docs/building/product/unlocking-exercises#h-learning-mode-vs-practice-mode [mail-abuse]: mailto:abuse@exercism.org?subject=%5BCoC%5D] -[new-cli-issue]: https://github.com/exercism/CLI/issues/new +[forum]: https://forum.exercism.org/ [new-language-request]: https://github.com/exercism/generic-track/blob/main/README.md [opening-an-issue]: #opening-an-issue [here-be-dragons]: https://www.youtube.com/watch?v=QAUHYzC9kFM @@ -208,4 +204,3 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [unlocking-exercises]: /docs/building/product/unlocking-exercises [upgrade-cli]: https://github.com/exercism/website-copy/blob/main/pages/cli_v1_to_v2.md [website-copy]: https://github.com/exercism/website-copy/issues -[website-copy-new-issue]: https://github.com/exercism/website-copy/issues/new From d1a1f47b7ba646f22e44688b8cef893b567ed6b7 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Sun, 26 Jan 2025 20:55:37 +0000 Subject: [PATCH 123/129] Update using/faqs.md Co-authored-by: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> --- using/faqs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/using/faqs.md b/using/faqs.md index cfb26c5b..b5103fe9 100644 --- a/using/faqs.md +++ b/using/faqs.md @@ -149,7 +149,7 @@ If you'd like to get involved in helping set one up, there are [instructions her ## Telling us when something is wrong -If you find something that's not wrong, please let us know by creating a thread on [the forum][forum]. +If you find something that's wrong, please let us know by creating a thread on [the forum][forum]. Before creating a thread, be sure to check for existing discussions. Try a few different keywords. From 3b81b0ad028a309e10597612d443944bf86238a7 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Mon, 27 Jan 2025 08:50:45 -0500 Subject: [PATCH 124/129] Remove another reference to GitHub issues. As noticed by @tasxatzial --- using/faqs.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/using/faqs.md b/using/faqs.md index b5103fe9..76497e12 100644 --- a/using/faqs.md +++ b/using/faqs.md @@ -35,8 +35,7 @@ To learn more about what it means for the devil to be in the details, check out You're in luck. Here's a [step-by-step guide][getting-started] to get you started. -If something is still unclear for you or not working then it might be the same for others so we'd appreciate you letting us know. -Refer to [Opening an Issue][opening-an-issue] below for instructions on how you can help us help others. +If something is still unclear for you or not working then it might be the same for others so we'd appreciate you [letting us know on the forum][forum]. ## I get the error "Sorry, we could not authenticate you from GitHub" when trying to log in. What should I do? From 15fea4d95a9d31714df89b8d866ee22aa741dd72 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Tue, 28 Jan 2025 08:41:22 -0800 Subject: [PATCH 125/129] Adding a "Test-Driven Development" document (#579) * Adding a "Test-Driven Development" document Co-authored-by: Jeremy Walker Co-authored-by: Anastasios Chatzialexiou <16361161+tasxatzial@users.noreply.github.com> Co-authored-by: Victor Goff Co-authored-by: Isaac Good --- using/config.json | 7 +++ using/faqs.md | 48 +++++++++-------- using/solving-exercises/tdd.md | 97 ++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 22 deletions(-) create mode 100644 using/solving-exercises/tdd.md diff --git a/using/config.json b/using/config.json index b6085c26..18b9e3c4 100644 --- a/using/config.json +++ b/using/config.json @@ -63,6 +63,13 @@ "title": "Solving exercises", "blurb": "Learn how to solve exercises on Exercism" }, + { + "uuid": "d78582b0-d90a-45a7-a1b4-2e87dac645b1", + "slug": "solving-exercises/tdd", + "path": "using/solving-exercises/tdd.md", + "title": "What is Test-Driven Development?", + "blurb": "Use the TDD methodology and the given test suite to solve exercises" + }, { "uuid": "7b753e2a-bd68-4d68-bdd8-c356de4342b6", "slug": "solving-exercises/working-locally", diff --git a/using/faqs.md b/using/faqs.md index 76497e12..df2441b6 100644 --- a/using/faqs.md +++ b/using/faqs.md @@ -30,26 +30,31 @@ To learn more about what it means for the devil to be in the details, check out ## The Basics -## I'm lost. Where do I go? +### I'm lost. Where do I go? You're in luck. Here's a [step-by-step guide][getting-started] to get you started. If something is still unclear for you or not working then it might be the same for others so we'd appreciate you [letting us know on the forum][forum]. -## I get the error "Sorry, we could not authenticate you from GitHub" when trying to log in. What should I do? +### I get the error "Sorry, we could not authenticate you from GitHub" when trying to log in. What should I do? This means that GitHub isn't willing to verify who you are. That can be because you chose not to give permission or it might be because your GitHub account is not properly configured. A common problem is that you haven't verified your email address on GitHub. You can check that in your [GitHub email settings][email-settings]. -## How do I delete my account? +### How do I delete my account? You can delete your account by following the link at the bottom of your [settings page][settings]. If your issue is with emails and notifications you can adjust that in [your personal preferences][personal-settings]. -## How do I unlock exercises? +### What am I supposed to do to solve an exercise? + +When you start working on an exercise, you will have a "stub solution" that you need to complete, and you will have a complete test suite that represents the requirements your solution must satisfy. +This development methodology is a form of [Test-Driven Development][tdd]. + +### How do I unlock exercises? By default, tracks with Learning Exercises require you to solve Learning Exercises to unlock other exercises (each should take around 5 minutes to solve if you are fluent in a language). This is called [Learning Mode][learning-mode]. @@ -57,7 +62,7 @@ This is called [Learning Mode][learning-mode]. While we recommend using Learning Mode to progress through a track, you could [switch to Practice Mode][switching-modes] to unlock all exercises. For more information, see [unlocking exercises][unlocking-exercises]. -## Why have my unlocked exercises in the old site become locked? +### Why have my unlocked exercises in the old site become locked? By default, tracks with Learning Exercises require you to solve Learning Exercises to unlock other exercises (each should take around 5 minutes to solve if you are fluent in a language). This is called [Learning Mode][learning-mode]. @@ -70,7 +75,7 @@ For more information, see [unlocking exercises][unlocking-exercises]. ## Exercism Command-line Client -## I can't submit! What should I do? +### I can't submit! What should I do? The new site has a brand new command-line client, and also needs some extra metadata for your exercise. Read about how to [upgrade your command-line client and migrate your solutions for the new site][upgrade-cli]. @@ -78,16 +83,16 @@ Read about how to [upgrade your command-line client and migrate your solutions f If that doesn't help, or you didn't use the old version of the command-line client, please read through the [command-line client Walkthrough][cli-walkthrough]. If that doesn't help either, [please post on the forum][forum] and we will help you get it sorted out. -## How do I check the version of my command-line client? +### How do I check the version of my command-line client? The version command `exercism version` outputs the running version of the Exercism command-line client. By running the version command with the latest flag `exercism version --latest` you can check if there is a newer version available. -## How do I upgrade to the latest version of the command-line client? +### How do I upgrade to the latest version of the command-line client? The command `exercism upgrade` will upgrade to the latest available version of the command-line client if one is available. -## I get permission denied errors when upgrading! What should I do? +### I get permission denied errors when upgrading! What should I do? If you are receiving permission denied errors when trying to upgrade the command-line client, chances are the binary was installed via a system package manager (e.g Homebrew) or has been installed into a directory that you no longer have write access to. @@ -97,36 +102,36 @@ If your command-line client was installed manually, please check the path of the If so, use the tools provided by your system to change the permissions of the directory to grant write access to your user and trying upgrading again. If you are not sure if you created the directory, or the returned path is a system path, please use your system tools to uninstall the command-line client and reinstall using the [interactive walkthrough][interactive-walkthrough]. -## I get the "16-bit MS-DOS Subsystem" error dialog after upgrading on Windows. What should I do? +### I get the "16-bit MS-DOS Subsystem" error dialog after upgrading on Windows. What should I do? Prior to version 3.0.5 of the Exercism command-line client, there was a bug in the upgrade command that would replace the command-line client binary file with a single text file causing the "16-bit MS-DOS Subsystem" error. To resolve this issue remove the corrupt binary and reinstall the command-line client using the [interactive walkthrough][interactive-walkthrough]. ## Mentored Mode -## My queue hasn't updated in a while - is there something wrong? +### My queue hasn't updated in a while - is there something wrong? While wait times can be longer than normal for a number of reasons (number of mentors, time of year), it can also appear that the queue is not updating. This is not unusual and you should see movement fairly soon. If you your wait is far longer than the average, refer to [Opening an Issue][opening-an-issue] below. -## How many mentors does my track have? I only see a handful on the website. +### How many mentors does my track have? I only see a handful on the website. The website only shows mentors who have provided bio information for the website, not the actual number of mentors who are actively reviewing solutions. Rest assured, there are mentors working through their queues so hang in there! -## I submitted the wrong solution / file. What can I do? +### I submitted the wrong solution / file. What can I do? On the exercise page in question, navigate to "Your Iterations". If the unwanted iteration is folded, click the circled arrow. In the "dots" menu, select "Delete iteration". -## How can I report abuse or examples of bad mentoring? +### How can I report abuse or examples of bad mentoring? Please check our [Code of Conduct][coc] for more information about our expectations of conduct. If you would like to report something, please reach out to us at [abuse@exercism.org][mail-abuse] and we will try to fix or resolve the issue respecting both you and your privacy. -## Why can't I give feedback on representations? +### Why can't I give feedback on representations? To give feedback on representations for a track, you need to pass the following three criteria: @@ -136,24 +141,24 @@ To give feedback on representations for a track, you need to pass the following ## Improving Exercism -## This is great! How do I get involved? +### This is great! How do I get involved? There are a few different ways - becoming a mentor, managing a language track or reporting (or addressing!) issues on GitHub. You can see more on the [How to Contribute page][contribute]. -## How do new language tracks get added to the site? +### How do new language tracks get added to the site? A new language track gets created when a member of the community takes the lead on it and becomes a maintainer of the track. If you'd like to get involved in helping set one up, there are [instructions here][new-language-request]. -## Telling us when something is wrong +### Telling us when something is wrong If you find something that's wrong, please let us know by creating a thread on [the forum][forum]. Before creating a thread, be sure to check for existing discussions. Try a few different keywords. -## What if my issue is not listed here or on the forum? +### What if my issue is not listed here or on the forum? If your problem hasn't been resolved or reported, then create a new [forum post][forum]. @@ -164,7 +169,7 @@ Make sure to include the following information: 1. Instructions on how to reproduce the issue 1. If applicable, reference to any related issue using its issue number (formatted like #1203) -## What if there is an issue with language on the website? +### What if there is an issue with language on the website? If your issue pertains to an exercise in your language track, then please find the correct language track [from this list][from-this-list] and submit an issue there. Please specify if the issue is with the instructions or something language specific, using the template below. @@ -190,7 +195,6 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [kytrinyx]: https://exercism.github.io/kytrinyx/ [languages]: https://github.com/search?q=topic%3Aexercism-track+org%3Aexercism&type=Repositories [learning-mode]: /docs/building/product/unlocking-exercises#h-learning-mode-vs-practice-mode - [mail-abuse]: mailto:abuse@exercism.org?subject=%5BCoC%5D] [forum]: https://forum.exercism.org/ [new-language-request]: https://github.com/exercism/generic-track/blob/main/README.md @@ -202,4 +206,4 @@ If you have spotted a typo or if you have a suggestion for clearer language or i [switching-modes]: /docs/building/product/unlocking-exercises#h-switching-modes [unlocking-exercises]: /docs/building/product/unlocking-exercises [upgrade-cli]: https://github.com/exercism/website-copy/blob/main/pages/cli_v1_to_v2.md -[website-copy]: https://github.com/exercism/website-copy/issues +[tdd]: https://exercism.org/docs/using/solving-exercises/tdd diff --git a/using/solving-exercises/tdd.md b/using/solving-exercises/tdd.md new file mode 100644 index 00000000..0589c4b5 --- /dev/null +++ b/using/solving-exercises/tdd.md @@ -0,0 +1,97 @@ +# Test-Driven Development (TDD) + +Test-Driven Development (sometimes called Test-First Development or Test-Driven Design) is the practice of writing the unit tests first, before you write a single line of implementation code. + +## On Exercism, the tests _are_ the requirements! + +All Practice Exercises you work on (those ones that don't teach you a new concept) will have some instructions describing in general terms what you need to do. +By design, these instructions do not account for programming-language-specific implementation details because they are shared by all of Exercism's 70+ language tracks. +Some language tracks will append more specific details for you, but not all of them do. + +When you start working on a Practice Exercise, give the instructions a careful read. +They will give you a broad overview of how you go about implementing a solution. +But you will have to read the _tests_ to understand the full and exact requirements: + +- Must the result be a particular kind of data structure? +- Must the result be sorted in some order? +- How are you expected to handle exceptions? And so on. + +You have solved an exercise when all the provided tests run and pass. +In other words, your solution is not just an interpretation of the instructions that "looks right", your solution is a program that _satisfies the given tests_. +**The tests represent the complete requirements for the exercise.** + +## How does Exercism apply TDD? + +We've done the work of writing a unit test suite for you. +Your goal is to write a solution that contains just enough code to make all those unit tests pass. + +Keep this in mind: the TDD approach will help you get to the solution, but you don't need to stop there. +If you want to extend your solution beyond the requirements, you are welcome to do so. +Should you choose to work with a mentor (and we encourage you to do that once you get the tests passing), they can help you refactor and refine your initial implementation, or even propose new unit tests. + +## Working in the online editor + +When you're working in the code editor on Exercism's website, you can read the tests but you are not able to edit them. +All tests will be executed each time you run them, regardless of any "skip" mechanisms noted in the test file. + +When there are multiple tests that fail, the website initially only displays the results of the first failure. +You can click on other failures to expand them, too! +Sometimes the first result may not be the most informative. + +Don't be discouraged by a large number of failing tests. +Focus on making them pass one-by-one. + +## Working locally + +Many tracks use "skipped" tests in their test files. +Initially, only the first test is "active" and the remaining are inactive (how this happens varies by track). +When you run the test suite in your environment, only the first test runs. +We do this to encourage you to follow this workflow: + +1. Before adding any new code, run the test suite: you should see a failing test. +1. Add _just enough_ code to pass the test. +1. Run the test suite. +1. If the test still fails, repeat step 2. +1. Once the test passes, refactor your code as desired, ensuring all active tests still pass. + Refactoring might include: + - removing any duplicated code, + - splitting long functions into smaller ones, + - adding comments, etc. +1. "Unskip" the next test and repeat from step 1. + +Repeat these steps until you have unskipped all the tests. +Once all the tests are passing, congratulations, you have solved the exercise! + +Exactly how tests are "unskipped" (or activated) depends on the track. +For some tracks, it might be commenting or removing an annotation. +For some tracks, it might be changing an attribute from true to false. +Take the time to read [the documentation for your track][track-docs]; it will explain these details. + +For tracks that don't skip the tests, applying this workflow may be as straightforward as commenting out the tests and uncommenting them one-by-one. + +## Rationale for Test-Driven Development + +While it may seem like "putting the cart before the horse", there are several good reasons why you might want to write unit tests before writing the implementation code. + +1. Design. + It forces you to think first about your program's [interface][api] (how it exposes its functionality to the world), instead of jumping straight into how you will implement the code. + Having a well-designed (and testable!) interface is often more important than having an efficient implementation. + +1. Discipline. + Writing tests is often seen as a chore or an afterthought; writing the tests _first_ guarantees that at the end of the day you will have written enough unit tests to cover most or all of your code's functionality (rather than possibly never getting around to it). + +1. Less Work. + If you apply a tight cycle of write one test, then write the code to implement that test, then write the next test, your code ends up growing organically. + This often (though not always) leads to less wasted effort; you end up writing all the code you need, and none of the code you don't need. + +## Further reading + +- [About Test-First Teaching][test-first] at the archived TestFirst\.org site. +- [Test-driven development][tdd-wiki] at Wikipedia. +- [Test Driven Development][tdd-python] on the Python track. + +[track-docs]: https://exercism.org/docs/tracks +[test-first]: https://web.archive.org/web/20220918221108/http://testfirst.org/about +[tdd-wiki]: https://en.wikipedia.org/wiki/Test-driven_development +[tdd-python]: https://exercism.org/docs/tracks/python/test-driven-development +[api]: https://en.wikipedia.org/wiki/API From a563c7db9829e014f950c0e75893545a699dc70a Mon Sep 17 00:00:00 2001 From: Kevin Bloch Date: Sat, 24 May 2025 18:03:21 +0200 Subject: [PATCH 126/129] Update config-json.md (#581) Addresses #480 . Corresponding change already implemented in the linter. --- building/tracks/config-json.md | 1 + 1 file changed, 1 insertion(+) diff --git a/building/tracks/config-json.md b/building/tracks/config-json.md index f65cff3a..104508e5 100644 --- a/building/tracks/config-json.md +++ b/building/tracks/config-json.md @@ -386,6 +386,7 @@ Tags are specified in the top-level `tags` field which is defined as an array of ### Typing - `typing/static`: the language uses static typing +- `typing/gradual`: the language uses gradual typing - `typing/dynamic`: the language uses dynamic typing - `typing/strong`: the language uses strong typing - `typing/weak`: the language uses weak typing From c507913b211b6bb6d13dd28809887f0f485c9a90 Mon Sep 17 00:00:00 2001 From: keiravillekode Date: Wed, 28 May 2025 00:13:07 +1000 Subject: [PATCH 127/129] CLI: describe the slug placeholder (#582) --- building/tooling/cli.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/building/tooling/cli.md b/building/tooling/cli.md index 83d79b13..e7c0511c 100644 --- a/building/tooling/cli.md +++ b/building/tooling/cli.md @@ -10,10 +10,11 @@ You can add support for your language by adding an entry to that [configuration ### Test command placeholders -There are two placeholders that can be used in the track-specific command: +There are three placeholders that can be used in the track-specific command: - `{{test_files}}`: a space-separated list of the test files (as found in the `.files.test` key in the exercise's `.meta/config.json` file) - `{{solution_files}}`: a space-separated list of the solution files (as found in the `.files.solution` key in the exercise's `.meta/config.json` file) +- `{{slug}}`: the exercise slug (as found in the exercise's `slug` key in the tracks's root `config.json` file) Here is an [example pull request][example-pr] that adds support for the Arturo language. From 4467a8893288f5057ec7f327585e93510d050db7 Mon Sep 17 00:00:00 2001 From: Colin Leach Date: Tue, 2 Sep 2025 01:41:49 -0700 Subject: [PATCH 128/129] Add lines on links inside admonition blacks (#585) Co-authored-by: Colin Leach --- building/markdown/markdown.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/building/markdown/markdown.md b/building/markdown/markdown.md index d58a11f4..ff768915 100644 --- a/building/markdown/markdown.md +++ b/building/markdown/markdown.md @@ -115,6 +115,10 @@ You can include code: ```ruby str = "Hello, World" ``` + +A [reference link][forum-link] must have the link definition inside the block. + +[forum-link]: https://forum.exercism.org/t/link-references-in-special-blocks-admonitions-are-not-rendered/3803 ~~~~ ```` From e1f6075e3a9cb44358c2b5f4ae21929f0f60e4b2 Mon Sep 17 00:00:00 2001 From: Rich Park Date: Tue, 30 Sep 2025 15:18:36 +0100 Subject: [PATCH 129/129] clarify conditions for overall error status (#587) * clarify conditions for overall error status --- building/tooling/test-runners/interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/building/tooling/test-runners/interface.md b/building/tooling/test-runners/interface.md index 04e080a9..5d8132df 100644 --- a/building/tooling/test-runners/interface.md +++ b/building/tooling/test-runners/interface.md @@ -52,9 +52,9 @@ The following overall statuses are valid: - `fail`: At least one test has the status `fail` or `error` - `error`: No test was executed (this usually means a compile error or a syntax error) -The `error` status should _only_ be used if **none of the tests were run**. +The `error` status should _only_ be used if **all of the tests errored**. For compiled languages this is generally a result of the code not being able to compile. -For interpreted languages, this is generally the result of a syntax error that stops the file from parsing. +For interpreted languages this is a runtime error, such as a syntax error that stops the file from parsing. #### Message