From 3b2b1ab8d64cb21a91e7814bb5e79ddf33e4ac91 Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Tue, 2 Jun 2026 21:10:59 -0700 Subject: [PATCH 1/2] Add a workflow to check practice exercises are sorted --- .github/workflows/sorted.yml | 40 +++++++++++++++++++++++++++++++++++ README.md | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 .github/workflows/sorted.yml diff --git a/.github/workflows/sorted.yml b/.github/workflows/sorted.yml new file mode 100644 index 0000000..f6043d6 --- /dev/null +++ b/.github/workflows/sorted.yml @@ -0,0 +1,40 @@ +name: Check practice exercises are sorted + +on: + workflow_call: + inputs: + ordering: + description: "How exercises are ordered" + required: true + type: string + +jobs: + check: + name: Check exercises + runs-on: ubuntu-slim + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: Check if practice exercises are sorted + env: + ORDERING: ${{ inputs.ordering }} + run: | + diff <( jq -r '.exercises.practice[].slug' config.json ) <( + jq -r ' + # Bucket by displayed difficulty. 0-3: easy; 4-7: medium; 8-10: hard. + def bucket(i): [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3][i]; + + # Code injection to inject the sort literal expression. + def sort: sort_by('"$ORDERING"'); + + .exercises.practice | + # Add displayed difficulty + map(.bucket = bucket(.difficulty)) | + # Leave hello-world at the top + if .[0].slug == "hello-world" then + [.[0]] + (.[1:] | sort) + else + sort + end | + map(.slug)[] + ' config.json) diff --git a/README.md b/README.md index f542064..794f760 100644 --- a/README.md +++ b/README.md @@ -295,5 +295,46 @@ jobs: uses: exercism/github-actions/.github/workflows/labels.yml@main ``` +## Reusable workflow: sorted + +The `sorted` reusable workflow checks if the practice exercises on a track are sorted. + +### Inputs + +The workflow takes a single input, `ordering`, that determines how sorting works. +The `ordering` input is passed to `sort_by()` and must be a valid JQ path expression. +A `bucket` key is added to the data, which indicates the difficulty "bucket" or "displayed" difficulty (easy, medium, hard). + +Some common ordering values: + +* **".name"**: sort alphabetically by the displayed name +* **".slug"**: sort alphabetically by the slug +* **".bucket"**: sort by the difficulty bucket (easy, medium, hard) +* **".bucket, .slug"**: sort by the difficulty bucket then by slug +* **".difficulty"**: sort by the difficulty value (1, 2, 3, ... 10) +* **".difficulty, .name"**: sort by the difficulty then by name +* **".difficulty, .slug"**: sort by the difficulty then by slug + +### Example + +```yaml +name: Exercise order + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + call-gha-workflow: + name: check + uses: exercism/github-actions/.github/workflows/sorted.yml@main + with: + ordering: ".bucket, .slug" +``` + [configlet]: https://exercism.org/docs/building/configlet [configlet-lint]: https://exercism.org/docs/building/configlet/lint From cc1e106c871217d4b1de66af28bebf3a4329a135 Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Wed, 3 Jun 2026 15:58:59 -0700 Subject: [PATCH 2/2] Add `.lowercase_name` --- .github/workflows/sorted.yml | 2 ++ README.md | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sorted.yml b/.github/workflows/sorted.yml index f6043d6..8ce3103 100644 --- a/.github/workflows/sorted.yml +++ b/.github/workflows/sorted.yml @@ -30,6 +30,8 @@ jobs: .exercises.practice | # Add displayed difficulty map(.bucket = bucket(.difficulty)) | + # Add lowercase name + map(.lowercase_name = (.name | ascii_downcase)) | # Leave hello-world at the top if .[0].slug == "hello-world" then [.[0]] + (.[1:] | sort) diff --git a/README.md b/README.md index 794f760..815a195 100644 --- a/README.md +++ b/README.md @@ -299,15 +299,22 @@ jobs: The `sorted` reusable workflow checks if the practice exercises on a track are sorted. +### Extra fields + +Some extra fields are added to the exercise data for sorting. + +* `bucket` is added, which indicates the difficulty "bucket" or "displayed" difficulty (easy, medium, hard). +* `lowercase_name` is added to allow sorting by exercise name, case insensitive. + ### Inputs -The workflow takes a single input, `ordering`, that determines how sorting works. +The workflow takes a single input, `ordering` (required), that determines how sorting works. The `ordering` input is passed to `sort_by()` and must be a valid JQ path expression. -A `bucket` key is added to the data, which indicates the difficulty "bucket" or "displayed" difficulty (easy, medium, hard). -Some common ordering values: +Some valid ordering values: * **".name"**: sort alphabetically by the displayed name +* **".lowercase_name"**: sort alphabetically (case insensitive) by the displayed name * **".slug"**: sort alphabetically by the slug * **".bucket"**: sort by the difficulty bucket (easy, medium, hard) * **".bucket, .slug"**: sort by the difficulty bucket then by slug