From 214d2b0158b177e586faed89b5f7bce5ae952273 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 10:38:43 +0800 Subject: [PATCH 1/9] Restructure and update developer documentation --- README.md | 73 ++++++++- docs/app/command-reference.md | 63 ++++++++ docs/app/configuration.md | 118 ++++++++++++++ docs/app/download-and-verify-flow.md | 75 +++++++++ docs/app/index.md | 30 ++++ docs/app/progress-integration.md | 52 +++++++ docs/architecture/deployment-pipeline.md | 29 ---- docs/architecture/exercise-structure.md | 98 ------------ docs/architecture/hands-on-structure.md | 52 ------- docs/architecture/index.md | 6 - docs/architecture/verification-workflow.md | 12 -- docs/contributing/exercise.md | 128 ---------------- docs/contributing/hands-on.md | 93 ----------- docs/contributing/index.md | 7 - docs/contributing/setup.md | 33 ---- .../download-workflow.md | 42 +++-- docs/exercises/exercise-structure.md | 114 ++++++++++++++ docs/exercises/exercise-utils.md | 26 ++++ docs/exercises/exercise.md | 145 ++++++++++++++++++ docs/exercises/hands-on-structure.md | 51 ++++++ docs/exercises/hands-on.md | 91 +++++++++++ docs/exercises/index.md | 27 ++++ docs/exercises/testing-patterns.md | 107 +++++++++++++ docs/exercises/verification-workflow.md | 33 ++++ docs/getting-started/common-workflows.md | 52 +++++++ docs/getting-started/index.md | 27 ++++ docs/getting-started/setup.md | 81 ++++++++++ docs/libraries/git-autograder.md | 86 +++++++++++ docs/libraries/index.md | 29 ++++ .../published-packages.md} | 6 +- docs/libraries/repo-smith.md | 106 +++++++++++++ docs/overview/choosing-where-to-change.md | 48 ++++++ docs/overview/ecosystem-map.md | 36 +++++ docs/overview/index.md | 35 +++++ docs/tooling/developer-tooling.md | 15 -- docs/tooling/exercise-utils.md | 28 ---- docs/tooling/index.md | 6 - index.markdown | 33 ++-- 38 files changed, 1550 insertions(+), 543 deletions(-) create mode 100644 docs/app/command-reference.md create mode 100644 docs/app/configuration.md create mode 100644 docs/app/download-and-verify-flow.md create mode 100644 docs/app/index.md create mode 100644 docs/app/progress-integration.md delete mode 100644 docs/architecture/deployment-pipeline.md delete mode 100644 docs/architecture/exercise-structure.md delete mode 100644 docs/architecture/hands-on-structure.md delete mode 100644 docs/architecture/index.md delete mode 100644 docs/architecture/verification-workflow.md delete mode 100644 docs/contributing/exercise.md delete mode 100644 docs/contributing/hands-on.md delete mode 100644 docs/contributing/index.md delete mode 100644 docs/contributing/setup.md rename docs/{architecture => exercises}/download-workflow.md (65%) create mode 100644 docs/exercises/exercise-structure.md create mode 100644 docs/exercises/exercise-utils.md create mode 100644 docs/exercises/exercise.md create mode 100644 docs/exercises/hands-on-structure.md create mode 100644 docs/exercises/hands-on.md create mode 100644 docs/exercises/index.md create mode 100644 docs/exercises/testing-patterns.md create mode 100644 docs/exercises/verification-workflow.md create mode 100644 docs/getting-started/common-workflows.md create mode 100644 docs/getting-started/index.md create mode 100644 docs/getting-started/setup.md create mode 100644 docs/libraries/git-autograder.md create mode 100644 docs/libraries/index.md rename docs/{tooling/libraries.md => libraries/published-packages.md} (83%) create mode 100644 docs/libraries/repo-smith.md create mode 100644 docs/overview/choosing-where-to-change.md create mode 100644 docs/overview/ecosystem-map.md create mode 100644 docs/overview/index.md delete mode 100644 docs/tooling/developer-tooling.md delete mode 100644 docs/tooling/exercise-utils.md delete mode 100644 docs/tooling/index.md diff --git a/README.md b/README.md index a2a898d..f8b1dc2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ -# developers +# Developer Documentation + Developer documentation about all things Git-Mastery + +This repository contains the source for the Git-Mastery developer docs site, built with Jekyll and the Just the Docs theme. + +## Prerequisites + +Install the following tools first: + +- Ruby 3.2.2 + + | Note: the installation commands below are for macOS using Homebrew only. Adjust as needed for your OS and package manager. + + ```bash + brew install rbenv ruby-build + rbenv install 3.2.2 + echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc + source ~/.zshrc + rbenv global 3.2.2 + ruby -v + ``` + +- Bundler + + ```bash + gem install bundler + ``` + +## Run locally + +From the repository root: + +1. Install dependencies: + + ```bash + bundle install + ``` + +2. Start the local docs server: + + ```bash + bundle exec jekyll serve --livereload + ``` + +3. Open the site at: + + ```text + http://127.0.0.1:4000/developers/ + ``` + +Note: this repository uses `baseurl: "/developers"`, so the local path includes `/developers/`. + +## Build for production + +To generate a static build in `_site/`: + +```bash +bundle exec jekyll build +``` + +## Authoring notes + +- Add or edit docs in `docs/`. +- Use frontmatter fields like `title`, `parent`, and `nav_order` so pages appear correctly in navigation. +- Keep links and examples consistent with the current Git-Mastery workflows. + +## Troubleshooting + +- `bundle: command not found`: install Bundler with `gem install bundler`. +- Shell still reports wrong Ruby version: run `rbenv version` to confirm 3.2.2 is active; if not, run `rbenv global 3.2.2` and restart the terminal. +- Port `4000` already in use: run `bundle exec jekyll serve --port 4001`. +- Styling or content not updating: restart `jekyll serve` and hard refresh your browser. diff --git a/docs/app/command-reference.md b/docs/app/command-reference.md new file mode 100644 index 0000000..1b86c05 --- /dev/null +++ b/docs/app/command-reference.md @@ -0,0 +1,63 @@ +--- +title: Command reference +parent: App +nav_order: 1 +--- + +# Command reference + +The `gitmastery` CLI is the main entrypoint for local Git-Mastery workflows. + +If the CLI is launched without a subcommand, it starts an interactive REPL instead of exiting immediately. + +## Main command areas + +- `setup`: initialize local Git-Mastery configuration +- `check`: verify Git and GitHub CLI prerequisites +- `download`: download a hands-on or exercise +- `verify`: verify the current exercise attempt +- `progress`: inspect, reset, or sync learner progress +- `version`: print the current app version + +## Command groups + +### `setup` + +- Creates a new Git-Mastery root directory +- Writes `.gitmastery.json` +- Creates a local `progress/` folder with `progress.json` + +### `check` + +- `gitmastery check git`: checks Git installation, minimum version, `user.name`, and `user.email` +- `gitmastery check github`: checks GitHub CLI installation, authentication, and `delete_repo` scope + +These checks are also invoked automatically by other commands when required by an exercise or workflow. + +### `download` + +- Downloads either a hands-on or an exercise from the configured exercises source +- Hands-ons are identified by the `hp-` prefix +- Exercises use `.gitmastery-exercise.json` plus `download.py` to set up the student workspace + +### `verify` + +- Loads the current exercise's metadata +- Executes the exercise's `verify.py` +- Prints the grading result +- Records progress locally and optionally syncs it remotely + +### `progress` + +- `gitmastery progress show`: show the latest known status for each exercise +- `gitmastery progress reset`: reset the current exercise workspace and remove its saved progress entries +- `gitmastery progress sync on`: connect the local tracker to a GitHub fork of the progress repository +- `gitmastery progress sync off`: remove remote sync and keep a local-only progress tracker + +## REPL behavior + +The REPL accepts both Git-Mastery commands and shell commands: + +- Use `/verify` or `gitmastery verify` for Git-Mastery commands +- Use `cd` to change directories inside the REPL +- Any other command is executed through the local shell diff --git a/docs/app/configuration.md b/docs/app/configuration.md new file mode 100644 index 0000000..2673267 --- /dev/null +++ b/docs/app/configuration.md @@ -0,0 +1,118 @@ +--- +title: Configuration +parent: App +nav_order: 2 +--- + +# Configuration + +The app uses local configuration files to track Git-Mastery setup and exercise metadata. + +## Important files + +- `.gitmastery.json`: local Git-Mastery configuration +- `.gitmastery-exercise.json`: metadata for a downloaded exercise + +## `.gitmastery.json` + +This file lives at the Git-Mastery root created by `gitmastery setup`. + +### Main fields + +- `progress_local`: whether local progress tracking is enabled +- `progress_remote`: whether progress sync to GitHub is enabled +- `exercises_source`: where the app should fetch exercises from + +### `exercises_source` + +The app supports both remote and local exercise sources. + +Remote source shape: + +```json +{ + "type": "remote", + "username": "git-mastery", + "repository": "exercises", + "branch": "main" +} +``` + +Local source shape: + +```json +{ + "type": "local", + "repo_path": "/absolute/path/to/exercises" +} +``` + +When `type` is `local`, the app copies the local exercises repository into a temporary directory and reads exercises from there. This is useful when developing `app` and `exercises` together. + +### Local co-development example + +If you are working on `app` and want it to use a local checkout of `exercises`, update `.gitmastery.json` like this: + +```json +{ + "progress_local": true, + "progress_remote": false, + "exercises_source": { + "type": "local", + "repo_path": "/Users/you/dev/exercises" + } +} +``` + +This lets `gitmastery download` and `gitmastery verify` read exercise content from your local `exercises` clone instead of GitHub. + +### Remote source override example + +You can also point the app at a fork or branch of `exercises`: + +```json +{ + "progress_local": true, + "progress_remote": false, + "exercises_source": { + "type": "remote", + "username": "", + "repository": "exercises", + "branch": "feature/my-branch" + } +} +``` + +## `.gitmastery-exercise.json` + +This file lives in each downloaded exercise root. + +### Main fields + +- `exercise_name` +- `tags` +- `requires_git` +- `requires_github` +- `base_files` +- `exercise_repo` +- `downloaded_at` + +### `exercise_repo` + +The app currently understands these repository modes: + +- `local` +- `remote` +- `ignore` +- `local-ignore` + +These fields control whether the app creates a local repository, clones a remote repository, skips repository creation, or creates a folder without initializing Git. + +For remote exercises, the app may also read `fork_all_branches` when creating a student fork. + +## How the app uses these files + +- `setup` writes `.gitmastery.json` +- `download` reads the Git-Mastery root config, then writes or updates `.gitmastery-exercise.json` +- `verify` reads the exercise config to determine how the exercise should be graded +- `progress` reads the Git-Mastery root config to determine whether local or remote sync is enabled diff --git a/docs/app/download-and-verify-flow.md b/docs/app/download-and-verify-flow.md new file mode 100644 index 0000000..501abf0 --- /dev/null +++ b/docs/app/download-and-verify-flow.md @@ -0,0 +1,75 @@ +--- +title: Download and verify flow +parent: App +nav_order: 3 +--- + +# Download and verify flow + +This page explains how the `gitmastery` CLI coordinates exercise content from `exercises` with runtime behavior in `app`. + +## Download flow + +When a user runs `gitmastery download `, the app first determines whether `` is a hands-on or an exercise. + +### Hands-ons + +For hands-ons, the app: + +1. checks that the matching `hands_on/*.py` file exists in the exercises source +2. creates a fresh local folder +3. loads the hands-on file as a Python namespace +4. checks Git and GitHub prerequisites if `__requires_git__` or `__requires_github__` are set +5. executes `download(...)` + +The hands-on script receives a `repo-smith` helper object as `rs`, which is useful for file and repository setup without needing a fully managed student repository. + +### Exercises + +For exercises, the app: + +1. checks that `.gitmastery-exercise.json` exists in the exercises source +2. creates a fresh exercise root directory +3. downloads the base files `.gitmastery-exercise.json` and `README.md` +4. loads the exercise config locally +5. checks Git and GitHub prerequisites when required +6. downloads any configured `base_files` +7. sets up the working repository or working folder according to `exercise_repo.repo_type` +8. loads `download.py` and executes `setup(...)` + +## Working repository modes + +The `exercise_repo.repo_type` field changes how download behaves: + +- `local`: create a local working folder, optionally initialize Git, then run `setup(...)` +- `remote`: clone a GitHub repository or fork-and-clone it, then run `setup(...)` +- `ignore`: skip managed repository creation and leave the student at the exercise root +- `local-ignore`: create a working folder but do not run `git init` + +## Verify flow + +When a user runs `gitmastery verify`, the app: + +1. confirms that the current directory belongs to a downloaded exercise +2. loads the local exercise config +3. fetches `verify.py` from the configured exercises source +4. constructs `GitAutograderExercise` +5. executes `verify(exercise)` +6. converts wrong-answer and invalid-state exceptions into structured output +7. prints the result to the terminal +8. updates local progress and optionally remote progress + +## Where code lives + +- `app/app/commands/download.py`: download orchestration +- `app/app/commands/verify.py`: verify orchestration and progress submission +- `app/app/utils/gitmastery.py`: exercises source loading and dynamic namespace execution +- `exercises//download.py`: exercise-specific setup logic +- `exercises//verify.py`: exercise-specific grading logic + +## Why this split exists + +- `app` owns runtime behavior, safety checks, and student-facing orchestration +- `exercises` owns content and exercise-specific logic +- `git-autograder` owns reusable verification abstractions +- `repo-smith` supports deterministic repository setup for tests and downloads diff --git a/docs/app/index.md b/docs/app/index.md new file mode 100644 index 0000000..487bf1a --- /dev/null +++ b/docs/app/index.md @@ -0,0 +1,30 @@ +--- +title: App +nav_order: 5 +has_children: true +--- + +# App + +The [`app`](https://github.com/git-mastery/app) repository contains the `gitmastery` CLI. + +## What this section covers + +- CLI responsibilities in the wider Git-Mastery ecosystem +- The main command groups and flows +- Configuration files and local developer behavior +- How the app interacts with exercise verification and progress tracking + +## Main responsibilities + +- Set up a local Git-Mastery workspace +- Fetch hands-ons and exercises from the configured exercises source +- Execute verification logic from the `exercises` repository +- Maintain local and remote progress tracking + +## Suggested reading + +1. [Command reference](/developers/docs/app/command-reference) +2. [Configuration](/developers/docs/app/configuration) +3. [Download and verify flow](/developers/docs/app/download-and-verify-flow) +4. [Progress integration](/developers/docs/app/progress-integration) diff --git a/docs/app/progress-integration.md b/docs/app/progress-integration.md new file mode 100644 index 0000000..d23babb --- /dev/null +++ b/docs/app/progress-integration.md @@ -0,0 +1,52 @@ +--- +title: Progress integration +parent: App +nav_order: 3 +--- + +# Progress integration + +The app is responsible for writing and syncing exercise progress based on verification results. + +Progress data is part of the wider Git-Mastery ecosystem and can be consumed by other repositories such as `progress-dashboard`. + +## Local progress + +`gitmastery setup` creates a `progress/` folder inside the Git-Mastery root. + +After each `gitmastery verify` run, the app appends a new entry to `progress/progress.json` with: + +- `exercise_name` +- `started_at` +- `completed_at` +- `comments` +- `status` + +The app keeps a history of attempts, but `gitmastery progress show` displays the latest known entry for each exercise. + +## Remote sync + +When a user runs `gitmastery progress sync on`, the app: + +1. Checks Git and GitHub CLI prerequisites +2. Creates or reuses the user's fork of the progress repository +3. Replaces the local `progress/` folder with a clone of that fork +4. Merges local and remote progress entries +5. Pushes changes and opens a PR if needed + +When remote sync is enabled, later `verify` and `progress reset` runs also update the remote fork. + +## Turning sync off + +`gitmastery progress sync off` deletes the remote progress fork, switches `.gitmastery.json` back to local-only tracking, and recreates a plain local `progress/` folder containing the existing progress entries. + +## Reset behavior + +`gitmastery progress reset` does two things for the current exercise: + +- recreates the exercise workspace +- removes saved progress entries for that exercise from `progress.json` + +{: .reference } + +See [Download and verify flow](/developers/docs/app/download-and-verify-flow) for the command orchestration that happens before progress is updated. diff --git a/docs/architecture/deployment-pipeline.md b/docs/architecture/deployment-pipeline.md deleted file mode 100644 index 703661c..0000000 --- a/docs/architecture/deployment-pipeline.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Deployment Pipeline -parent: Architecture -nav_order: 5 ---- - -## Exercises - -Unit tests and [exercise configuration](/developers/docs/architecture/exercise-structure/#configuration-structure) will be run during the PR and every time a branch is merged into `main`. - -These are controlled through the `CI` action on Github Actions ([here](https://github.com/git-mastery/exercises/actions/workflows/ci.yml)). - -## Git-Mastery app - -A new version of the Git-Mastery application is published when a new tag is pushed to `main`. - -The versioning is done as follows: `..` where - -1. ``: for when major changes to the app occur (i.e. rewrites) where the application could break entirely -2. ``: for when critical bug fixes occur that users should be aware of -3. ``: for when minor bug fixes occur (i.e. cosmetic) that users don't need to update to - -The app automatically checks for the latest version published and warns users if their local version is out of date if the `` or `` versions are wrong. - -If you do not have permissions to push a new tag, contact for assistance. - -{: .note } - -For more information for packaging for Linux, refer to [this Notion page](https://woojiahao.notion.site/linux-packaging-226f881eda0580d68bc8dc6f8e1d5d0d?source=copy_link). diff --git a/docs/architecture/exercise-structure.md b/docs/architecture/exercise-structure.md deleted file mode 100644 index db5080b..0000000 --- a/docs/architecture/exercise-structure.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Exercise structure -parent: Architecture -nav_order: 2 ---- - -Exercises follow a fixed structure for the Git-Mastery app to pick up on: - -```text - -├── .gitmastery-exercise.json -├── README.md -├── __init__.py -├── download.py -├── res -│ └── ... -├── tests -│ ├── specs -│ │ └── base.yml -│ └── test_verify.py -└── verify.py -``` - -- `.gitmastery-exercise.json`: contains the exercise configuration -- `README.md`: contains the instructions for the exercise for the students to attempt -- `download.py`: contains the download instructions to setup the student's exercise -- `verify.py`: contains the verification script for the exercise attempt -- `res/`: contains resources that are available to students (see this section about [types of resources](#types-of-resources)) -- `tests/specs/`: contains specification files written using [`repo-smith`](https://github.com/git-mastery/git-autograder) -- `tests/test_verify.py`: contains unit tests for verification script - -## What students see - -When a student downloads an exercise, they will see the following folder structure: - -```text - -├── .gitmastery-exercise.json -├── README.md -└── - ├── .git - └── ... -``` - -The root of the exercise will contain the `README.md` and `.gitmastery-exercise.json` configured from your template. - -It also contains the sub-folder configured in `.gitmastery-exercise.json`, which is where students will attempt the exercise. - -## Configuration structure - -`.gitmastery-exercise.json` is used to tell the [Git-Mastery app](https://git-mastery.github.io/app) how to setup the student's exercise. - -{: .note } - -We opted to use a standardized configuration for exercises because they often follow a certain shape for being setup so it is easier if the application -standardizes the setup via the exercise configuration. - -The `new.sh` script should have already generated one for you, but you may change your mind with the configuration and modify the file directly: - -- `exercise_name`: raw exercise name that will be indexed; recommended to use [kebab case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) -- `tags`: used during indexing on the [exercise directory](https://git-mastery.github.io/exercises) -- `requires_git`: performs a check to ensure that Git is installed and the user has already configured their `user.name` and `user.email` -- `requires_github`: performs a check to ensure that Github CLI is installed and the user has already authenticated themselves -- `base_files`: specifies the files from `res/` to be downloaded into the exercise root; typically used for the `answers.txt` (more about grading types [here]()) -- `exercise_repo`: controls the sub-folder that is generated; this is where students work on the exercise - - `repo_type`: `local` or `remote`; if `remote`, then the sub-folder is generated from a remote repository - - `repo_name`: name of the sub-folder; required for both `repo_type` - - `init`: determines if `git init` is run for the sub-folder; required only for `local` - - `create_fork`: determines if a fork is created on the user's Github account; required only for `remote` - - `repo_title`: name of the remote repository to fork + clone; required only for `remote` - -## Exercise resource types - -There are two distinct types of resources: - -1. Base files: configured through the `base_files` property in `.gitmastery-exercise.json` in your template; files located in `res/` are downloaded to the root of the exercise folder - - ```text - - ├── .gitmastery-exercise.json - ├── README.md - ├── <-- here - └── - ├── .git - └── ... - ``` - -2. Resources: configured through the `__resources__` field in `download.py`; supporting files for the exercise with files located in `res/` downloaded into the sub folder - - ```text - - ├── .gitmastery-exercise.json - ├── README.md - ├── - └── - ├── .git - └── <-- here - ``` diff --git a/docs/architecture/hands-on-structure.md b/docs/architecture/hands-on-structure.md deleted file mode 100644 index 0d0493d..0000000 --- a/docs/architecture/hands-on-structure.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Hands-on Structure -parent: Architecture -nav_order: 1 ---- - -All hands-ons are stored within the `hands_on` folder of the [`exercises`](https://github.com/git-mastery/exercises). - -They are represented by a single Python file, whose name is the hands-on ID (replacing `_` with `-`). - -Hands-ons are not graded and the progress is not tracked. They are only present to help setup the student's folder structure for a given lesson! - -{: .note } - -Git-Mastery uses the format `hp-` for hands-on names (e.g., `hp-init-repo`), to differentiate them from exercise names. Internally, we use a `hands_on/` folder (instead of the `hp-` prefix) to differentiate hands-on files from exercise files (e.g., `hands_on/init_repo.py`). - -## File structure - -Since the hands-on is only comprised of a single file, there isn't a lot of complexity to it. Given below is an example: - -```python -import os - -from exercise_utils.cli import run_command -from exercise_utils.file import append_to_file, create_or_update_file -from exercise_utils.git import add, init - -__requires_git__ = True -__requires_github__ = False - - -def download(verbose: bool): - os.makedirs("things") - os.chdir("things") - init(verbose) - create_or_update_file( - "fruits.txt", - """ - apples - bananas - cherries - """, - ) - add(["fruits.txt"], verbose) - run_command(["git", "add", "fruits.txt"], verbose) - append_to_file("fruits.txt", "dragon fruits") - -``` - -The setup instructions of the hands-on go under the `download` function. - -`__requires_git__` and `__requires_github__` tells the Git-Mastery app whether to run automatic verification that the student has already setup Git and/or Github CLI correctly! diff --git a/docs/architecture/index.md b/docs/architecture/index.md deleted file mode 100644 index bbe47f0..0000000 --- a/docs/architecture/index.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Architecture -nav_order: 3 ---- - -These are the various components of the Git-Mastery ecosystem! diff --git a/docs/architecture/verification-workflow.md b/docs/architecture/verification-workflow.md deleted file mode 100644 index 5b34fca..0000000 --- a/docs/architecture/verification-workflow.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Verification Workflow -parent: Architecture -nav_order: 4 ---- - -```mermaid -flowchart -a[Verify exercise] --> b["Check if in exercise (using .gitmastery-exercise.json)"] -b -- not in exercise --> c[Cancel] -b -- in exercise --> d[Execute verification script on exercise folder] -``` diff --git a/docs/contributing/exercise.md b/docs/contributing/exercise.md deleted file mode 100644 index d6e1309..0000000 --- a/docs/contributing/exercise.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Exercise -parent: Contributing -nav_order: 3 ---- - -1. TOC -{:toc} - -## Before contributing - -If you are proposing a new exercise (i.e., not implementing an [already approved exercise proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22exercise%20discussion%22%20label%3A%22help%20wanted%22)) make sure that you have done the following: - -- [ ] Create an [exercise discussion](https://github.com/git-mastery/exercises/issues/new?template=exercise_discussion.yaml) -- [ ] Obtain approval on the exercise -- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) - -## Create a new exercise - -Use the provided `new.sh` script to generate the scaffolding for a new exercise: - -```bash -./new.sh -``` - -The script will first prompt if you want to create a hands-on or exercise: - -Enter `exercise` or `e` to create a new exercise. - -Then, the script will prompt you for: - -1. The name of the exercise -- likely to be specified in the corresponding exercise discussion (you should be using kebab case for the exercise name) -2. The exercise tags (split by space) -- likely to be specified in the corresponding exercise discussion -3. The exercise configuration (read the [exercise configuration](/developers/docs/architecture/exercise-structure#configuration-structure) section for more info on this) - -{: .reference } - -Refer to the [exercise structure document](/developers/docs/architecture/exercise-structure) for more information about the folder structure generated. - -## Download setup - -The `download.py` contains the instructions to setup the local repository. - -{: .reference } - -For more information about how Git-Mastery downloads exercises, refer to the [Download Workflow](/developers/docs/architecture/download-workflow) - -{: .note } - -> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke any command and `exercise_utils.file.create_or_update_file` to create or update a given file. -> -> For the full list of utility functions, refer [here](/developers/docs/tooling/exercise-utils). - -These are some references for download setups for other exercises: - -- [ignoring-somethings](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/ignoring_somethings/download.py) -- [branch-bender](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/branch_bender/download.py) -- [amateur-detective](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/amateur_detective/download.py) - -### Download conventions - -1. Any operations should use OS agnostic options (e.g. opting to use `shutil.rmtree` over `run_command(["rm"])`) -2. If you need to compare the states before and after the student has started to add commits, use the given "start tag" as `git-autograder` is designed to read that if necessary -3. For any commands that require `gh` (Github CLI), make sure that the `requires_github` configuration is set to `true` so that the app will automatically check that the student has correctly setup Github CLI - -### Testing downloads - -To test that your download script works, we have provided a script to simulate the download process in this repository for you to verify. - -```bash -./test-download.sh -``` - -You can find the downloaded repository under the `test-downloads/` folder. - -## Verification setup - -The verification process is controlled by the `verify.py`. - -{: .reference } - -For more information about how Git-Mastery verifies exercise attempts, refer to the [Verification Workflow](/developers/docs/architecture/verification-workflow) - -The [`git-autograder`](https://github.com/git-mastery/git-autograder) is built as a wrapper around [`GitPython`](https://github.com/gitpython-developers/GitPython). As a result, if you are writing any verification scripts and there is no available helper function with `git-autograder`, you can fall back to the underlying `Repo` object: - -```python -def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: - # Access the underlying GitPython repo: - exercise.repo.repo - - return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) -``` - -Refer to existing `verify.py` scripts to understand what are the available helper functions to streamline the grading. Open an issue if there is something that is not yet supported or if you have a question. - -Some examples of verifications: - -- [branch-bender](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/branch_bender/verify.py) -- [conflict-mediator](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/conflict_mediator/verify.py) - -### Verification conventions - -1. Store the comments of the verification as constants so that they can be imported and used reliably in unit tests -2. For any remote behavior to verify, provide a mock to substitute the behavior in the unit tests - -### Testing verification - -To test the verification, we rely on [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate exercise states and write unit tests to verify the verification script's behavior. You don't need to simulate the entire flow, just the end states that you require for your verification script. - -Refer to existing `test_verify.py` to see examples of unit testing the verification script. - -You can run the unit tests of your exercise via: - -```bash -./test.sh -``` - -## Submitting the exercise for review - -Create a pull request from your fork using the provided pull request template. - -Fill in all of the details necessary. - -## Example - -1. Exercise discussion: -2. Remote repository request: -3. Exercise PR: diff --git a/docs/contributing/hands-on.md b/docs/contributing/hands-on.md deleted file mode 100644 index 955706a..0000000 --- a/docs/contributing/hands-on.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Hands-on -parent: Contributing -nav_order: 2 ---- - -1. TOC -{:toc} - -## What is a hands-on? - -[Lessons accompanying Git-Mastery App](https://nus-cs2103-ay2526s1.github.io/website/se-book-adapted/git-trail/index.html) contains hands-on practicals for students to get 'hands-on' experience of the Git concepts covered by the lesson. Some of those hands-on practicals need to set up a 'sandbox' containing the required folders, files, repos before the student can do the practical. Git-Mastery app can set up that sandbox for students so that they can get to the 'practical' part more easily (we refer to this as 'downloading' the hands-on), without having to set up the sandbox manually. For example, a student can run `gitmastery download ` (e.g., `gitmastery download hp-init-repo`) to set up the sandbox for a specific hands-on practical. - -Some examples of how Git-Mastery app helps to create the sandbox for a hands-on practical can be seen in [this Git Tour](https://nus-cs2103-ay2526s1.github.io/website/book/gitAndGithub/trail/recordingFolderHistory/index.html) (see T1L3, T1L4). - -## Before contributing - -New contributors are recommended to start by implementing an [already approved hands-on proposal]((https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22hands-on%20discussion%22%20label%3A%22help%20wanted%22)). - -If you are proposing a new hands-on instead, make sure that you have done the following: - -- [ ] Create an [hands-on discussion](https://github.com/git-mastery/exercises/issues/new?template=hands_on_discussion.yaml) -- [ ] Obtain approval on the hands-on -- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) - -## Implementing a hands-on - -First, run the provided `new.sh` script to generate the scaffolding for a new hands-on sandbox: - -```bash -./new.sh -``` - -The script will first prompt if you want to create a hands-on or exercise: - -Enter `hands-on` or `h` to create a new hands-on. - -Then, the script will prompt you for: - -1. The name of the hands-on -- likely to be specified in the corresponding hands-on discussion (without the `hp-` prefix!) (you should be using kebab case for the hands-on name) -2. Does the hands-on require Git? -3. Does the hands-on require Github? - -{: .reference } - -Refer to the [hands-on structure document](/developers/docs/architecture/hands-on-structure) for more information about the folder structure generated. - - -Each hands-on is implemented as a single file `hands_on/.py`, containing the instructions to set up the hands-on sandbox . - -{: .reference } - -For more information about how Git-Mastery downloads a hands-on, refer to the [Download Workflow](/developers/docs/architecture/download-workflow) - -{: .note } - -> `exercises` repo comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke any command and `exercise_utils.file.create_or_update_file` to create or update a given file. -> -> For the full list of utility functions, refer [here](/developers/docs/tooling/exercise-utils). - -These are some references for download setups for other exercises: - -- [init-repo](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/init_repo.py) -- [add-files](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/add_files.py) -- [stage-modified](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/stage_modified.py) - -### Conventions - -1. Any operations should use OS-agnostic options (e.g. opting to use `shutil.rmtree` over `run_command(["rm"])`) -2. For any commands that require `gh` (GitHub CLI), make sure that the `__requires_github__` variable in the download setup is set to `True` so that the app will automatically check that the student has correctly setup Github CLI - -### Testing downloads - -To test that your download script works, we have provided a script to simulate the download process in this repository for you to verify. - -```bash -./test-download.sh -``` - -You can find the sandbox created by the script under the `test-downloads/` folder. Check it manually to verify it is as expected. - -## Submitting the hands-on for review - -Create a pull request from your fork using the provided pull request template. - -Fill in all the details necessary. - -## Example - -1. Hands-on discussion: -2. Remote repository request: -3. Hands-on PR: - diff --git a/docs/contributing/index.md b/docs/contributing/index.md deleted file mode 100644 index 4bf6f78..0000000 --- a/docs/contributing/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Contributing -nav_order: 2 ---- - -[![CI](https://github.com/git-mastery/exercises/actions/workflows/ci.yml/badge.svg)](https://github.com/git-mastery/exercises/actions/workflows/ci.yml) - diff --git a/docs/contributing/setup.md b/docs/contributing/setup.md deleted file mode 100644 index a224e59..0000000 --- a/docs/contributing/setup.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Setup -parent: Contributing -nav_order: 1 ---- - -Both hands-on and exercises reside in the same repository: [`git-mastery/exercises`](https://github.com/git-mastery/exercises). So before you start to contributing, you should have these setup already: - -- Bash environment -- Python 3.13+ -- Github CLI [installed and authenticated](https://github.com/cli/cli#installation) for testing the download script -- uv [installed](https://docs.astral.sh/uv/getting-started/installation/) - -## Setup - -1. Fork the repository: -2. Clone the fork - - ```bash - git clone https://github.com//exercises - ``` - -3. Run the following command to set up virtual environment and install dependencies: - - ```bash - uv sync - ``` - -4. Set up pre-commit hooks (for linting, formatting and type checking) using LeftHook - - ```bash - uv run lefthook install - ``` diff --git a/docs/architecture/download-workflow.md b/docs/exercises/download-workflow.md similarity index 65% rename from docs/architecture/download-workflow.md rename to docs/exercises/download-workflow.md index a6993c7..dda5bc0 100644 --- a/docs/architecture/download-workflow.md +++ b/docs/exercises/download-workflow.md @@ -1,14 +1,14 @@ --- -title: Download Workflow -parent: Architecture -nav_order: 3 +title: Download workflow +parent: Exercises +nav_order: 4 --- ## General download sequence -Both hands-on and exercises are downloaded using the same `gitmastery download` command. +Both hands-ons and exercises are downloaded using the same `gitmastery download` command. -Hands-on will have the `hp-` prefix before the name of the hands-on. +Hands-ons use the `hp-` prefix before the hands-on name. ```mermaid flowchart @@ -21,7 +21,7 @@ Refer to the following sections for the specific download sequences. ## Hands-on download -These are handled within the app's `download.py` command, through the `download_hands_on` function. +These are handled within the app's `download.py` command through the `download_hands_on` function. ```mermaid flowchart @@ -29,18 +29,18 @@ a[Download hands-on] --> b[Check that hands-on exists in the hands_on/ folder] b -- does not exist --> c[Error and stop the download] b -- else --> d[Check if hands-on already exists] d -- yes --> e[Delete hands-on and re-download] -e --> f["Create hands-on folder (hp-)"] +e --> f[Create hands-on folder] d -- no --> f f --> g[Change directory to hands-on directory] g --> h[Load hands-on file as namespace to extract variables and functions] h --> i[Check Git if toggled] -i --> j[Check Github if toggled] +i --> j[Check GitHub if toggled] j --> k[Execute the download function in the hands-on file] ``` ## Exercise download -These are handled within the app's `download.py` command, through the `download_exercise` function. +These are handled within the app's `download.py` command through the `download_exercise` function. ```mermaid flowchart @@ -51,7 +51,7 @@ D -- Yes --> E[Delete existing folder] D -- No --> F[Create exercise folder] E --> F F --> G[Change directory to exercise folder] -G --> H["Download base files (.gitmastery-exercise.json, README.md)"] +G --> H[Download base files] H --> I[Read config file] I --> J{Requires Git?} @@ -59,8 +59,8 @@ J -- Yes --> K[Check Git setup] K -- Not setup --> L[Rollback, remove folder, stop download] J -- No --> M[Proceed] -M --> N{Requires Github?} -N -- Yes --> O[Check Github setup] +M --> N{Requires GitHub?} +N -- Yes --> O[Check GitHub setup] O -- Not setup --> P[Rollback, remove folder, stop download] N -- No --> Q[Proceed] @@ -68,20 +68,19 @@ Q --> R{Config has base files?} R -- Yes --> S[Download additional resources] R -- No --> T[Skip resource download] -S --> U{"Repo type != 'ignore'?"} +S --> U{Repo type is managed?} T --> U -U -- Yes --> V[Setup exercise folder] -U -- No --> Z1[Save config with timestamp
Print cd into exercise folder] +U -- Yes --> V[Set up exercise folder] +U -- No --> Z1[Save config with timestamp and print next steps] -%% setup_exercise_folder details V --> V1[Save metadata to .gitmastery-exercise.json] V1 --> V2{Repo type: local or remote?} V2 -- Local --> V3[Create local folder] V2 -- Remote --> V4[Retrieve from GitHub] V4 --> V5{Fork required?} -V5 -- Yes --> V6[Check/delete existing fork
Create new fork
Clone fork] +V5 -- Yes --> V6[Check or delete existing fork, create new fork, clone fork] V5 -- No --> V7[Clone repository] -V3 --> V8[cd into repo folder] +V3 --> V8[Change into repo folder] V6 --> V8 V7 --> V8 V8 --> V9[Fetch resources via download.py] @@ -90,13 +89,12 @@ V10 -- Yes --> V11[Download and save resources] V10 -- No --> V12[Skip resource download] V11 --> V13{Repo init enabled?} V12 --> V13 -V13 -- Yes --> V14[Initialize repo
Commit initial state] +V13 -- Yes --> V14[Initialize repo and commit initial state] V13 -- No --> V15[Skip repo init] -V14 --> V16["Execute setup() from download.py"] +V14 --> V16[Execute setup from download.py] V15 --> V16 -V16 --> V17[Print cd into exercise repo folder] +V16 --> V17[Print next steps] Z1 --> Z2[Download complete] V17 --> Z2 ``` - diff --git a/docs/exercises/exercise-structure.md b/docs/exercises/exercise-structure.md new file mode 100644 index 0000000..b79f10a --- /dev/null +++ b/docs/exercises/exercise-structure.md @@ -0,0 +1,114 @@ +--- +title: Exercise structure +parent: Exercises +nav_order: 3 +--- + +# Exercise structure + +Exercises follow a fixed structure for the Git-Mastery app to pick up on: + +```text + +├── .gitmastery-exercise.json +├── README.md +├── __init__.py +├── download.py +├── res +│ └── ... +├── tests +│ └── specs +│ └── base.yml +├── test_verify.py +└── verify.py +``` + +- `.gitmastery-exercise.json`: contains the exercise configuration +- `README.md`: contains the instructions for students +- `download.py`: contains the download instructions to set up the student's exercise +- `verify.py`: contains the verification script for the exercise attempt +- `res/`: contains resources that are available to students +- `tests/specs/`: contains specification files written using [`repo-smith`](https://github.com/git-mastery/repo-smith) +- `test_verify.py`: contains unit tests for the verification script + +The scaffold created by `exercises/scripts/new-exercise.py` currently places `test_verify.py` at the exercise root, not inside `tests/`. + +## What students see + +When a student downloads a typical exercise, they will see the following folder structure: + +```text + +├── .gitmastery-exercise.json +├── README.md +└── + ├── .git + └── ... +``` + +The root of the exercise contains the configured `README.md` and `.gitmastery-exercise.json`. + +It also contains the subfolder configured in `.gitmastery-exercise.json`, which is where students attempt the exercise. + +Some exercises intentionally differ: + +- `repo_type: ignore`: no managed repository subfolder is created by the app; the student works directly in the exercise root. +- `repo_type: local-ignore`: the app creates the working folder but does not run `git init` for the student. + +## Configuration structure + +`.gitmastery-exercise.json` is used to tell the [Git-Mastery app](https://git-mastery.github.io/app) how to set up the student's exercise. + +{: .note } + +We opted to use a standardized configuration for exercises because they often follow a common setup shape. + +The `new.sh` script generates one for you, but you can modify the file directly: + +- `exercise_name`: raw exercise name that will be indexed; recommended to use [kebab case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) +- `tags`: used during indexing on the [exercise directory](https://git-mastery.github.io/exercises) +- `requires_git`: performs a check to ensure that Git is installed and `user.name` and `user.email` are configured +- `requires_github`: performs a check to ensure that GitHub CLI is installed and the user has authenticated +- `base_files`: specifies the files from `res/` to be downloaded into the exercise root +- `exercise_repo`: controls the subfolder that is generated; this is where students work on the exercise + - `repo_type`: one of `local`, `remote`, `ignore`, or `local-ignore` + - `repo_name`: name of the subfolder + - `init`: determines if `git init` is run for the subfolder; used for local or local-ignore style repositories + - `create_fork`: determines if a fork is created on the user's GitHub account; used for remote repositories + - `fork_all_branches`: optional remote-only setting controlling whether all branches are forked + - `repo_title`: name of the remote repository to fork and clone; used for remote repositories + +### Repo type meanings + +- `local`: create a local subfolder and optionally initialize it as a Git repository. +- `remote`: clone a GitHub repository, optionally via a student fork. +- `ignore`: do not create or manage a working repository; verification should not depend on `exercise.repo`. +- `local-ignore`: create a local subfolder but do not initialize Git. This is useful for exercises where the student is expected to run `git init` themselves. + +## Exercise resource types + +There are two distinct types of resources: + +1. Base files: configured through the `base_files` property in `.gitmastery-exercise.json`; files located in `res/` are downloaded to the root of the exercise folder. + + ```text + + ├── .gitmastery-exercise.json + ├── README.md + ├── + └── + ├── .git + └── ... + ``` + +2. Resources: configured through the `__resources__` field in `download.py`; supporting files from `res/` are downloaded into the subfolder. + + ```text + + ├── .gitmastery-exercise.json + ├── README.md + ├── + └── + ├── .git + └── + ``` diff --git a/docs/exercises/exercise-utils.md b/docs/exercises/exercise-utils.md new file mode 100644 index 0000000..2b0714d --- /dev/null +++ b/docs/exercises/exercise-utils.md @@ -0,0 +1,26 @@ +--- +title: Exercise utilities +parent: Exercises +nav_order: 6 +--- + +These utilities are contained within the [`exercises/exercise_utils` module](https://github.com/git-mastery/exercises/tree/main/exercise_utils). + +They are loaded during exercise download, which means that the `app` has access to these functions while setting up a hands-on or exercise. + +## Available utility modules + +- `exercise_utils.cli`: generic CLI calls +- `exercise_utils.file`: file creation and appending helpers +- `exercise_utils.git`: common Git operations such as commit, repository initialization, and adding files +- `exercise_utils.gitmastery`: Git-Mastery specific functions such as creating the start tag + +## Contributing utility functions + +These should cover most use cases, but if there is no existing utility function for your use case, `exercise_utils.cli.run_command` is available to execute other CLI calls. + +If you believe that more utility functions should be supported, feel free to open a pull request adding one. + +{: .note } + +If you add a new file such as `exercise_utils/general.py`, remember to update `app` to include the file in the `EXERCISE_UTILS_FILES` constant. diff --git a/docs/exercises/exercise.md b/docs/exercises/exercise.md new file mode 100644 index 0000000..3e727cd --- /dev/null +++ b/docs/exercises/exercise.md @@ -0,0 +1,145 @@ +--- +title: Exercise +parent: Exercises +nav_order: 2 +--- + +1. TOC +{:toc} + +## Before contributing + +If you are proposing a new exercise, instead of implementing an [already approved exercise proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22exercise%20discussion%22%20label%3A%22help%20wanted%22), make sure that you have done the following: + +- [ ] Create an [exercise discussion](https://github.com/git-mastery/exercises/issues/new?template=exercise_discussion.yaml) +- [ ] Obtain approval on the exercise +- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) + +## Create a new exercise + +Use the provided `new.sh` script to generate the scaffolding for a new exercise: + +```bash +./new.sh +``` + +The script will first prompt if you want to create a hands-on or exercise. + +Enter `exercise` or `e` to create a new exercise. + +Then, the script will prompt you for: + +1. The name of the exercise, likely specified in the corresponding exercise discussion. Use kebab case. +2. The exercise tags, split by spaces, likely specified in the corresponding exercise discussion. +3. The exercise repository type. The scaffold currently supports `local`, `remote`, and `ignore` directly. Read the [Exercise structure](/developers/docs/exercises/exercise-structure#configuration-structure) section for more information. + +{: .reference } + +Refer to the [Exercise structure](/developers/docs/exercises/exercise-structure) page for more information about the generated folder structure. + +## Download setup + +`download.py` contains the instructions to set up the local repository. + +The app loads `download.py` dynamically from the exercises source, reads `__resources__` if present, and executes `setup(...)` inside the student's working repository. + +{: .reference } + +For more information about how Git-Mastery downloads exercises, refer to the [Download workflow](/developers/docs/exercises/download-workflow). + +{: .note } + +> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. +> +> For the full list of utility functions, refer to [Exercise utilities](/developers/docs/exercises/exercise-utils). + +These are some references for download setups for other exercises: + +- [ignoring-somethings](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/ignoring_somethings/download.py) +- [branch-bender](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/branch_bender/download.py) +- [amateur-detective](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/amateur_detective/download.py) + +### Download conventions + +1. Any operations should use OS-agnostic options, for example `shutil.rmtree` instead of `run_command(["rm"])`. +2. If you need to compare repository state before and after the student's work, create the start tag during setup using the shared Git-Mastery utilities. +3. For any commands that require `gh`, make sure that the `requires_github` configuration is set to `true` so that the app automatically checks that the student has set up GitHub CLI correctly. +4. `setup(...)` may receive a `repo-smith` helper object as `rs`; use it instead of shelling out directly when possible. + +### Testing downloads + +To test that your download script works, use the provided script: + +```bash +./test-download.sh +``` + +You can find the downloaded repository under `test-downloads/`. + +## Verification setup + +The verification process is controlled by `verify.py`. + +{: .reference } + +For more information about how Git-Mastery verifies exercise attempts, refer to the [Verification workflow](/developers/docs/exercises/verification-workflow). + +The [`git-autograder`](https://github.com/git-mastery/git-autograder) library builds the `GitAutograderExercise` object passed to `verify(...)`. It exposes the exercise config, the working repository, and optional answer parsing. + +If there is no helper for a check you need, you can still fall back to the underlying `GitPython` repository: + +```python +from git_autograder import ( + GitAutograderExercise, + GitAutograderOutput, + GitAutograderStatus, +) + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + # Access the underlying GitPython repo: + exercise.repo.repo + + return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) +``` + +Refer to existing `verify.py` scripts to understand the available helper functions that streamline grading. Open an issue if there is something that is not yet supported or if you have a question. + +For `repo_type: ignore` exercises, `exercise.repo` is a null repository wrapper. In that mode, verify against files such as `answers.txt` or exercise metadata rather than Git state. + +Some examples of verifications: + +- [branch-bender](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/branch_bender/verify.py) +- [conflict-mediator](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/conflict_mediator/verify.py) + +### Verification conventions + +1. Store the comments of the verification as constants so that they can be imported and used reliably in unit tests. +2. For any remote behavior to verify, provide a mock to substitute the behavior in unit tests. +3. Prefer raising `exercise.wrong_answer([...])` for incorrect submissions and reserve unexpected exceptions for genuine errors. + +### Testing verification + +To test verification, Git-Mastery relies on [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate exercise states and write unit tests for the verification script's behavior. You do not need to simulate the entire flow, only the end states that matter for your verification script. + +Refer to existing `test_verify.py` files to see examples of unit testing the verification script. + +{: .reference } + +See [Testing patterns](/developers/docs/exercises/testing-patterns) for the recommended `GitAutograderTestLoader` workflow. + +You can run the unit tests of your exercise via: + +```bash +./test.sh +``` + +## Submitting the exercise for review + +Create a pull request from your fork using the provided pull request template. + +## Example + +1. Exercise discussion: +2. Remote repository request: +3. Exercise PR: diff --git a/docs/exercises/hands-on-structure.md b/docs/exercises/hands-on-structure.md new file mode 100644 index 0000000..ee03fb5 --- /dev/null +++ b/docs/exercises/hands-on-structure.md @@ -0,0 +1,51 @@ +--- +title: Hands-on structure +parent: Exercises +nav_order: 7 +--- + +All hands-ons are stored within the `hands_on` folder of the [`exercises`](https://github.com/git-mastery/exercises) repository. + +They are represented by a single Python file whose name is the hands-on ID. + +Hands-ons are not graded and progress is not tracked. They only help set up the student's folder structure for a given lesson. + +{: .note } + +Git-Mastery uses the format `hp-` for hands-on names, for example `hp-init-repo`, to differentiate them from exercise names. Internally, the `hands_on/` folder stores the Python files, for example `hands_on/init_repo.py`. + +## File structure + +Given below is a simple example: + +```python +import os + +from exercise_utils.cli import run_command +from exercise_utils.file import append_to_file, create_or_update_file +from exercise_utils.git import add, init + +__requires_git__ = True +__requires_github__ = False + + +def download(verbose: bool): + os.makedirs("things") + os.chdir("things") + init(verbose) + create_or_update_file( + "fruits.txt", + """ + apples + bananas + cherries + """, + ) + add(["fruits.txt"], verbose) + run_command(["git", "add", "fruits.txt"], verbose) + append_to_file("fruits.txt", "dragon fruits") +``` + +The setup instructions go under the `download` function. + +`__requires_git__` and `__requires_github__` tell the Git-Mastery app whether to run automatic verification that the student has set up Git and GitHub CLI correctly. diff --git a/docs/exercises/hands-on.md b/docs/exercises/hands-on.md new file mode 100644 index 0000000..3699cdb --- /dev/null +++ b/docs/exercises/hands-on.md @@ -0,0 +1,91 @@ +--- +title: Hands-on +parent: Exercises +nav_order: 1 +--- + +1. TOC +{:toc} + +## What is a hands-on? + +[Lessons accompanying Git-Mastery App](https://nus-cs2103-ay2526s1.github.io/website/se-book-adapted/git-trail/index.html) contains hands-on practicals for students to get hands-on experience of the Git concepts covered by the lesson. Some of those hands-on practicals need to set up a sandbox containing the required folders, files, and repositories before the student can start. Git-Mastery app can set up that sandbox for students so that they can get to the practical part more easily. + +For example, a student can run `gitmastery download hp-init-repo` to set up a sandbox for a specific hands-on practical. + +Some examples of how Git-Mastery app helps create the sandbox for a hands-on practical can be seen in [this Git Tour](https://nus-cs2103-ay2526s1.github.io/website/book/gitAndGitHub/trail/recordingFolderHistory/index.html) (see T1L3 and T1L4). + +## Before contributing + +New contributors are recommended to start by implementing an [already approved hands-on proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22hands-on%20discussion%22%20label%3A%22help%20wanted%22). + +If you are proposing a new hands-on instead, make sure that you have done the following: + +- [ ] Create a [hands-on discussion](https://github.com/git-mastery/exercises/issues/new?template=hands_on_discussion.yaml) +- [ ] Obtain approval on the hands-on +- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) + +## Implementing a hands-on + +First, run the provided `new.sh` script to generate the scaffolding for a new hands-on sandbox: + +```bash +./new.sh +``` + +The script will first prompt if you want to create a hands-on or exercise. + +Enter `hands-on` or `h` to create a new hands-on. + +Then, the script will prompt you for: + +1. The name of the hands-on, likely specified in the corresponding hands-on discussion, without the `hp-` prefix. Use kebab case. +2. Whether the hands-on requires Git +3. Whether the hands-on requires GitHub + +{: .reference } + +Refer to the [Hands-on structure](/developers/docs/exercises/hands-on-structure) page for more information about the generated file structure. + +Each hands-on is implemented as a single file at `hands_on/.py`, containing the instructions to set up the hands-on sandbox. + +{: .reference } + +For more information about how Git-Mastery downloads a hands-on, refer to the [Download workflow](/developers/docs/exercises/download-workflow). + +{: .note } + +> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. +> +> For the full list of utility functions, refer to [Exercise utilities](/developers/docs/exercises/exercise-utils). + +These are some references for download setups for other hands-ons: + +- [init-repo](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/init_repo.py) +- [add-files](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/add_files.py) +- [stage-modified](https://raw.githubusercontent.com/git-mastery/exercises/refs/heads/main/hands_on/stage_modified.py) + +### Conventions + +1. Any operations should use OS-agnostic options, for example `shutil.rmtree` instead of `run_command(["rm"])`. +2. For any commands that require `gh`, make sure that the `__requires_github__` variable in the download setup is set to `True` so that the app automatically checks that the student has set up GitHub CLI correctly. + +### Testing downloads + +To test that your download script works, use the provided script: + +```bash +./test-download.sh +``` + +You can find the sandbox created by the script under `test-downloads/`. Check it manually to verify that it is as expected. + +## Submitting the hands-on for review + +Create a pull request from your fork using the provided pull request template. + +## Example + +1. Hands-on discussion: +2. Remote repository request: +3. Hands-on PR: diff --git a/docs/exercises/index.md b/docs/exercises/index.md new file mode 100644 index 0000000..e7c0b47 --- /dev/null +++ b/docs/exercises/index.md @@ -0,0 +1,27 @@ +--- +title: Exercises +nav_order: 4 +has_children: true +--- + +# Exercises + +The [`exercises`](https://github.com/git-mastery/exercises) repository contains both hands-ons and graded exercises. + +## What this section covers + +- How to contribute hands-ons +- How to contribute exercises +- How exercise downloads are structured +- How verification works +- How to test `verify.py` reliably +- Shared utilities used during exercise download + +## Suggested reading + +1. [Hands-on](/developers/docs/exercises/hands-on) +2. [Exercise](/developers/docs/exercises/exercise) +3. [Exercise structure](/developers/docs/exercises/exercise-structure) +4. [Download workflow](/developers/docs/exercises/download-workflow) +5. [Verification workflow](/developers/docs/exercises/verification-workflow) +6. [Testing patterns](/developers/docs/exercises/testing-patterns) diff --git a/docs/exercises/testing-patterns.md b/docs/exercises/testing-patterns.md new file mode 100644 index 0000000..98a6382 --- /dev/null +++ b/docs/exercises/testing-patterns.md @@ -0,0 +1,107 @@ +--- +title: Testing patterns +parent: Exercises +nav_order: 6 +--- + +# Testing patterns + +Exercise verification tests usually focus on repository end states rather than replaying the entire student workflow. + +## Recommended approach + +1. Use `GitAutograderTestLoader` from `exercise_utils.test` +2. Build the repository state you need with `repo-smith` +3. Run `verify(...)` +4. Assert on `GitAutograderStatus` and important comments + +## Basic pattern + +```python +from git_autograder import GitAutograderStatus +from exercise_utils.test import GitAutograderTestLoader, assert_output + +from .verify import verify + +REPOSITORY_NAME = "my-exercise" + +loader = GitAutograderTestLoader(REPOSITORY_NAME, verify) + + +def test_base() -> None: + with loader.start() as (test, rs): + rs.git.create_file("notes.txt", "hello") + rs.git.add(["notes.txt"]) + rs.git.commit("Add notes") + + output = test.run() + + assert_output(output, GitAutograderStatus.SUCCESSFUL) +``` + +## What the loader gives you + +`GitAutograderTestLoader` sets up a temporary exercise directory, patches exercise config loading, and constructs a real `GitAutograderExercise` object for the test run. + +Within `with loader.start() as (test, rs):` + +- `test.run()` executes the exercise's `verify(...)` +- `rs` is a `RepoSmith` helper rooted at the student's working repository + +## Useful test options + +### Start from an existing repository + +Use `clone_from=...` when the exercise needs a prepared repository as its starting point. + +```python +with loader.start(clone_from="https://github.com/git-mastery/some-repo") as (test, rs): + ... +``` + +### Mock `answers.txt` + +Use `mock_answers` for answer-driven exercises. + +```python +with loader.start(mock_answers={"What is Git?": "Version control"}) as (test, rs): + output = test.run() +``` + +### Include a remote repository + +Use `include_remote_repo=True` when verification depends on remote behavior. + +```python +with loader.start(include_remote_repo=True) as (test, rs, rs_remote): + ... +``` + +## What to assert + +- `output.status` for the overall result +- key comments, not necessarily every comment +- repository state only when it helps explain the verification behavior + +`exercise_utils.test.assert_output(...)` is useful when you want to check the status and ensure specific comments are present. + +## Testing guidance + +- Prefer one test per verification rule or scenario +- Keep each test focused on the minimal repository state needed +- Store verification comments as constants in `verify.py` so tests can import them safely +- Mock remote interactions instead of making real network calls + +## Running tests + +In `exercises`, run: + +```bash +./test.sh +``` + +Or run the full suite with: + +```bash +pytest . -s -vv +``` diff --git a/docs/exercises/verification-workflow.md b/docs/exercises/verification-workflow.md new file mode 100644 index 0000000..5f062ed --- /dev/null +++ b/docs/exercises/verification-workflow.md @@ -0,0 +1,33 @@ +--- +title: Verification workflow +parent: Exercises +nav_order: 5 +--- + +# Verification workflow + +Exercise verification is driven by the student's local exercise metadata and the exercise's `verify.py` script. + +```mermaid +flowchart +a[Verify exercise] --> b[Check if in exercise using .gitmastery-exercise.json] +b -- not in exercise --> c[Cancel] +b -- in exercise --> d[Load exercise config and verification module] +d --> e[Run verify.py against the student's working repository] +e --> f[Return structured grading output] +f --> g[Save progress locally] +g --> h{Remote sync enabled?} +h -- yes --> i[Commit and push progress fork and create PR if needed] +h -- no --> j[Finish] +i --> j +``` + +## In practice + +- The app first confirms that the current directory belongs to a Git-Mastery exercise. +- It loads the local exercise metadata and the corresponding verification logic from the exercises source. +- `verify.py` receives a `GitAutograderExercise` object, which loads `.gitmastery-exercise.json` and prepares either a real repository wrapper or a null repository wrapper for `ignore` exercises. +- `verify.py` uses `git-autograder` to inspect repository state, parse `answers.txt` when needed, and produce structured comments and a status. +- `GitAutograderWrongAnswerException` becomes an unsuccessful verification result, while invalid state and unexpected exceptions become error results. +- After verification, the app appends an entry to local `progress/progress.json` and optionally syncs it to the student's progress fork. +- Exercise unit tests use `repo-smith` to construct end states that validate verification behavior. diff --git a/docs/getting-started/common-workflows.md b/docs/getting-started/common-workflows.md new file mode 100644 index 0000000..f94daf9 --- /dev/null +++ b/docs/getting-started/common-workflows.md @@ -0,0 +1,52 @@ +--- +title: Common workflows +parent: Getting Started +nav_order: 2 +--- + +# Common workflows + +## Choosing a repository + +- Use `exercises` for new hands-ons, exercises, and verification tests. +- Use `app` for CLI behavior, download flows, and local progress features. +- Use `repo-smith` when exercise verification tests need new repository setup primitives. +- Use `git-autograder` when verification logic needs new reusable grading helpers. + +## Common local tasks + +- Install dependencies: + - `app`, `repo-smith`, `git-autograder`: `uv sync` + - `exercises`: `uv venv && source .venv/bin/activate && uv pip install -r requirements.txt` +- Run Python tests: + - `app`: `uv run pytest` + - `repo-smith`: `uv run pytest -s -vv` + - `git-autograder`: `uv run pytest -s -vv` + - `exercises`: `./test.sh ` or `pytest . -s -vv` +- Run the CLI locally from `app`: `uv run python main.py` +- Test exercise downloads from `exercises`: `./test-download.sh ` + +## Common contribution paths + +### Author or update an exercise + +1. Work in `exercises` +2. Implement `download.py`, `verify.py`, and `test_verify.py` +3. Test the download flow locally +4. Test the verification flow locally + +### Change CLI behavior + +1. Work in `app` +2. Update the relevant command or config logic +3. Re-run the local CLI using `uv run python main.py` +4. Test against a local Git-Mastery exercises directory + +### Extend verification helpers + +1. Work in `git-autograder` when the helper should be reusable across exercises +2. Work in `repo-smith` when the missing piece is repository-state setup for tests + +## Recommended contributor path + +If you are new to Git-Mastery, start by contributing to `exercises`, then move to `app` or the supporting libraries once you are familiar with the exercise lifecycle. diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md new file mode 100644 index 0000000..eb80ccb --- /dev/null +++ b/docs/getting-started/index.md @@ -0,0 +1,27 @@ +--- +title: Getting Started +nav_order: 3 +has_children: true +--- + +# Getting Started + +This section helps contributors choose the right repository, set up a local development environment, and follow common Git-Mastery workflows. + +## Typical starting points + +- Implement or update a hands-on or exercise: [Exercises](/developers/docs/exercises) +- Work on the CLI experience: [App](/developers/docs/app) +- Update verification or testing libraries: [Libraries](/developers/docs/libraries) + +## Recommended reading order + +1. [Setup](/developers/docs/getting-started/setup) +2. [Common workflows](/developers/docs/getting-started/common-workflows) +3. The section for the repository you plan to contribute to + +## After setup + +- Working on authoring or verification: go to [Exercises](/developers/docs/exercises) +- Working on the CLI runtime: go to [App](/developers/docs/app) +- Working on reusable test or grading code: go to [Libraries](/developers/docs/libraries) diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md new file mode 100644 index 0000000..5c9f094 --- /dev/null +++ b/docs/getting-started/setup.md @@ -0,0 +1,81 @@ +--- +title: Setup +parent: Getting Started +nav_order: 1 +--- + +# Setup + +Git-Mastery is standardizing on a `uv`-first local development workflow. + +Use the guidance on this page as the default when setting up repositories locally. Some repositories are still in transition, so the exact `uv` command differs slightly by repository. + +## Prerequisites + +- Git +- Python 3.13+ +- [`uv`](https://docs.astral.sh/uv/getting-started/installation/) +- GitHub CLI (`gh`) installed and authenticated when working on flows that interact with GitHub + +## Recommended repository setup + +1. Fork the repository you want to work on. +2. Clone your fork. +3. Set up the environment with the repository's `uv` workflow. + +```bash +git clone https://github.com// +cd +``` + +## Repository-specific setup + +### `app` + +`app` already has `pyproject.toml` and `uv.lock`, so the standard workflow is: + +```bash +uv sync +uv run lefthook install +uv run python main.py +``` + +### `repo-smith` + +`repo-smith` is already a Python package, so the standard workflow is: + +```bash +uv sync +uv run pytest -s -vv +``` + +### `git-autograder` + +`git-autograder` also has package metadata, so the standard workflow is: + +```bash +uv sync +uv run pytest -s -vv +``` + +### `exercises` + +`exercises` has not fully migrated to a `uv sync`-based project layout yet, but you can still use `uv` as the package manager for local development: + +```bash +uv venv +source .venv/bin/activate +uv pip install -r requirements.txt +``` + +The repository still contains older helper scripts such as `setup.sh`, `test.sh`, and `test-download.sh`. They remain useful, but new documentation should prefer `uv` where practical. + +## GitHub-dependent work + +If you are working on downloads, remote exercises, or progress sync flows, ensure `gh auth status` succeeds before testing. + +Git-Mastery's GitHub-dependent commands also expect the `delete_repo` scope for flows that create and remove forks. + +```bash +gh auth refresh -s delete_repo +``` diff --git a/docs/libraries/git-autograder.md b/docs/libraries/git-autograder.md new file mode 100644 index 0000000..9f8118f --- /dev/null +++ b/docs/libraries/git-autograder.md @@ -0,0 +1,86 @@ +--- +title: git-autograder +parent: Libraries +nav_order: 2 +--- + +# git-autograder + +`git-autograder` is the verification library used by Git-Mastery exercise `verify.py` scripts. + +## Typical use in Git-Mastery + +- Load and inspect a student's exercise repository +- Produce structured grading comments and statuses +- Provide reusable helpers over raw Git access + +## Basic example + +```python +from git_autograder import ( + GitAutograderExercise, + GitAutograderOutput, + GitAutograderStatus, +) + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) +``` + +## Core concepts + +### `GitAutograderExercise` + +This is the main entrypoint used by exercise verification scripts. + +It: + +- reads `.gitmastery-exercise.json` +- loads the student's working repository +- exposes `exercise_name`, `config`, and `repo` +- provides `to_output(...)` and `wrong_answer(...)` + +### `GitAutograderOutput` + +Verification scripts return a `GitAutograderOutput` containing: + +- `status` +- `started_at` +- `completed_at` +- `comments` +- `exercise_name` + +### `GitAutograderStatus` + +The current statuses are: + +- `SUCCESSFUL` +- `UNSUCCESSFUL` +- `ERROR` + +## Repository modes + +For normal exercises, `exercise.repo` wraps a real Git repository. + +For `repo_type: ignore`, `exercise.repo` becomes a null repository wrapper. Accessing Git-specific properties on it raises an error, which helps catch verification code that assumes a repository exists when it should not. + +## Answers support + +`GitAutograderExercise.answers` parses `answers.txt` from the exercise root on demand. + +It supports question-and-answer style validation and can accumulate validation rules before calling `validate()`. + +## Typical verification flow + +1. Construct `GitAutograderExercise` +2. Inspect repository state through `exercise.repo` +3. Optionally inspect `exercise.answers` +4. Raise `exercise.wrong_answer([...])` for incorrect submissions +5. Return `exercise.to_output([...], GitAutograderStatus.SUCCESSFUL)` on success + +{: .reference } + +See [Exercise](/developers/docs/exercises/exercise) and [Verification workflow](/developers/docs/exercises/verification-workflow) for how `git-autograder` is used inside the main Git-Mastery exercise flow. + +For implementation details, refer to the [`git-autograder` repository](https://github.com/git-mastery/git-autograder). diff --git a/docs/libraries/index.md b/docs/libraries/index.md new file mode 100644 index 0000000..b97e4e2 --- /dev/null +++ b/docs/libraries/index.md @@ -0,0 +1,29 @@ +--- +title: Libraries +nav_order: 6 +has_children: true +--- + +# Libraries + +Git-Mastery maintains reusable libraries that support exercise verification and testing. + +These libraries are most relevant when you are working on reusable grading behavior rather than a single exercise. + +## Main libraries + +- [`repo-smith`](https://github.com/git-mastery/repo-smith): repository construction for unit tests +- [`git-autograder`](https://github.com/git-mastery/git-autograder): grading helpers and verification abstractions + +## When to use which library + +- Reach for `repo-smith` when your test needs a specific repository history or working tree state. +- Reach for `git-autograder` when multiple exercises would benefit from the same verification helper or abstraction. + +{: .reference } + +For a broader decision guide across repositories, see [Choosing where to change](/developers/docs/overview/choosing-where-to-change). + +## Additional published packages + +- [`difflib-parser`](https://github.com/git-mastery/difflib-parser) diff --git a/docs/tooling/libraries.md b/docs/libraries/published-packages.md similarity index 83% rename from docs/tooling/libraries.md rename to docs/libraries/published-packages.md index 0f1bc29..1d0f21b 100644 --- a/docs/tooling/libraries.md +++ b/docs/libraries/published-packages.md @@ -1,7 +1,7 @@ --- -title: Published libraries -nav_order: 2 -parent: Tooling +title: Published packages +parent: Libraries +nav_order: 3 --- ## Published Python packages diff --git a/docs/libraries/repo-smith.md b/docs/libraries/repo-smith.md new file mode 100644 index 0000000..1c21be3 --- /dev/null +++ b/docs/libraries/repo-smith.md @@ -0,0 +1,106 @@ +--- +title: repo-smith +parent: Libraries +nav_order: 1 +--- + +# repo-smith + +`repo-smith` is a YAML-based library for initializing Git repositories for unit testing. + +## Typical use in Git-Mastery + +- Build repository states for `test_verify.py` +- Simulate specific Git histories and repository layouts +- Keep verification tests readable through declarative setup + +## Example usage + +```python +from repo_smith.initialize_repo import initialize_repo + + +def test_dummy(): + repo_initializer = initialize_repo("tests/specs/basic_spec.yml") + with repo_initializer.initialize() as repo: + print(repo) +``` + +## Why Git-Mastery uses it + +`repo-smith` keeps exercise verification tests declarative. Instead of building test repositories imperatively in Python, contributors can describe the repository history and filesystem state in YAML. + +## Spec structure + +A spec contains: + +- optional `name` +- optional `description` +- `initialization` + - optional `clone-from` + - ordered `steps` + +All steps run sequentially from top to bottom. + +## Supported step types + +Current step types include: + +- `commit` +- `add` +- `tag` +- `new-file` +- `edit-file` +- `delete-file` +- `append-file` +- `bash` +- `branch` +- `branch-rename` +- `branch-delete` +- `checkout` +- `remote` +- `reset` +- `revert` +- `merge` +- `fetch` + +## Features worth knowing + +### `clone-from` + +Use `initialization.clone-from` when the test should begin from an existing repository instead of an empty freshly initialized one. + +### Step IDs and hooks + +When a step has an `id`, tests can attach pre-hooks and post-hooks around that specific step. + +This is useful when you need to assert intermediate repository state, not just the final result. + +```python +from repo_smith.initialize_repo import initialize_repo + + +def test_with_hook() -> None: + repo_initializer = initialize_repo("tests/specs/hooks.yml") + + def before_commit(repo) -> None: + print(repo.working_tree_dir) + + repo_initializer.add_pre_hook("first-commit", before_commit) + + with repo_initializer.initialize() as repo: + print(repo) +``` + +## Typical Git-Mastery test pattern + +1. Write one or more YAML specs under `tests/specs/` +2. Build the repository state with `initialize_repo(...)` +3. Run the exercise's `verify(...)` function against that state +4. Assert on comments and final status + +{: .reference } + +See [Exercises testing patterns](/developers/docs/exercises/testing-patterns) for how this is typically wrapped inside `exercises` tests. + +For the full field-level specification, refer to `repo-smith/specification.md` in the [`repo-smith` repository](https://github.com/git-mastery/repo-smith). diff --git a/docs/overview/choosing-where-to-change.md b/docs/overview/choosing-where-to-change.md new file mode 100644 index 0000000..c988cf4 --- /dev/null +++ b/docs/overview/choosing-where-to-change.md @@ -0,0 +1,48 @@ +--- +title: Choosing where to change +parent: Overview +nav_order: 2 +--- + +# Choosing where to change + +When a feature or bug spans multiple repositories, it helps to decide where the primary change belongs. + +## Change `exercises` when + +- the exercise instructions are wrong +- a hands-on or exercise setup needs different files or repository state +- a `verify.py` rule is exercise-specific +- a test for a single exercise needs updating + +## Change `app` when + +- the CLI command behavior is wrong +- download, verify, setup, or progress flows need to change for all users +- `.gitmastery.json` or `.gitmastery-exercise.json` handling needs to change +- the exercises source behavior or local-development override behavior needs to change + +## Change `git-autograder` when + +- multiple exercises need the same verification helper +- `GitAutograderExercise`, output handling, answer parsing, or repository wrappers need improvement +- grading behavior should become easier to express across many exercises + +## Change `repo-smith` when + +- tests need a repository state that is hard to express today +- a new YAML step type or hook capability would simplify many exercise tests +- repository construction logic should be reusable beyond a single exercise + +## Typical examples + +- A bug in one exercise's branch-order check usually belongs in `exercises`. +- A missing helper for parsing `answers.txt` usually belongs in `git-autograder`. +- A missing repository setup primitive for tests usually belongs in `repo-smith`. +- A bug in `gitmastery progress sync on` belongs in `app`. + +## Rule of thumb + +If the change is specific to one learning task, start in `exercises`. + +If the change improves the platform behavior for many exercises or many contributors, it probably belongs in `app`, `git-autograder`, or `repo-smith`. diff --git a/docs/overview/ecosystem-map.md b/docs/overview/ecosystem-map.md new file mode 100644 index 0000000..cba70a6 --- /dev/null +++ b/docs/overview/ecosystem-map.md @@ -0,0 +1,36 @@ +--- +title: Ecosystem map +parent: Overview +nav_order: 1 +--- + +# Ecosystem map + +The main Git-Mastery repositories work together as follows: + +```mermaid +flowchart LR + exercises[exercises] --> app[app] + exercises --> gitautograder[git-autograder] + gitautograder --> reposmith[repo-smith] + app --> progress[progress-dashboard] + actions[actions] --> exercises + actions --> app + actions --> gitautograder + actions --> reposmith +``` + +## How to read the map + +- `exercises` is where contributors define hands-ons, exercises, resources, and verification tests. +- `app` downloads and verifies hands-ons and exercises through the `gitmastery` CLI. +- `git-autograder` provides the verification model used by exercise `verify.py` scripts. +- `repo-smith` creates repository states for unit tests that validate `verify.py` behavior. +- `progress-dashboard` and `actions` support the wider platform, but are not first-class documentation sections yet. + +## Common contributor journeys + +- Writing a new exercise usually touches `exercises`, `git-autograder`, and sometimes `repo-smith`. +- Changing how students download, verify, or track progress usually starts in `app`. +- Adding a reusable repository-state primitive belongs in `repo-smith`. +- Adding a reusable grading abstraction belongs in `git-autograder`. diff --git a/docs/overview/index.md b/docs/overview/index.md new file mode 100644 index 0000000..b404efa --- /dev/null +++ b/docs/overview/index.md @@ -0,0 +1,35 @@ +--- +title: Overview +nav_order: 2 +has_children: true +--- + +# Overview + +Git-Mastery is a multi-repository project for teaching Git through guided hands-on tasks, graded exercises, and supporting developer tooling. + +## Core repositories + +- [`exercises`](https://github.com/git-mastery/exercises): hands-ons, exercises, download setup, and verification tests +- [`app`](https://github.com/git-mastery/app): the `gitmastery` CLI used by students and contributors +- [`repo-smith`](https://github.com/git-mastery/repo-smith): repository state builder used for unit testing exercise verification +- [`git-autograder`](https://github.com/git-mastery/git-autograder): verification library used by exercise `verify.py` scripts + +## Supporting repositories + +These repositories are part of the wider ecosystem and may receive dedicated documentation later: + +- [`progress-dashboard`](https://github.com/git-mastery/progress-dashboard): visualizes learner progress data +- [`actions`](https://github.com/git-mastery/actions): shared GitHub Actions and reusable automation + +## Where to start + +- New contributor: start with [Getting Started](/developers/docs/getting-started) +- Exercise contributor: go to [Exercises](/developers/docs/exercises) +- CLI contributor: go to [App](/developers/docs/app) +- Library contributor: go to [Libraries](/developers/docs/libraries) + +## Useful overview pages + +- [Ecosystem map](/developers/docs/overview/ecosystem-map) +- [Choosing where to change](/developers/docs/overview/choosing-where-to-change) diff --git a/docs/tooling/developer-tooling.md b/docs/tooling/developer-tooling.md deleted file mode 100644 index 38f53df..0000000 --- a/docs/tooling/developer-tooling.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Developer tooling -nav_order: 3 -parent: Tooling ---- - -## Publishing new versions of libraries - -When working on `app` or the other supported libraries, if you attach one of the following labels: - -- `bump:major` -- `bump:minor` -- `bump:patch` - -The CI will automatically tag the merge commit when the pull request is merged and automatically perform a publish for the library/tool. diff --git a/docs/tooling/exercise-utils.md b/docs/tooling/exercise-utils.md deleted file mode 100644 index 3108d19..0000000 --- a/docs/tooling/exercise-utils.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Exercise utilities -nav_order: 1 -parent: Tooling ---- - -These are all contained within the [`exercises/exercise_utils` module](https://github.com/git-mastery/exercises/tree/main/exercise_utils). - -These are loaded when downloading an exercise, which means that the `app` has access to these functions. - -## Available utility functions - -There are functions broadly for the following categories: - -- `exercise_utils.cli`: Generic CLI calls -- `exercise_utils.file`: File creation/appending -- `exercise_utils.git`: Common Git operations like commit, initializing a repository, and adding files -- `exercise_utils.gitmastery`: Git-Mastery specific functions like creating the start tag - -## Contributing utility functions - -These should cover around 80% of all use cases, but if there isn't an existing utility function for your use case, `exercise_utils.cli.run_command` is available to execute any other CLI calls. - -If you believe that more utility functions should be supported, feel free to open a PR adding one. - -{: .note } - -If you add a new file, for example `exercise_utils/general.py`, please update `app` to include the file in the `EXERICSE_UTILS_FILES` constant [here](https://github.com/git-mastery/app/blob/6786aa998e9c8f7ca63c77534bfaf5b7514a89c2/app/utils/gitmastery.py#L212). diff --git a/docs/tooling/index.md b/docs/tooling/index.md deleted file mode 100644 index 108a45c..0000000 --- a/docs/tooling/index.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Tooling -nav_order: 4 ---- - -Git-Mastery maintains various tools to support development. diff --git a/index.markdown b/index.markdown index f1ebbb4..52be409 100644 --- a/index.markdown +++ b/index.markdown @@ -7,25 +7,38 @@ nav_order: 1 # Git-Mastery Developers {: .fs-6 .fw-300 } -Developer documentation about all things Git-Mastery. +Developer documentation for the Git-Mastery ecosystem. -## What is Git-Mastery? +Git-Mastery spans multiple repositories that support hands-ons, exercises, verification, and local CLI workflows. -Git-Mastery is a Git education tool developed by the National University of Singapore School of Computing to help students master Git through hands-on, guided tours with real-world scenario exercises. +## Start here -## Try Git-Mastery! +- New contributor: [Getting Started](/developers/docs/getting-started) +- Exercise contributor: [Exercises](/developers/docs/exercises) +- CLI contributor: [App](/developers/docs/app) +- Library contributor: [Libraries](/developers/docs/libraries) +- System view: [Overview](/developers/docs/overview) -Before contributing to Git-Mastery, it would be good if you tried it out to get a sense of what students experience. +## Quick paths -To setup Git-Mastery, refer to [this setup guide](https://git-mastery.org/companion-app/index.html) for your OS. +- Need help deciding which repo to change? Start with [Choosing where to change](/developers/docs/overview/choosing-where-to-change) +- Want to understand how repos fit together? Read the [Ecosystem map](/developers/docs/overview/ecosystem-map) -We recommend following along with [tour 1](https://git-mastery.org/lessons/trail/recordingFolderHistory/) and [tour 2](https://git-mastery.org/lessons/trail/backingUpOnCloud/) of the Git-Mastery curriculum! +## Core repositories -## Contributor's progression +- [`exercises`](https://github.com/git-mastery/exercises) +- [`app`](https://github.com/git-mastery/app) +- [`repo-smith`](https://github.com/git-mastery/repo-smith) +- [`git-autograder`](https://github.com/git-mastery/git-autograder) -You are welcome to start contributing to Git-Mastery in any way you see fit! +## Supporting repositories -However, if you are looking for a certain angle to start at, we recommend [contributing 1-2 hands-on](/developers/docs/contributing/hands-on) before diving into [contributing exercises](/developers/docs/contributing/exercise)! +- [`progress-dashboard`](https://github.com/git-mastery/progress-dashboard) +- [`actions`](https://github.com/git-mastery/actions) + +## First contribution recommendation + +If you are looking for a good starting point, begin with 1 to 2 hands-ons before moving into full exercises or CLI and library work. ## Noticed something wrong with the documentation? From 0fd9ef2977d6d88fd1c14a7b952804cefdfde412 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 10:58:22 +0800 Subject: [PATCH 2/9] Update path names and improve documentation --- docs/app/command-reference.md | 20 ++++++++- docs/app/configuration.md | 4 +- docs/app/download-and-verify-flow.md | 4 +- docs/app/index.md | 22 +++++++-- docs/app/progress-integration.md | 8 ++-- docs/exercises/download-workflow.md | 4 +- docs/exercises/exercise-structure.md | 4 +- docs/exercises/exercise-utils.md | 6 ++- docs/exercises/exercise.md | 16 ++++--- docs/exercises/hands-on-structure.md | 51 --------------------- docs/exercises/hands-on.md | 54 ++++++++++++++++++++--- docs/exercises/index.md | 13 +++--- docs/exercises/testing-patterns.md | 4 +- docs/exercises/verification-workflow.md | 4 +- docs/getting-started/common-workflows.md | 10 +++-- docs/getting-started/index.md | 10 ++--- docs/getting-started/setup.md | 6 +-- docs/libraries/git-autograder.md | 8 ++-- docs/libraries/index.md | 12 ++--- docs/libraries/published-packages.md | 13 ------ docs/libraries/repo-smith.md | 8 ++-- docs/overview/choosing-where-to-change.md | 48 -------------------- docs/overview/ecosystem-map.md | 4 +- docs/overview/index.md | 7 ++- index.markdown | 11 ++--- 25 files changed, 158 insertions(+), 193 deletions(-) delete mode 100644 docs/exercises/hands-on-structure.md delete mode 100644 docs/libraries/published-packages.md delete mode 100644 docs/overview/choosing-where-to-change.md diff --git a/docs/app/command-reference.md b/docs/app/command-reference.md index 1b86c05..77e826f 100644 --- a/docs/app/command-reference.md +++ b/docs/app/command-reference.md @@ -1,10 +1,10 @@ --- -title: Command reference +title: CLI command reference parent: App nav_order: 1 --- -# Command reference +# CLI command reference The `gitmastery` CLI is the main entrypoint for local Git-Mastery workflows. @@ -61,3 +61,19 @@ The REPL accepts both Git-Mastery commands and shell commands: - Use `/verify` or `gitmastery verify` for Git-Mastery commands - Use `cd` to change directories inside the REPL - Any other command is executed through the local shell + +## E2E test coverage + +The current E2E suite lives under `app/tests/e2e` and includes coverage for: + +- `check` +- `setup` +- `download` +- `verify` +- `progress` +- `version` +- the REPL + +If you add a new top-level command or change the visible behavior of an existing command, add or update E2E tests in `app/tests/e2e` so the built CLI behavior stays covered. + +The CI workflows run the E2E suite through `uv run pytest tests/e2e/ -v` after building the binary and setting `GITMASTERY_BINARY`. diff --git a/docs/app/configuration.md b/docs/app/configuration.md index 2673267..f04116a 100644 --- a/docs/app/configuration.md +++ b/docs/app/configuration.md @@ -1,10 +1,10 @@ --- -title: Configuration +title: Configuration reference parent: App nav_order: 2 --- -# Configuration +# Configuration reference The app uses local configuration files to track Git-Mastery setup and exercise metadata. diff --git a/docs/app/download-and-verify-flow.md b/docs/app/download-and-verify-flow.md index 501abf0..ee229ab 100644 --- a/docs/app/download-and-verify-flow.md +++ b/docs/app/download-and-verify-flow.md @@ -1,10 +1,10 @@ --- -title: Download and verify flow +title: Download and verification flow parent: App nav_order: 3 --- -# Download and verify flow +# Download and verification flow This page explains how the `gitmastery` CLI coordinates exercise content from `exercises` with runtime behavior in `app`. diff --git a/docs/app/index.md b/docs/app/index.md index 487bf1a..ed7df5c 100644 --- a/docs/app/index.md +++ b/docs/app/index.md @@ -24,7 +24,21 @@ The [`app`](https://github.com/git-mastery/app) repository contains the `gitmast ## Suggested reading -1. [Command reference](/developers/docs/app/command-reference) -2. [Configuration](/developers/docs/app/configuration) -3. [Download and verify flow](/developers/docs/app/download-and-verify-flow) -4. [Progress integration](/developers/docs/app/progress-integration) +1. [CLI command reference](/developers/docs/app/command-reference) +2. [Configuration reference](/developers/docs/app/configuration) +3. [Download and verification flow](/developers/docs/app/download-and-verify-flow) +4. [Progress tracking](/developers/docs/app/progress-integration) + +## Testing expectations + +`app` has an end-to-end test suite under `app/tests/e2e` that exercises the built CLI across setup, check, download, verify, progress, version, and REPL flows. + +When you add a new command or change command behavior, update or add E2E tests for the user-visible behavior. Command-level unit tests are useful, but they do not replace the cross-platform CLI coverage provided by the E2E suite. + +Typical local flow: + +```bash +uv run pyinstaller gitmastery.spec +export GITMASTERY_BINARY="$PWD/dist/gitmastery" +uv run pytest tests/e2e/ -v +``` diff --git a/docs/app/progress-integration.md b/docs/app/progress-integration.md index d23babb..edd638c 100644 --- a/docs/app/progress-integration.md +++ b/docs/app/progress-integration.md @@ -1,10 +1,10 @@ --- -title: Progress integration +title: Progress tracking parent: App -nav_order: 3 +nav_order: 4 --- -# Progress integration +# Progress tracking The app is responsible for writing and syncing exercise progress based on verification results. @@ -49,4 +49,4 @@ When remote sync is enabled, later `verify` and `progress reset` runs also updat {: .reference } -See [Download and verify flow](/developers/docs/app/download-and-verify-flow) for the command orchestration that happens before progress is updated. +See [Download and verification flow](/developers/docs/app/download-and-verify-flow) for the command orchestration that happens before progress is updated. diff --git a/docs/exercises/download-workflow.md b/docs/exercises/download-workflow.md index dda5bc0..c148d6f 100644 --- a/docs/exercises/download-workflow.md +++ b/docs/exercises/download-workflow.md @@ -1,9 +1,11 @@ --- -title: Download workflow +title: Download flow parent: Exercises nav_order: 4 --- +# Download flow + ## General download sequence Both hands-ons and exercises are downloaded using the same `gitmastery download` command. diff --git a/docs/exercises/exercise-structure.md b/docs/exercises/exercise-structure.md index b79f10a..72d229b 100644 --- a/docs/exercises/exercise-structure.md +++ b/docs/exercises/exercise-structure.md @@ -1,10 +1,10 @@ --- -title: Exercise structure +title: Exercise format reference parent: Exercises nav_order: 3 --- -# Exercise structure +# Exercise format reference Exercises follow a fixed structure for the Git-Mastery app to pick up on: diff --git a/docs/exercises/exercise-utils.md b/docs/exercises/exercise-utils.md index 2b0714d..6e23f2c 100644 --- a/docs/exercises/exercise-utils.md +++ b/docs/exercises/exercise-utils.md @@ -1,9 +1,11 @@ --- -title: Exercise utilities +title: Exercise utilities reference parent: Exercises -nav_order: 6 +nav_order: 7 --- +# Exercise utilities reference + These utilities are contained within the [`exercises/exercise_utils` module](https://github.com/git-mastery/exercises/tree/main/exercise_utils). They are loaded during exercise download, which means that the `app` has access to these functions while setting up a hands-on or exercise. diff --git a/docs/exercises/exercise.md b/docs/exercises/exercise.md index 3e727cd..dbe3b13 100644 --- a/docs/exercises/exercise.md +++ b/docs/exercises/exercise.md @@ -1,9 +1,11 @@ --- -title: Exercise +title: How to add an exercise parent: Exercises nav_order: 2 --- +# How to add an exercise + 1. TOC {:toc} @@ -31,11 +33,11 @@ Then, the script will prompt you for: 1. The name of the exercise, likely specified in the corresponding exercise discussion. Use kebab case. 2. The exercise tags, split by spaces, likely specified in the corresponding exercise discussion. -3. The exercise repository type. The scaffold currently supports `local`, `remote`, and `ignore` directly. Read the [Exercise structure](/developers/docs/exercises/exercise-structure#configuration-structure) section for more information. +3. The exercise repository type. The scaffold currently supports `local`, `remote`, and `ignore` directly. Read the [Exercise format reference](/developers/docs/exercises/exercise-structure#configuration-structure) section for more information. {: .reference } -Refer to the [Exercise structure](/developers/docs/exercises/exercise-structure) page for more information about the generated folder structure. +Refer to the [Exercise format reference](/developers/docs/exercises/exercise-structure) page for more information about the generated folder structure. ## Download setup @@ -45,13 +47,13 @@ The app loads `download.py` dynamically from the exercises source, reads `__reso {: .reference } -For more information about how Git-Mastery downloads exercises, refer to the [Download workflow](/developers/docs/exercises/download-workflow). +For more information about how Git-Mastery downloads exercises, refer to the [Download flow](/developers/docs/exercises/download-workflow). {: .note } > `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. > -> For the full list of utility functions, refer to [Exercise utilities](/developers/docs/exercises/exercise-utils). +> For the full list of utility functions, refer to [Exercise utilities reference](/developers/docs/exercises/exercise-utils). These are some references for download setups for other exercises: @@ -82,7 +84,7 @@ The verification process is controlled by `verify.py`. {: .reference } -For more information about how Git-Mastery verifies exercise attempts, refer to the [Verification workflow](/developers/docs/exercises/verification-workflow). +For more information about how Git-Mastery verifies exercise attempts, refer to the [Verification flow](/developers/docs/exercises/verification-workflow). The [`git-autograder`](https://github.com/git-mastery/git-autograder) library builds the `GitAutograderExercise` object passed to `verify(...)`. It exposes the exercise config, the working repository, and optional answer parsing. @@ -126,7 +128,7 @@ Refer to existing `test_verify.py` files to see examples of unit testing the ver {: .reference } -See [Testing patterns](/developers/docs/exercises/testing-patterns) for the recommended `GitAutograderTestLoader` workflow. +See [Testing guide](/developers/docs/exercises/testing-patterns) for the recommended `GitAutograderTestLoader` workflow. You can run the unit tests of your exercise via: diff --git a/docs/exercises/hands-on-structure.md b/docs/exercises/hands-on-structure.md deleted file mode 100644 index ee03fb5..0000000 --- a/docs/exercises/hands-on-structure.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Hands-on structure -parent: Exercises -nav_order: 7 ---- - -All hands-ons are stored within the `hands_on` folder of the [`exercises`](https://github.com/git-mastery/exercises) repository. - -They are represented by a single Python file whose name is the hands-on ID. - -Hands-ons are not graded and progress is not tracked. They only help set up the student's folder structure for a given lesson. - -{: .note } - -Git-Mastery uses the format `hp-` for hands-on names, for example `hp-init-repo`, to differentiate them from exercise names. Internally, the `hands_on/` folder stores the Python files, for example `hands_on/init_repo.py`. - -## File structure - -Given below is a simple example: - -```python -import os - -from exercise_utils.cli import run_command -from exercise_utils.file import append_to_file, create_or_update_file -from exercise_utils.git import add, init - -__requires_git__ = True -__requires_github__ = False - - -def download(verbose: bool): - os.makedirs("things") - os.chdir("things") - init(verbose) - create_or_update_file( - "fruits.txt", - """ - apples - bananas - cherries - """, - ) - add(["fruits.txt"], verbose) - run_command(["git", "add", "fruits.txt"], verbose) - append_to_file("fruits.txt", "dragon fruits") -``` - -The setup instructions go under the `download` function. - -`__requires_git__` and `__requires_github__` tell the Git-Mastery app whether to run automatic verification that the student has set up Git and GitHub CLI correctly. diff --git a/docs/exercises/hands-on.md b/docs/exercises/hands-on.md index 3699cdb..8407d47 100644 --- a/docs/exercises/hands-on.md +++ b/docs/exercises/hands-on.md @@ -1,9 +1,11 @@ --- -title: Hands-on +title: How to add a hands-on parent: Exercises nav_order: 1 --- +# How to add a hands-on + 1. TOC {:toc} @@ -43,21 +45,61 @@ Then, the script will prompt you for: 2. Whether the hands-on requires Git 3. Whether the hands-on requires GitHub -{: .reference } +Each hands-on is implemented as a single file at `hands_on/.py`, containing the instructions to set up the hands-on sandbox. -Refer to the [Hands-on structure](/developers/docs/exercises/hands-on-structure) page for more information about the generated file structure. +## Hands-on file format -Each hands-on is implemented as a single file at `hands_on/.py`, containing the instructions to set up the hands-on sandbox. +All hands-ons are stored within the `hands_on` folder of the [`exercises`](https://github.com/git-mastery/exercises) repository. + +They are represented by a single Python file whose name is the hands-on ID. + +Hands-ons are not graded and progress is not tracked. They only help set up the student's folder structure for a given lesson. + +{: .note } + +Git-Mastery uses the format `hp-` for hands-on names, for example `hp-init-repo`, to differentiate them from exercise names. Internally, the `hands_on/` folder stores the Python files, for example `hands_on/init_repo.py`. + +```python +import os + +from exercise_utils.cli import run_command +from exercise_utils.file import append_to_file, create_or_update_file +from exercise_utils.git import add, init + +__requires_git__ = True +__requires_github__ = False + + +def download(verbose: bool): + os.makedirs("things") + os.chdir("things") + init(verbose) + create_or_update_file( + "fruits.txt", + """ + apples + bananas + cherries + """, + ) + add(["fruits.txt"], verbose) + run_command(["git", "add", "fruits.txt"], verbose) + append_to_file("fruits.txt", "dragon fruits") +``` + +The setup instructions go under the `download` function. + +`__requires_git__` and `__requires_github__` tell the app whether to run automatic verification that the student has set up Git and GitHub CLI correctly. {: .reference } -For more information about how Git-Mastery downloads a hands-on, refer to the [Download workflow](/developers/docs/exercises/download-workflow). +For more information about how Git-Mastery downloads a hands-on, refer to the [Download flow](/developers/docs/exercises/download-workflow). {: .note } > `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. > -> For the full list of utility functions, refer to [Exercise utilities](/developers/docs/exercises/exercise-utils). +> For the full list of utility functions, refer to [Exercise utilities reference](/developers/docs/exercises/exercise-utils). These are some references for download setups for other hands-ons: diff --git a/docs/exercises/index.md b/docs/exercises/index.md index e7c0b47..eca466d 100644 --- a/docs/exercises/index.md +++ b/docs/exercises/index.md @@ -19,9 +19,10 @@ The [`exercises`](https://github.com/git-mastery/exercises) repository contains ## Suggested reading -1. [Hands-on](/developers/docs/exercises/hands-on) -2. [Exercise](/developers/docs/exercises/exercise) -3. [Exercise structure](/developers/docs/exercises/exercise-structure) -4. [Download workflow](/developers/docs/exercises/download-workflow) -5. [Verification workflow](/developers/docs/exercises/verification-workflow) -6. [Testing patterns](/developers/docs/exercises/testing-patterns) +1. [How to add a hands-on](/developers/docs/exercises/hands-on) +2. [How to add an exercise](/developers/docs/exercises/exercise) +3. [Exercise format reference](/developers/docs/exercises/exercise-structure) +4. [Download flow](/developers/docs/exercises/download-workflow) +5. [Verification flow](/developers/docs/exercises/verification-workflow) +6. [Testing guide](/developers/docs/exercises/testing-patterns) +7. [Exercise utilities reference](/developers/docs/exercises/exercise-utils) diff --git a/docs/exercises/testing-patterns.md b/docs/exercises/testing-patterns.md index 98a6382..6e79247 100644 --- a/docs/exercises/testing-patterns.md +++ b/docs/exercises/testing-patterns.md @@ -1,10 +1,10 @@ --- -title: Testing patterns +title: Testing guide parent: Exercises nav_order: 6 --- -# Testing patterns +# Testing guide Exercise verification tests usually focus on repository end states rather than replaying the entire student workflow. diff --git a/docs/exercises/verification-workflow.md b/docs/exercises/verification-workflow.md index 5f062ed..9b8bd2c 100644 --- a/docs/exercises/verification-workflow.md +++ b/docs/exercises/verification-workflow.md @@ -1,10 +1,10 @@ --- -title: Verification workflow +title: Verification flow parent: Exercises nav_order: 5 --- -# Verification workflow +# Verification flow Exercise verification is driven by the student's local exercise metadata and the exercise's `verify.py` script. diff --git a/docs/getting-started/common-workflows.md b/docs/getting-started/common-workflows.md index f94daf9..cb56380 100644 --- a/docs/getting-started/common-workflows.md +++ b/docs/getting-started/common-workflows.md @@ -1,10 +1,10 @@ --- -title: Common workflows -parent: Getting Started +title: Common contributor workflows +parent: Getting started nav_order: 2 --- -# Common workflows +# Common contributor workflows ## Choosing a repository @@ -20,6 +20,7 @@ nav_order: 2 - `exercises`: `uv venv && source .venv/bin/activate && uv pip install -r requirements.txt` - Run Python tests: - `app`: `uv run pytest` + - `app` E2E CLI tests: build the binary, set `GITMASTERY_BINARY`, then run `uv run pytest tests/e2e/ -v` - `repo-smith`: `uv run pytest -s -vv` - `git-autograder`: `uv run pytest -s -vv` - `exercises`: `./test.sh ` or `pytest . -s -vv` @@ -40,7 +41,8 @@ nav_order: 2 1. Work in `app` 2. Update the relevant command or config logic 3. Re-run the local CLI using `uv run python main.py` -4. Test against a local Git-Mastery exercises directory +4. Add or update E2E tests when the command surface or user-visible behavior changes +5. Test against a local Git-Mastery exercises directory ### Extend verification helpers diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index eb80ccb..16af2a9 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -1,10 +1,10 @@ --- -title: Getting Started +title: Getting started nav_order: 3 has_children: true --- -# Getting Started +# Getting started This section helps contributors choose the right repository, set up a local development environment, and follow common Git-Mastery workflows. @@ -12,16 +12,16 @@ This section helps contributors choose the right repository, set up a local deve - Implement or update a hands-on or exercise: [Exercises](/developers/docs/exercises) - Work on the CLI experience: [App](/developers/docs/app) -- Update verification or testing libraries: [Libraries](/developers/docs/libraries) +- Update verification or testing libraries: [Shared libraries](/developers/docs/libraries) ## Recommended reading order 1. [Setup](/developers/docs/getting-started/setup) -2. [Common workflows](/developers/docs/getting-started/common-workflows) +2. [Common contributor workflows](/developers/docs/getting-started/common-workflows) 3. The section for the repository you plan to contribute to ## After setup - Working on authoring or verification: go to [Exercises](/developers/docs/exercises) - Working on the CLI runtime: go to [App](/developers/docs/app) -- Working on reusable test or grading code: go to [Libraries](/developers/docs/libraries) +- Working on reusable test or grading code: go to [Shared libraries](/developers/docs/libraries) diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 5c9f094..ec7906d 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -1,10 +1,10 @@ --- -title: Setup -parent: Getting Started +title: Development setup +parent: Getting started nav_order: 1 --- -# Setup +# Development setup Git-Mastery is standardizing on a `uv`-first local development workflow. diff --git a/docs/libraries/git-autograder.md b/docs/libraries/git-autograder.md index 9f8118f..4f66b09 100644 --- a/docs/libraries/git-autograder.md +++ b/docs/libraries/git-autograder.md @@ -1,10 +1,10 @@ --- -title: git-autograder -parent: Libraries +title: git-autograder reference +parent: Shared libraries nav_order: 2 --- -# git-autograder +# git-autograder reference `git-autograder` is the verification library used by Git-Mastery exercise `verify.py` scripts. @@ -81,6 +81,6 @@ It supports question-and-answer style validation and can accumulate validation r {: .reference } -See [Exercise](/developers/docs/exercises/exercise) and [Verification workflow](/developers/docs/exercises/verification-workflow) for how `git-autograder` is used inside the main Git-Mastery exercise flow. +See [How to add an exercise](/developers/docs/exercises/exercise) and [Verification flow](/developers/docs/exercises/verification-workflow) for how `git-autograder` is used inside the main Git-Mastery exercise flow. For implementation details, refer to the [`git-autograder` repository](https://github.com/git-mastery/git-autograder). diff --git a/docs/libraries/index.md b/docs/libraries/index.md index b97e4e2..f919503 100644 --- a/docs/libraries/index.md +++ b/docs/libraries/index.md @@ -1,10 +1,10 @@ --- -title: Libraries +title: Shared libraries nav_order: 6 has_children: true --- -# Libraries +# Shared libraries Git-Mastery maintains reusable libraries that support exercise verification and testing. @@ -20,10 +20,6 @@ These libraries are most relevant when you are working on reusable grading behav - Reach for `repo-smith` when your test needs a specific repository history or working tree state. - Reach for `git-autograder` when multiple exercises would benefit from the same verification helper or abstraction. -{: .reference } +## Scope of this section -For a broader decision guide across repositories, see [Choosing where to change](/developers/docs/overview/choosing-where-to-change). - -## Additional published packages - -- [`difflib-parser`](https://github.com/git-mastery/difflib-parser) +This section focuses on the two shared libraries that new Git-Mastery contributors are most likely to touch while working on verification behavior or test infrastructure. diff --git a/docs/libraries/published-packages.md b/docs/libraries/published-packages.md deleted file mode 100644 index 1d0f21b..0000000 --- a/docs/libraries/published-packages.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Published packages -parent: Libraries -nav_order: 3 ---- - -## Published Python packages - -Git-Mastery publishes and maintains several public packages: - -- [`repo-smith`](https://github.com/git-mastery/repo-smith) -- [`git-autograder`](https://github.com/git-mastery/git-autograder) -- [`difflib-parser`](https://github.com/git-mastery/difflib-parser) diff --git a/docs/libraries/repo-smith.md b/docs/libraries/repo-smith.md index 1c21be3..0a488dc 100644 --- a/docs/libraries/repo-smith.md +++ b/docs/libraries/repo-smith.md @@ -1,10 +1,10 @@ --- -title: repo-smith -parent: Libraries +title: repo-smith reference +parent: Shared libraries nav_order: 1 --- -# repo-smith +# repo-smith reference `repo-smith` is a YAML-based library for initializing Git repositories for unit testing. @@ -101,6 +101,6 @@ def test_with_hook() -> None: {: .reference } -See [Exercises testing patterns](/developers/docs/exercises/testing-patterns) for how this is typically wrapped inside `exercises` tests. +See [Testing guide](/developers/docs/exercises/testing-patterns) for how this is typically wrapped inside `exercises` tests. For the full field-level specification, refer to `repo-smith/specification.md` in the [`repo-smith` repository](https://github.com/git-mastery/repo-smith). diff --git a/docs/overview/choosing-where-to-change.md b/docs/overview/choosing-where-to-change.md deleted file mode 100644 index c988cf4..0000000 --- a/docs/overview/choosing-where-to-change.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Choosing where to change -parent: Overview -nav_order: 2 ---- - -# Choosing where to change - -When a feature or bug spans multiple repositories, it helps to decide where the primary change belongs. - -## Change `exercises` when - -- the exercise instructions are wrong -- a hands-on or exercise setup needs different files or repository state -- a `verify.py` rule is exercise-specific -- a test for a single exercise needs updating - -## Change `app` when - -- the CLI command behavior is wrong -- download, verify, setup, or progress flows need to change for all users -- `.gitmastery.json` or `.gitmastery-exercise.json` handling needs to change -- the exercises source behavior or local-development override behavior needs to change - -## Change `git-autograder` when - -- multiple exercises need the same verification helper -- `GitAutograderExercise`, output handling, answer parsing, or repository wrappers need improvement -- grading behavior should become easier to express across many exercises - -## Change `repo-smith` when - -- tests need a repository state that is hard to express today -- a new YAML step type or hook capability would simplify many exercise tests -- repository construction logic should be reusable beyond a single exercise - -## Typical examples - -- A bug in one exercise's branch-order check usually belongs in `exercises`. -- A missing helper for parsing `answers.txt` usually belongs in `git-autograder`. -- A missing repository setup primitive for tests usually belongs in `repo-smith`. -- A bug in `gitmastery progress sync on` belongs in `app`. - -## Rule of thumb - -If the change is specific to one learning task, start in `exercises`. - -If the change improves the platform behavior for many exercises or many contributors, it probably belongs in `app`, `git-autograder`, or `repo-smith`. diff --git a/docs/overview/ecosystem-map.md b/docs/overview/ecosystem-map.md index cba70a6..0d47193 100644 --- a/docs/overview/ecosystem-map.md +++ b/docs/overview/ecosystem-map.md @@ -1,10 +1,10 @@ --- -title: Ecosystem map +title: System overview parent: Overview nav_order: 1 --- -# Ecosystem map +# System overview The main Git-Mastery repositories work together as follows: diff --git a/docs/overview/index.md b/docs/overview/index.md index b404efa..a206c0b 100644 --- a/docs/overview/index.md +++ b/docs/overview/index.md @@ -24,12 +24,11 @@ These repositories are part of the wider ecosystem and may receive dedicated doc ## Where to start -- New contributor: start with [Getting Started](/developers/docs/getting-started) +- New contributor: start with [Getting started](/developers/docs/getting-started) - Exercise contributor: go to [Exercises](/developers/docs/exercises) - CLI contributor: go to [App](/developers/docs/app) -- Library contributor: go to [Libraries](/developers/docs/libraries) +- Library contributor: go to [Shared libraries](/developers/docs/libraries) ## Useful overview pages -- [Ecosystem map](/developers/docs/overview/ecosystem-map) -- [Choosing where to change](/developers/docs/overview/choosing-where-to-change) +- [System overview](/developers/docs/overview/ecosystem-map) diff --git a/index.markdown b/index.markdown index 52be409..b5ce97a 100644 --- a/index.markdown +++ b/index.markdown @@ -9,20 +9,21 @@ nav_order: 1 {: .fs-6 .fw-300 } Developer documentation for the Git-Mastery ecosystem. -Git-Mastery spans multiple repositories that support hands-ons, exercises, verification, and local CLI workflows. +## What is Git-Mastery? + +Git-Mastery is a Git education tool developed by the National University of Singapore School of Computing to help students master Git through hands-on, guided tours with real-world scenario exercises. ## Start here -- New contributor: [Getting Started](/developers/docs/getting-started) +- New contributor: [Getting started](/developers/docs/getting-started) - Exercise contributor: [Exercises](/developers/docs/exercises) - CLI contributor: [App](/developers/docs/app) -- Library contributor: [Libraries](/developers/docs/libraries) +- Library contributor: [Shared libraries](/developers/docs/libraries) - System view: [Overview](/developers/docs/overview) ## Quick paths -- Need help deciding which repo to change? Start with [Choosing where to change](/developers/docs/overview/choosing-where-to-change) -- Want to understand how repos fit together? Read the [Ecosystem map](/developers/docs/overview/ecosystem-map) +- Want to understand how repos fit together? Read the [System overview](/developers/docs/overview/ecosystem-map) ## Core repositories From 61398b3991af9a0b46aa2128f84d53137c992074 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:43:22 +0800 Subject: [PATCH 3/9] Revise and reorganize developer documentation, removing outdated content and enhancing clarity in setup and ecosystem overview --- docs/getting-started/common-workflows.md | 54 --------------- docs/getting-started/index.md | 14 +--- docs/getting-started/setup.md | 84 ++++++++++++++---------- docs/overview/ecosystem-map.md | 36 ---------- docs/overview/index.md | 14 ++-- docs/overview/system-overview.md | 23 +++++++ index.markdown => index.md | 31 +++++---- 7 files changed, 95 insertions(+), 161 deletions(-) delete mode 100644 docs/getting-started/common-workflows.md delete mode 100644 docs/overview/ecosystem-map.md create mode 100644 docs/overview/system-overview.md rename index.markdown => index.md (50%) diff --git a/docs/getting-started/common-workflows.md b/docs/getting-started/common-workflows.md deleted file mode 100644 index cb56380..0000000 --- a/docs/getting-started/common-workflows.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Common contributor workflows -parent: Getting started -nav_order: 2 ---- - -# Common contributor workflows - -## Choosing a repository - -- Use `exercises` for new hands-ons, exercises, and verification tests. -- Use `app` for CLI behavior, download flows, and local progress features. -- Use `repo-smith` when exercise verification tests need new repository setup primitives. -- Use `git-autograder` when verification logic needs new reusable grading helpers. - -## Common local tasks - -- Install dependencies: - - `app`, `repo-smith`, `git-autograder`: `uv sync` - - `exercises`: `uv venv && source .venv/bin/activate && uv pip install -r requirements.txt` -- Run Python tests: - - `app`: `uv run pytest` - - `app` E2E CLI tests: build the binary, set `GITMASTERY_BINARY`, then run `uv run pytest tests/e2e/ -v` - - `repo-smith`: `uv run pytest -s -vv` - - `git-autograder`: `uv run pytest -s -vv` - - `exercises`: `./test.sh ` or `pytest . -s -vv` -- Run the CLI locally from `app`: `uv run python main.py` -- Test exercise downloads from `exercises`: `./test-download.sh ` - -## Common contribution paths - -### Author or update an exercise - -1. Work in `exercises` -2. Implement `download.py`, `verify.py`, and `test_verify.py` -3. Test the download flow locally -4. Test the verification flow locally - -### Change CLI behavior - -1. Work in `app` -2. Update the relevant command or config logic -3. Re-run the local CLI using `uv run python main.py` -4. Add or update E2E tests when the command surface or user-visible behavior changes -5. Test against a local Git-Mastery exercises directory - -### Extend verification helpers - -1. Work in `git-autograder` when the helper should be reusable across exercises -2. Work in `repo-smith` when the missing piece is repository-state setup for tests - -## Recommended contributor path - -If you are new to Git-Mastery, start by contributing to `exercises`, then move to `app` or the supporting libraries once you are familiar with the exercise lifecycle. diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 16af2a9..631ca5d 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -12,16 +12,4 @@ This section helps contributors choose the right repository, set up a local deve - Implement or update a hands-on or exercise: [Exercises](/developers/docs/exercises) - Work on the CLI experience: [App](/developers/docs/app) -- Update verification or testing libraries: [Shared libraries](/developers/docs/libraries) - -## Recommended reading order - -1. [Setup](/developers/docs/getting-started/setup) -2. [Common contributor workflows](/developers/docs/getting-started/common-workflows) -3. The section for the repository you plan to contribute to - -## After setup - -- Working on authoring or verification: go to [Exercises](/developers/docs/exercises) -- Working on the CLI runtime: go to [App](/developers/docs/app) -- Working on reusable test or grading code: go to [Shared libraries](/developers/docs/libraries) +- Make improvements to shared libraries: [Shared libraries](/developers/docs/libraries) diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index ec7906d..a4c8664 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -8,67 +8,79 @@ nav_order: 1 Git-Mastery is standardizing on a `uv`-first local development workflow. -Use the guidance on this page as the default when setting up repositories locally. Some repositories are still in transition, so the exact `uv` command differs slightly by repository. +Use the guidance on this page as the default when setting up repositories locally. Some repositories are still in transition, and may be using `pip` and `venv` for local development. ## Prerequisites - Git - Python 3.13+ -- [`uv`](https://docs.astral.sh/uv/getting-started/installation/) +- `uv` (refer to [installation guide](https://docs.astral.sh/uv/getting-started/installation/)) - GitHub CLI (`gh`) installed and authenticated when working on flows that interact with GitHub -## Recommended repository setup +## Repository setup (uv) 1. Fork the repository you want to work on. 2. Clone your fork. -3. Set up the environment with the repository's `uv` workflow. -```bash -git clone https://github.com// -cd -``` + ```bash + git clone https://github.com// + cd + ``` -## Repository-specific setup +3. Run the following command to set up virtual environment and install dependencies: -### `app` + ```bash + uv sync + ``` -`app` already has `pyproject.toml` and `uv.lock`, so the standard workflow is: +4. Set up pre-commit hooks using LeftHook -```bash -uv sync -uv run lefthook install -uv run python main.py -``` + {: .warning-title } -### `repo-smith` + > Note + > + > Recommended to install for formatting and linting support. -`repo-smith` is already a Python package, so the standard workflow is: + ```bash + uv run lefthook install + ``` -```bash -uv sync -uv run pytest -s -vv -``` +## Repository setup (pip) -### `git-autograder` +{: .warning-title } -`git-autograder` also has package metadata, so the standard workflow is: +> Deprecated +> +> As we are transitioning to uv, this setup is still relevant for some repositories. -```bash -uv sync -uv run pytest -s -vv -``` +1. Fork the repository you want to work on. +2. Clone your fork. -### `exercises` + ```bash + git clone https://github.com// + cd + ``` -`exercises` has not fully migrated to a `uv sync`-based project layout yet, but you can still use `uv` as the package manager for local development: +3. Run the following command to set up virtual environment and install dependencies: -```bash -uv venv -source .venv/bin/activate -uv pip install -r requirements.txt -``` + ```bash + pip install -r requirements.txt + python -m venv .venv + source .venv/bin/activate # macOS/Linux + source .venv/Scripts/activate # Windows + ``` + +4. Set up pre-commit hooks using LeftHook + + {: .warning-title } + + > Note + > + > Recommended to install for formatting and linting support. -The repository still contains older helper scripts such as `setup.sh`, `test.sh`, and `test-download.sh`. They remain useful, but new documentation should prefer `uv` where practical. + ```bash + lefthook install + ``` ## GitHub-dependent work diff --git a/docs/overview/ecosystem-map.md b/docs/overview/ecosystem-map.md deleted file mode 100644 index 0d47193..0000000 --- a/docs/overview/ecosystem-map.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: System overview -parent: Overview -nav_order: 1 ---- - -# System overview - -The main Git-Mastery repositories work together as follows: - -```mermaid -flowchart LR - exercises[exercises] --> app[app] - exercises --> gitautograder[git-autograder] - gitautograder --> reposmith[repo-smith] - app --> progress[progress-dashboard] - actions[actions] --> exercises - actions --> app - actions --> gitautograder - actions --> reposmith -``` - -## How to read the map - -- `exercises` is where contributors define hands-ons, exercises, resources, and verification tests. -- `app` downloads and verifies hands-ons and exercises through the `gitmastery` CLI. -- `git-autograder` provides the verification model used by exercise `verify.py` scripts. -- `repo-smith` creates repository states for unit tests that validate `verify.py` behavior. -- `progress-dashboard` and `actions` support the wider platform, but are not first-class documentation sections yet. - -## Common contributor journeys - -- Writing a new exercise usually touches `exercises`, `git-autograder`, and sometimes `repo-smith`. -- Changing how students download, verify, or track progress usually starts in `app`. -- Adding a reusable repository-state primitive belongs in `repo-smith`. -- Adding a reusable grading abstraction belongs in `git-autograder`. diff --git a/docs/overview/index.md b/docs/overview/index.md index a206c0b..79d2722 100644 --- a/docs/overview/index.md +++ b/docs/overview/index.md @@ -6,21 +6,23 @@ has_children: true # Overview -Git-Mastery is a multi-repository project for teaching Git through guided hands-on tasks, graded exercises, and supporting developer tooling. +Git-Mastery is a multi-repository project, and the documentation is organized around the different repositories and components. This overview page serves as a map to the ecosystem, guiding you to the relevant documentation for each part of the project. ## Core repositories +These are the main repositories that make up the Git-Mastery ecosystem, and are the most relevant for contributors: + - [`exercises`](https://github.com/git-mastery/exercises): hands-ons, exercises, download setup, and verification tests - [`app`](https://github.com/git-mastery/app): the `gitmastery` CLI used by students and contributors -- [`repo-smith`](https://github.com/git-mastery/repo-smith): repository state builder used for unit testing exercise verification -- [`git-autograder`](https://github.com/git-mastery/git-autograder): verification library used by exercise `verify.py` scripts +- [`repo-smith`](https://github.com/git-mastery/repo-smith): test-support library that creates Git repository states through helper objects so exercise verification scenarios can be set up deterministically in tests +- [`git-autograder`](https://github.com/git-mastery/git-autograder): grading library that loads a Git-Mastery exercise attempt and turns repository or answer checks into structured verification result ## Supporting repositories These repositories are part of the wider ecosystem and may receive dedicated documentation later: - [`progress-dashboard`](https://github.com/git-mastery/progress-dashboard): visualizes learner progress data -- [`actions`](https://github.com/git-mastery/actions): shared GitHub Actions and reusable automation +- [`difflib-parser`](https://github.com/git-mastery/difflib-parser): library for parsing diff output into structured data ## Where to start @@ -28,7 +30,3 @@ These repositories are part of the wider ecosystem and may receive dedicated doc - Exercise contributor: go to [Exercises](/developers/docs/exercises) - CLI contributor: go to [App](/developers/docs/app) - Library contributor: go to [Shared libraries](/developers/docs/libraries) - -## Useful overview pages - -- [System overview](/developers/docs/overview/ecosystem-map) diff --git a/docs/overview/system-overview.md b/docs/overview/system-overview.md new file mode 100644 index 0000000..2871ce6 --- /dev/null +++ b/docs/overview/system-overview.md @@ -0,0 +1,23 @@ +--- +title: System overview +parent: Overview +nav_order: 1 +--- + +# System overview + +The main Git-Mastery repositories work together as follows: + +```mermaid +flowchart LR + exercises --> gitautograder[git-autograder] + exercises --> reposmith[repo-smith] + app -->|downloads| exercises + app -->|updates| progress[progress-dashboard] +``` + +1. `app` downloads and verifies hands-ons and exercises through the `gitmastery` CLI. +2. `exercises` is where contributors define hands-ons, exercises, resources, and verification tests. +3. `git-autograder` provides the verification model used by exercise `verify.py` scripts. +4. `repo-smith` creates repository states for unit tests that validate `verify.py` behavior. +5. `progress-dashboard` visualizes learner progress data sent by the `gitmastery` CLI. diff --git a/index.markdown b/index.md similarity index 50% rename from index.markdown rename to index.md index b5ce97a..9f45be9 100644 --- a/index.markdown +++ b/index.md @@ -13,33 +13,36 @@ Developer documentation for the Git-Mastery ecosystem. Git-Mastery is a Git education tool developed by the National University of Singapore School of Computing to help students master Git through hands-on, guided tours with real-world scenario exercises. -## Start here +## Try Git-Mastery! + +Before contributing to Git-Mastery, it would be good if you tried it out to get a sense of what students experience. -- New contributor: [Getting started](/developers/docs/getting-started) -- Exercise contributor: [Exercises](/developers/docs/exercises) -- CLI contributor: [App](/developers/docs/app) -- Library contributor: [Shared libraries](/developers/docs/libraries) -- System view: [Overview](/developers/docs/overview) +To setup Git-Mastery, refer to this setup guide for your OS. -## Quick paths +We recommend following along with [tour 1](https://git-mastery.org/lessons/trail/recordingFolderHistory/index.html) and [tour 2](https://git-mastery.org/lessons/trail/backingUpOnCloud/index.html) of the Git-Mastery curriculum! + +## Start here -- Want to understand how repos fit together? Read the [System overview](/developers/docs/overview/ecosystem-map) +- [Getting started](/developers/docs/getting-started) +- [Overview](/developers/docs/overview) +- [Exercises](/developers/docs/exercises) +- [App](/developers/docs/app) +- [Shared libraries](/developers/docs/libraries) -## Core repositories +## Repositories - [`exercises`](https://github.com/git-mastery/exercises) - [`app`](https://github.com/git-mastery/app) - [`repo-smith`](https://github.com/git-mastery/repo-smith) - [`git-autograder`](https://github.com/git-mastery/git-autograder) - -## Supporting repositories - +- [`difflib-parser`](https://github.com/git-mastery/difflib-parser) - [`progress-dashboard`](https://github.com/git-mastery/progress-dashboard) -- [`actions`](https://github.com/git-mastery/actions) ## First contribution recommendation -If you are looking for a good starting point, begin with 1 to 2 hands-ons before moving into full exercises or CLI and library work. +If you are looking for a good starting point, begin with 1 to 2 hands-ons in `exercises` before moving into exercises, CLI and library work! + +Refer to [issues](https://github.com/git-mastery/exercises/issues) tagged with `good first issue` for recommended starting points. ## Noticed something wrong with the documentation? From e989e550fa7c11bc51eff7c7010f24f210f93777 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:03:08 +0800 Subject: [PATCH 4/9] Remove CLI command reference documentation and update index with key features and REPL notes --- docs/app/command-reference.md | 79 ----------------------------------- docs/app/index.md | 29 ++++++------- docs/libraries/index.md | 2 +- 3 files changed, 16 insertions(+), 94 deletions(-) delete mode 100644 docs/app/command-reference.md diff --git a/docs/app/command-reference.md b/docs/app/command-reference.md deleted file mode 100644 index 77e826f..0000000 --- a/docs/app/command-reference.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: CLI command reference -parent: App -nav_order: 1 ---- - -# CLI command reference - -The `gitmastery` CLI is the main entrypoint for local Git-Mastery workflows. - -If the CLI is launched without a subcommand, it starts an interactive REPL instead of exiting immediately. - -## Main command areas - -- `setup`: initialize local Git-Mastery configuration -- `check`: verify Git and GitHub CLI prerequisites -- `download`: download a hands-on or exercise -- `verify`: verify the current exercise attempt -- `progress`: inspect, reset, or sync learner progress -- `version`: print the current app version - -## Command groups - -### `setup` - -- Creates a new Git-Mastery root directory -- Writes `.gitmastery.json` -- Creates a local `progress/` folder with `progress.json` - -### `check` - -- `gitmastery check git`: checks Git installation, minimum version, `user.name`, and `user.email` -- `gitmastery check github`: checks GitHub CLI installation, authentication, and `delete_repo` scope - -These checks are also invoked automatically by other commands when required by an exercise or workflow. - -### `download` - -- Downloads either a hands-on or an exercise from the configured exercises source -- Hands-ons are identified by the `hp-` prefix -- Exercises use `.gitmastery-exercise.json` plus `download.py` to set up the student workspace - -### `verify` - -- Loads the current exercise's metadata -- Executes the exercise's `verify.py` -- Prints the grading result -- Records progress locally and optionally syncs it remotely - -### `progress` - -- `gitmastery progress show`: show the latest known status for each exercise -- `gitmastery progress reset`: reset the current exercise workspace and remove its saved progress entries -- `gitmastery progress sync on`: connect the local tracker to a GitHub fork of the progress repository -- `gitmastery progress sync off`: remove remote sync and keep a local-only progress tracker - -## REPL behavior - -The REPL accepts both Git-Mastery commands and shell commands: - -- Use `/verify` or `gitmastery verify` for Git-Mastery commands -- Use `cd` to change directories inside the REPL -- Any other command is executed through the local shell - -## E2E test coverage - -The current E2E suite lives under `app/tests/e2e` and includes coverage for: - -- `check` -- `setup` -- `download` -- `verify` -- `progress` -- `version` -- the REPL - -If you add a new top-level command or change the visible behavior of an existing command, add or update E2E tests in `app/tests/e2e` so the built CLI behavior stays covered. - -The CI workflows run the E2E suite through `uv run pytest tests/e2e/ -v` after building the binary and setting `GITMASTERY_BINARY`. diff --git a/docs/app/index.md b/docs/app/index.md index ed7df5c..5c7226c 100644 --- a/docs/app/index.md +++ b/docs/app/index.md @@ -8,26 +8,27 @@ has_children: true The [`app`](https://github.com/git-mastery/app) repository contains the `gitmastery` CLI. -## What this section covers - -- CLI responsibilities in the wider Git-Mastery ecosystem -- The main command groups and flows -- Configuration files and local developer behavior -- How the app interacts with exercise verification and progress tracking - -## Main responsibilities +## Key Features - Set up a local Git-Mastery workspace - Fetch hands-ons and exercises from the configured exercises source - Execute verification logic from the `exercises` repository - Maintain local and remote progress tracking -## Suggested reading +## Command Reference + +Refer to [command reference](https://git-mastery.org/companion-app/index.html#git-mastery-app-commands) for the full list of commands, their descriptions, and usage examples. + +{: .warning-title } -1. [CLI command reference](/developers/docs/app/command-reference) -2. [Configuration reference](/developers/docs/app/configuration) -3. [Download and verification flow](/developers/docs/app/download-and-verify-flow) -4. [Progress tracking](/developers/docs/app/progress-integration) +> Note +> +> Experimental command `gitmastery` to spawn a REPL instance with CLI runtime loaded. Not all commands may work as expected in the REPL, and behavior may differ from running the CLI directly.

+> The REPL accepts both Git-Mastery commands and shell commands: +> +> - Use `/verify` or `gitmastery verify` for Git-Mastery commands +> - Use `cd` to change directories inside the REPL +> - Any other command is executed through the local shell ## Testing expectations @@ -38,7 +39,7 @@ When you add a new command or change command behavior, update or add E2E tests f Typical local flow: ```bash -uv run pyinstaller gitmastery.spec +uv run pyinstaller --onefile main.py --name gitmastery export GITMASTERY_BINARY="$PWD/dist/gitmastery" uv run pytest tests/e2e/ -v ``` diff --git a/docs/libraries/index.md b/docs/libraries/index.md index f919503..b6b17c3 100644 --- a/docs/libraries/index.md +++ b/docs/libraries/index.md @@ -22,4 +22,4 @@ These libraries are most relevant when you are working on reusable grading behav ## Scope of this section -This section focuses on the two shared libraries that new Git-Mastery contributors are most likely to touch while working on verification behavior or test infrastructure. +This section focuses on the shared libraries that Git-Mastery contributors are most likely to touch while working on verification behavior or test infrastructure. From a253a462425eec17472be2d8ec83ffe4c5a89941 Mon Sep 17 00:00:00 2001 From: jia xin Date: Sun, 29 Mar 2026 12:31:10 +0800 Subject: [PATCH 5/9] Clean up exercises documentation --- docs/exercises/download-workflow.md | 7 ++++++- docs/exercises/exercise-structure.md | 8 +------- docs/exercises/exercise-utils.md | 14 ++++++++++++-- docs/exercises/exercise.md | 12 ++++++------ docs/exercises/hands-on.md | 3 +-- docs/exercises/testing-patterns.md | 4 ++-- 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/docs/exercises/download-workflow.md b/docs/exercises/download-workflow.md index c148d6f..defc91d 100644 --- a/docs/exercises/download-workflow.md +++ b/docs/exercises/download-workflow.md @@ -14,11 +14,16 @@ Hands-ons use the `hp-` prefix before the hands-on name. ```mermaid flowchart -a[gitmastery download] --> b[Check if prefixed with hp-] +a[gitmastery download] --> a1[Read exercises_source from .gitmastery.json] +a1 --> b[Check if prefixed with hp-] b -- prefixed with hp- --> c[Download hands-on] b -- else --> d[Download exercise] ``` +{: .note } + +> The source that exercises and hands-ons are fetched from is controlled by `exercises_source` in `.gitmastery.json`. See [`exercises_source`](/developers/docs/app/configuration#exercises_source) for the full details and override options. + Refer to the following sections for the specific download sequences. ## Hands-on download diff --git a/docs/exercises/exercise-structure.md b/docs/exercises/exercise-structure.md index 72d229b..9e9b8a0 100644 --- a/docs/exercises/exercise-structure.md +++ b/docs/exercises/exercise-structure.md @@ -16,9 +16,6 @@ Exercises follow a fixed structure for the Git-Mastery app to pick up on: ├── download.py ├── res │ └── ... -├── tests -│ └── specs -│ └── base.yml ├── test_verify.py └── verify.py ``` @@ -28,10 +25,7 @@ Exercises follow a fixed structure for the Git-Mastery app to pick up on: - `download.py`: contains the download instructions to set up the student's exercise - `verify.py`: contains the verification script for the exercise attempt - `res/`: contains resources that are available to students -- `tests/specs/`: contains specification files written using [`repo-smith`](https://github.com/git-mastery/repo-smith) -- `test_verify.py`: contains unit tests for the verification script - -The scaffold created by `exercises/scripts/new-exercise.py` currently places `test_verify.py` at the exercise root, not inside `tests/`. +- `test_verify.py`: contains unit tests for the verification script, written using [`repo-smith`](https://github.com/git-mastery/repo-smith) ## What students see diff --git a/docs/exercises/exercise-utils.md b/docs/exercises/exercise-utils.md index 6e23f2c..57273cb 100644 --- a/docs/exercises/exercise-utils.md +++ b/docs/exercises/exercise-utils.md @@ -16,10 +16,20 @@ They are loaded during exercise download, which means that the `app` has access - `exercise_utils.file`: file creation and appending helpers - `exercise_utils.git`: common Git operations such as commit, repository initialization, and adding files - `exercise_utils.gitmastery`: Git-Mastery specific functions such as creating the start tag +- `exercise_utils.github_cli`: GitHub CLI wrappers for forking, cloning, deleting, and creating repositories, as well as looking up the authenticated user +- `exercise_utils.test`: testing utilities including `GitAutograderTestLoader` and `assert_output` for unit testing verification scripts -## Contributing utility functions +### `exercise_utils.cli` functions + +The existing utility functions should cover most use cases, but if there is no existing utility function for your use case, `exercise_utils.cli` functions are available to execute other CLI calls. These should be used as a last resort. + +`exercise_utils.cli` exposes three functions for running shell commands: -These should cover most use cases, but if there is no existing utility function for your use case, `exercise_utils.cli.run_command` is available to execute other CLI calls. +- `run_command`: runs a command and exits the process if it fails; use for steps that must succeed +- `run_command_no_exit`: runs a command and returns `None` on failure without exiting; use for optional or conditional steps +- `run`: runs a command and returns a `CommandResult` you can inspect (`.is_success()`, `.stdout`, `.returncode`); use when you need to branch on the result + +## Contributing utility functions If you believe that more utility functions should be supported, feel free to open a pull request adding one. diff --git a/docs/exercises/exercise.md b/docs/exercises/exercise.md index dbe3b13..72b384c 100644 --- a/docs/exercises/exercise.md +++ b/docs/exercises/exercise.md @@ -33,11 +33,11 @@ Then, the script will prompt you for: 1. The name of the exercise, likely specified in the corresponding exercise discussion. Use kebab case. 2. The exercise tags, split by spaces, likely specified in the corresponding exercise discussion. -3. The exercise repository type. The scaffold currently supports `local`, `remote`, and `ignore` directly. Read the [Exercise format reference](/developers/docs/exercises/exercise-structure#configuration-structure) section for more information. +3. The exercise repository type. The scaffold currently supports `local`, `remote`, and `ignore` directly. {: .reference } -Refer to the [Exercise format reference](/developers/docs/exercises/exercise-structure) page for more information about the generated folder structure. +Refer to the [Exercise format reference](/developers/docs/exercises/exercise-structure) page for more information about the generated folder structure and repository type options. ## Download setup @@ -51,7 +51,7 @@ For more information about how Git-Mastery downloads exercises, refer to the [Do {: .note } -> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. +> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. Prefer these abstractions over raw CLI calls — for example, use `exercise_utils.file.create_or_update_file` to create or update files, and `exercise_utils.git.commit` to commit changes. Fall back to `exercise_utils.cli.run_command` only when no suitable abstraction exists. > > For the full list of utility functions, refer to [Exercise utilities reference](/developers/docs/exercises/exercise-utils). @@ -80,7 +80,7 @@ You can find the downloaded repository under `test-downloads/`. ## Verification setup -The verification process is controlled by `verify.py`. +The verification process is controlled by `verify.py`. This file contains the grading logic that inspects the student's work and returns a result with a status and feedback comments. {: .reference } @@ -120,9 +120,9 @@ Some examples of verifications: 2. For any remote behavior to verify, provide a mock to substitute the behavior in unit tests. 3. Prefer raising `exercise.wrong_answer([...])` for incorrect submissions and reserve unexpected exceptions for genuine errors. -### Testing verification +### Testing verify logic -To test verification, Git-Mastery relies on [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate exercise states and write unit tests for the verification script's behavior. You do not need to simulate the entire flow, only the end states that matter for your verification script. +Use [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate possible student answers and verify that your grading logic correctly accepts valid attempts and flags expected mistakes. Refer to existing `test_verify.py` files to see examples of unit testing the verification script. diff --git a/docs/exercises/hands-on.md b/docs/exercises/hands-on.md index 8407d47..5126cae 100644 --- a/docs/exercises/hands-on.md +++ b/docs/exercises/hands-on.md @@ -83,7 +83,6 @@ def download(verbose: bool): """, ) add(["fruits.txt"], verbose) - run_command(["git", "add", "fruits.txt"], verbose) append_to_file("fruits.txt", "dragon fruits") ``` @@ -97,7 +96,7 @@ For more information about how Git-Mastery downloads a hands-on, refer to the [D {: .note } -> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. They provide simple wrappers around common functionality such as `exercise_utils.cli.run_command` to invoke commands and `exercise_utils.file.create_or_update_file` to create or update files. +> `exercises` comes with a set of utility functions in the `exercise_utils` module that are made available during the download flow. Prefer these abstractions over raw CLI calls — for example, use `exercise_utils.file.create_or_update_file` to create or update files, and `exercise_utils.git.commit` to commit changes. Fall back to `exercise_utils.cli.run_command` only when no suitable abstraction exists. > > For the full list of utility functions, refer to [Exercise utilities reference](/developers/docs/exercises/exercise-utils). diff --git a/docs/exercises/testing-patterns.md b/docs/exercises/testing-patterns.md index 6e79247..80e29d4 100644 --- a/docs/exercises/testing-patterns.md +++ b/docs/exercises/testing-patterns.md @@ -46,7 +46,7 @@ def test_base() -> None: Within `with loader.start() as (test, rs):` - `test.run()` executes the exercise's `verify(...)` -- `rs` is a `RepoSmith` helper rooted at the student's working repository +- `rs` is a [`RepoSmith`](/developers/docs/libraries/repo-smith) helper rooted at the student's working repository. It abstracts the underlying Git repository, letting you set up state with calls like `rs.git.commit(...)`, `rs.git.create_file(...)`, etc. ## Useful test options @@ -70,7 +70,7 @@ with loader.start(mock_answers={"What is Git?": "Version control"}) as (test, rs ### Include a remote repository -Use `include_remote_repo=True` when verification depends on remote behavior. +Use `include_remote_repo=True` when verification depends on remote behavior (eg. pushing to remote). ```python with loader.start(include_remote_repo=True) as (test, rs, rs_remote): From 602bf421c690450ac83ef6f781bf4f14c1aaa0e1 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:38:25 +0800 Subject: [PATCH 6/9] Enhance documentation and structure for Git-Mastery --- docs/app/configuration.md | 124 +++++------ docs/app/download-and-verify-flow.md | 75 ------- docs/app/e2e-testing-flow.md | 146 +++++++++++++ docs/app/how-to-add-a-command.md | 212 +++++++++++++++++++ docs/app/index.md | 34 +-- docs/app/progress-integration.md | 67 +++--- docs/app/publishing-flow.md | 120 +++++++++++ docs/exercises/index.md | 11 +- docs/getting-started/index.md | 1 + docs/getting-started/setup.md | 4 +- docs/libraries/git-autograder.md | 299 +++++++++++++++++++++++---- docs/libraries/index.md | 1 + docs/libraries/repo-smith.md | 272 ++++++++++++++++++------ 13 files changed, 1050 insertions(+), 316 deletions(-) delete mode 100644 docs/app/download-and-verify-flow.md create mode 100644 docs/app/e2e-testing-flow.md create mode 100644 docs/app/how-to-add-a-command.md create mode 100644 docs/app/publishing-flow.md diff --git a/docs/app/configuration.md b/docs/app/configuration.md index f04116a..018db8c 100644 --- a/docs/app/configuration.md +++ b/docs/app/configuration.md @@ -6,28 +6,23 @@ nav_order: 2 # Configuration reference -The app uses local configuration files to track Git-Mastery setup and exercise metadata. - -## Important files - -- `.gitmastery.json`: local Git-Mastery configuration -- `.gitmastery-exercise.json`: metadata for a downloaded exercise +The app uses two JSON files to manage state: one for the Git-Mastery workspace and one per downloaded exercise. ## `.gitmastery.json` -This file lives at the Git-Mastery root created by `gitmastery setup`. - -### Main fields +Created by `gitmastery setup` in the Git-Mastery root directory. -- `progress_local`: whether local progress tracking is enabled -- `progress_remote`: whether progress sync to GitHub is enabled -- `exercises_source`: where the app should fetch exercises from +| Field | Type | Description | +| ------------------ | -------- | ------------------------------------------- | +| `progress_local` | `bool` | Whether local progress tracking is enabled | +| `progress_remote` | `bool` | Whether progress is synced to a GitHub fork | +| `exercises_source` | `object` | Where the app fetches exercises from | ### `exercises_source` -The app supports both remote and local exercise sources. +Two source types are supported: -Remote source shape: +**Remote** (default): ```json { @@ -38,7 +33,7 @@ Remote source shape: } ``` -Local source shape: +**Local** (for co-developing `app` and `exercises`): ```json { @@ -47,72 +42,51 @@ Local source shape: } ``` -When `type` is `local`, the app copies the local exercises repository into a temporary directory and reads exercises from there. This is useful when developing `app` and `exercises` together. - -### Local co-development example - -If you are working on `app` and want it to use a local checkout of `exercises`, update `.gitmastery.json` like this: - -```json -{ - "progress_local": true, - "progress_remote": false, - "exercises_source": { - "type": "local", - "repo_path": "/Users/you/dev/exercises" - } -} -``` - -This lets `gitmastery download` and `gitmastery verify` read exercise content from your local `exercises` clone instead of GitHub. - -### Remote source override example +With `type: local`, the app copies the local exercises directory into a temp directory at runtime. This means changes you make in your local `exercises/` clone are picked up immediately without needing to push to GitHub — useful when developing `app` and `exercises` together. -You can also point the app at a fork or branch of `exercises`: +To point at a fork or a feature branch, change `username` or `branch` in the remote config. -```json -{ - "progress_local": true, - "progress_remote": false, - "exercises_source": { - "type": "remote", - "username": "", - "repository": "exercises", - "branch": "feature/my-branch" - } -} -``` +--- ## `.gitmastery-exercise.json` -This file lives in each downloaded exercise root. - -### Main fields +Created per exercise by `gitmastery download`. Lives in the exercise root. -- `exercise_name` -- `tags` -- `requires_git` -- `requires_github` -- `base_files` -- `exercise_repo` -- `downloaded_at` +| Field | Type | Description | +| ----------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------- | +| `exercise_name` | `string` | Exercise identifier used for progress tracking | +| `tags` | `string[]` | Used for indexing on the exercise directory | +| `requires_git` | `bool` | If true, app checks Git installation and `user.name`/`user.email` before download | +| `requires_github` | `bool` | If true, app checks GitHub CLI authentication before download | +| `base_files` | `object` | Map of destination filename to source path in `res/`; these files are copied to the exercise root alongside `README.md` | +| `exercise_repo` | `object` | Controls what working repository the student receives | +| `downloaded_at` | `string` | ISO timestamp of when the exercise was downloaded | ### `exercise_repo` -The app currently understands these repository modes: - -- `local` -- `remote` -- `ignore` -- `local-ignore` - -These fields control whether the app creates a local repository, clones a remote repository, skips repository creation, or creates a folder without initializing Git. - -For remote exercises, the app may also read `fork_all_branches` when creating a student fork. - -## How the app uses these files - -- `setup` writes `.gitmastery.json` -- `download` reads the Git-Mastery root config, then writes or updates `.gitmastery-exercise.json` -- `verify` reads the exercise config to determine how the exercise should be graded -- `progress` reads the Git-Mastery root config to determine whether local or remote sync is enabled +| Field | Type | Description | +| ------------------- | -------- | ------------------------------------------------------------ | +| `repo_type` | `string` | One of `local`, `remote`, `ignore`, `local-ignore` | +| `repo_name` | `string` | Name of the subfolder created for the student | +| `init` | `bool` | Whether to run `git init` (used with `local`) | +| `create_fork` | `bool` | Whether to fork the remote repo to the student's account | +| `fork_all_branches` | `bool` | Whether all branches are included in the fork (remote only) | +| `repo_title` | `string` | Name of the GitHub repository to clone or fork (remote only) | + +### `repo_type` values + +| Value | Behaviour | +| -------------- | --------------------------------------------------------------- | +| `local` | Creates a local folder, optionally runs `git init` | +| `remote` | Clones or fork-and-clones a GitHub repository | +| `ignore` | No repository created; `exercise.repo` is a null wrapper | +| `local-ignore` | Creates a folder without `git init`; student runs it themselves | + +## How commands use these files + +| Command | Reads | Writes | +| ---------- | --------------------------- | ----------------------------------- | +| `setup` | — | `.gitmastery.json` | +| `download` | `.gitmastery.json` | `.gitmastery-exercise.json` | +| `verify` | `.gitmastery-exercise.json` | `progress/progress.json` | +| `progress` | `.gitmastery.json` | `.gitmastery.json` (on sync toggle) | diff --git a/docs/app/download-and-verify-flow.md b/docs/app/download-and-verify-flow.md deleted file mode 100644 index ee229ab..0000000 --- a/docs/app/download-and-verify-flow.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Download and verification flow -parent: App -nav_order: 3 ---- - -# Download and verification flow - -This page explains how the `gitmastery` CLI coordinates exercise content from `exercises` with runtime behavior in `app`. - -## Download flow - -When a user runs `gitmastery download `, the app first determines whether `` is a hands-on or an exercise. - -### Hands-ons - -For hands-ons, the app: - -1. checks that the matching `hands_on/*.py` file exists in the exercises source -2. creates a fresh local folder -3. loads the hands-on file as a Python namespace -4. checks Git and GitHub prerequisites if `__requires_git__` or `__requires_github__` are set -5. executes `download(...)` - -The hands-on script receives a `repo-smith` helper object as `rs`, which is useful for file and repository setup without needing a fully managed student repository. - -### Exercises - -For exercises, the app: - -1. checks that `.gitmastery-exercise.json` exists in the exercises source -2. creates a fresh exercise root directory -3. downloads the base files `.gitmastery-exercise.json` and `README.md` -4. loads the exercise config locally -5. checks Git and GitHub prerequisites when required -6. downloads any configured `base_files` -7. sets up the working repository or working folder according to `exercise_repo.repo_type` -8. loads `download.py` and executes `setup(...)` - -## Working repository modes - -The `exercise_repo.repo_type` field changes how download behaves: - -- `local`: create a local working folder, optionally initialize Git, then run `setup(...)` -- `remote`: clone a GitHub repository or fork-and-clone it, then run `setup(...)` -- `ignore`: skip managed repository creation and leave the student at the exercise root -- `local-ignore`: create a working folder but do not run `git init` - -## Verify flow - -When a user runs `gitmastery verify`, the app: - -1. confirms that the current directory belongs to a downloaded exercise -2. loads the local exercise config -3. fetches `verify.py` from the configured exercises source -4. constructs `GitAutograderExercise` -5. executes `verify(exercise)` -6. converts wrong-answer and invalid-state exceptions into structured output -7. prints the result to the terminal -8. updates local progress and optionally remote progress - -## Where code lives - -- `app/app/commands/download.py`: download orchestration -- `app/app/commands/verify.py`: verify orchestration and progress submission -- `app/app/utils/gitmastery.py`: exercises source loading and dynamic namespace execution -- `exercises//download.py`: exercise-specific setup logic -- `exercises//verify.py`: exercise-specific grading logic - -## Why this split exists - -- `app` owns runtime behavior, safety checks, and student-facing orchestration -- `exercises` owns content and exercise-specific logic -- `git-autograder` owns reusable verification abstractions -- `repo-smith` supports deterministic repository setup for tests and downloads diff --git a/docs/app/e2e-testing-flow.md b/docs/app/e2e-testing-flow.md new file mode 100644 index 0000000..3b5b8fc --- /dev/null +++ b/docs/app/e2e-testing-flow.md @@ -0,0 +1,146 @@ +--- +title: E2E testing flow +parent: App +nav_order: 4 +--- + +# E2E testing flow + +The `app` repository has an end-to-end test suite that runs the compiled `gitmastery` binary against real CLI commands. Tests assert on exit codes, stdout content, and side-effects such as file creation. + +## Why E2E tests + +Unit tests cover individual functions. E2E tests cover the full user-facing CLI path: argument parsing, command dispatch, file I/O, and cross-platform packaging behavior. Regressions caught only at this level include platform-specific path handling, binary encoding, and REPL dispatch. + +## Test structure + +``` +tests/e2e/ +├── conftest.py # shared pytest fixtures +├── constants.py # constants for test configuration +├── runner.py # executes the built binary and returns RunResult +├── result.py # helpers for assertions on exit code and stdout +├── utils.py # helper utility functions +└── commands/ # tests for commands + └── test_*.py +``` + +## Key components + +### `BinaryRunner` + +`BinaryRunner` wraps `subprocess.run` against the built binary. It is instantiated once per test session from the `GITMASTERY_BINARY` environment variable, falling back to `dist/gitmastery` (or `dist/gitmastery.exe` on Windows) if the variable is not set. + +```python +runner.run(["check", "git"]) +runner.run(["setup"], cwd=work_dir, stdin_text="\n") +runner.run(["progress", "sync", "off"], cwd=gitmastery_root, stdin_text="y\n") +``` + +### `RunResult` + +Every `runner.run(...)` call returns a `RunResult` with chainable assertion helpers: + +| Method | Description | +| --------------------------------- | ----------------------------------------- | +| `.assert_success()` | Assert exit code is 0 | +| `.assert_stdout_contains(text)` | Assert stdout contains an exact substring | +| `.assert_stdout_matches(pattern)` | Assert stdout matches a regex pattern | + +```python +res = runner.run(["version"]) +res.assert_success() +res.assert_stdout_contains("Git-Mastery app is") +res.assert_stdout_matches(r"v\d+\.\d+\.\d+") +``` + +### Fixtures in `conftest.py` + +| Fixture | Scope | Description | +| ------------------------- | -------- | ---------------------------------------------------------------- | +| `runner` | session | Single `BinaryRunner` shared across all tests | +| `gitmastery_root` | session | Runs `gitmastery setup`, yields the root path, cleans up on exit | +| `setup_gitmastery_root` | function | Same as above but for `test_setup.py` only | +| `downloaded_exercise_dir` | session | Downloads exercise once, returns its path | +| `downloaded_hands_on_dir` | session | Downloads hands-on once, returns its path | +| `verified_exercise_dir` | session | Runs `verify` on the downloaded exercise, returns its path | + +Session-scoped fixtures run once for the entire test run. This keeps the suite fast by reusing the same workspace across related tests. + +## Running E2E tests locally + +The tests run against the built binary, not the Python source directly. You must build first: + +```bash +# Build the binary +uv run pyinstaller --onefile main.py --name gitmastery + +# Point tests at the binary (Unix) +export GITMASTERY_BINARY="$PWD/dist/gitmastery" + +# Point tests at the binary (Windows, PowerShell) +$env:GITMASTERY_BINARY = "$PWD\dist\gitmastery.exe" + +# Run the suite +uv run pytest tests/e2e/ -v +``` + +{: .note } +E2E tests interact with GitHub (via `GH_TOKEN`) to download exercises and test progress sync. Ensure `gh auth status` succeeds and the `delete_repo` scope is granted before running locally. + +```bash +gh auth refresh -s delete_repo +``` + +## CI workflows + +Two GitHub Actions workflows run the E2E suite: + +### `test.yml` — runs on every push to `main` + +Steps: + +1. Validate `GH_PAT` is present +2. Check out source +3. Install dependencies with `uv sync` +4. Build binary with `uv run pyinstaller --onefile main.py --name gitmastery` +5. Set `GITMASTERY_BINARY` path (platform-aware) +6. Configure Git user (`github-actions[bot]`) +7. Run `uv run pytest tests/e2e/ -v` with `GH_TOKEN=${{ secrets.GH_PAT }}` + +### `test-pr.yml` — runs on pull requests + +This workflow uses `pull_request_target` so it can access repository secrets. The `e2e-test` environment gate means a maintainer must approve the workflow run before secrets are available. This prevents untrusted PRs from accessing secrets while still allowing E2E tests to run in PRs from forks. + +{: .note } + +> Maintainers must do due diligence on PRs before approving the workflow run to ensure they do not contain malicious code that could access secrets. Look for unexpected changes to test files or the addition of new test files that could exfiltrate secrets. If in doubt, request changes and ask the contributor to provide evidence of what the test is doing and why it needs secrets access. + +Steps are identical to `test.yml` except for the checkout step. + +## When to add or update E2E tests + +| Change | Action | +| ---------------------- | --------------------------------------------------- | +| New top-level command | Add `tests/e2e/commands/test_.py` | +| New subcommand or flag | Add a test case in the relevant test file | +| Changed stdout message | Update `assert_stdout_contains` strings | +| Changed exit behavior | Update `assert_success()` or add failure assertions | +| Changed REPL behavior | Update `test_repl.py` | + +Prefer one test per distinct behavior. Keep assertions focused on user-visible output rather than internal state where possible. + +## Writing a new E2E test + +```python +from ..runner import BinaryRunner + + +def test_my_command(runner: BinaryRunner, gitmastery_root: Path) -> None: + """my-command does X.""" + res = runner.run(["my-command"], cwd=gitmastery_root) + res.assert_success() + res.assert_stdout_contains("Expected output text") +``` + +Use the `gitmastery_root` fixture for commands that require a setup workspace. Use a fresh `setup_gitmastery_root` fixture only when the test must inspect the workspace in its initial pristine state. diff --git a/docs/app/how-to-add-a-command.md b/docs/app/how-to-add-a-command.md new file mode 100644 index 0000000..0df72c9 --- /dev/null +++ b/docs/app/how-to-add-a-command.md @@ -0,0 +1,212 @@ +--- +title: How to add a command +parent: App +nav_order: 1 +--- + +# How to add a command + +This guide walks through adding a new top-level command to the `gitmastery` CLI. The CLI uses [Click](https://click.palletsprojects.com/), a Python library for building command-line interfaces. + +We'll use a simple `greet` command as an example throughout. + +--- + +## 1. Create the command file + +Create a new file under `app/app/commands/`: + +``` +app/app/commands/greet.py +``` + +Every command is a Python function decorated with `@click.command()`. Use the shared output helpers from `app.utils.click` instead of `print()`. + +```python +# app/app/commands/greet.py +import click + +from app.utils.click import info, success + + +@click.command() +@click.argument("name") +def greet(name: str) -> None: + """ + Greets the user by name. + """ + info(f"Hello, {name}!") + success("Greeting complete.") +``` + +### Output helpers + +| Helper | When to use | +|---|---| +| `info(msg)` | Normal status messages | +| `success(msg)` | Command completed successfully | +| `warn(msg)` | Non-fatal issues or warnings | +| `error(msg)` | Fatal issues — exits immediately | + +--- + +## 2. Register the command in `cli.py` + +Open `app/app/cli.py` and add two things: + +1. Import your command at the top. +2. Add it to the `commands` list in `start()`. + +```python +# app/app/cli.py + +# 1. Import +from app.commands.greet import greet # add this + +# 2. Register +def start() -> None: + commands = [check, download, greet, progress, setup, verify, version] # add greet + for command in commands: + cli.add_command(command) + cli(obj={}) +``` + +--- + +## 3. Verify it works locally + +Run the CLI directly from source to confirm the command appears and works: + +```bash +uv run python main.py greet Alice +``` + +Expected output: + +``` + INFO Hello, Alice! + SUCCESS Greeting complete. +``` + +Also check it shows in `--help`: + +```bash +uv run python main.py --help +``` + +--- + +## 4. Add an E2E test + +Every new command needs an E2E test. Create a new file under `tests/e2e/commands/`: + +``` +tests/e2e/commands/test_greet.py +``` + +```python +# tests/e2e/commands/test_greet.py +from pathlib import Path + +from ..runner import BinaryRunner + + +def test_greet(runner: BinaryRunner, gitmastery_root: Path) -> None: + """greet prints the expected message.""" + res = runner.run(["greet", "Alice"], cwd=gitmastery_root) + res.assert_success() + res.assert_stdout_contains("Hello, Alice!") +``` + +### `RunResult` assertion methods + +| Method | Description | +|---|---| +| `.assert_success()` | Asserts exit code is 0 | +| `.assert_stdout_contains(text)` | Asserts stdout contains an exact substring | +| `.assert_stdout_matches(pattern)` | Asserts stdout matches a regex pattern | + +--- + +## 5. Run the E2E tests + +Build the binary first, then run the suite: + +```bash +# Build +uv run pyinstaller --onefile main.py --name gitmastery + +# Set binary path (Unix) +export GITMASTERY_BINARY="$PWD/dist/gitmastery" + +# Set binary path (Windows, PowerShell) +$env:GITMASTERY_BINARY = "$PWD\dist\gitmastery.exe" + +# Run only your new test +uv run pytest tests/e2e/commands/test_greet.py -v + +# Run the full E2E suite +uv run pytest tests/e2e/ -v +``` + +All tests should pass before opening a pull request. + +--- + +## Command group (optional) + +If you want to add a command that has subcommands (like `gitmastery progress show`), use `@click.group()` and register subcommands with `.add_command()`. + +```python +# app/app/commands/greet.py +import click + +from app.utils.click import info + + +@click.group() +def greet() -> None: + """Greet people in different ways.""" + pass + + +@click.command() +@click.argument("name") +def hello(name: str) -> None: + """Say hello.""" + info(f"Hello, {name}!") + + +@click.command() +@click.argument("name") +def goodbye(name: str) -> None: + """Say goodbye.""" + info(f"Goodbye, {name}!") + + +greet.add_command(hello) +greet.add_command(goodbye) +``` + +Register `greet` the same way in `cli.py`. The user then runs: + +```bash +gitmastery greet hello Alice +gitmastery greet goodbye Alice +``` + +--- + +## Checklist + +Before opening a pull request for a new command: + +- [ ] Command file created under `app/app/commands/` +- [ ] Command registered in `app/app/cli.py` +- [ ] Command works locally with `uv run python main.py` +- [ ] E2E test file created under `tests/e2e/commands/` +- [ ] E2E tests pass with the built binary + +{: .reference } + +See [E2E testing flow](/developers/docs/app/e2e-testing-flow) for a full explanation of the test infrastructure and how to write more complex tests. diff --git a/docs/app/index.md b/docs/app/index.md index 5c7226c..867439d 100644 --- a/docs/app/index.md +++ b/docs/app/index.md @@ -2,44 +2,32 @@ title: App nav_order: 5 has_children: true +has_toc: false --- # App The [`app`](https://github.com/git-mastery/app) repository contains the `gitmastery` CLI. -## Key Features +## Main responsibilities - Set up a local Git-Mastery workspace - Fetch hands-ons and exercises from the configured exercises source - Execute verification logic from the `exercises` repository - Maintain local and remote progress tracking -## Command Reference +## Suggested reading -Refer to [command reference](https://git-mastery.org/companion-app/index.html#git-mastery-app-commands) for the full list of commands, their descriptions, and usage examples. - -{: .warning-title } - -> Note -> -> Experimental command `gitmastery` to spawn a REPL instance with CLI runtime loaded. Not all commands may work as expected in the REPL, and behavior may differ from running the CLI directly.

-> The REPL accepts both Git-Mastery commands and shell commands: -> -> - Use `/verify` or `gitmastery verify` for Git-Mastery commands -> - Use `cd` to change directories inside the REPL -> - Any other command is executed through the local shell +1. [CLI command reference](/developers/docs/app/command-reference) +2. [Configuration reference](/developers/docs/app/configuration) +3. [Download and verification flow](/developers/docs/app/download-and-verify-flow) +4. [Progress tracking](/developers/docs/app/progress-integration) +5. [E2E testing flow](/developers/docs/app/e2e-testing-flow) +6. [Publishing flow](/developers/docs/app/publishing-flow) +7. [How to add a command](/developers/docs/app/how-to-add-a-command) ## Testing expectations `app` has an end-to-end test suite under `app/tests/e2e` that exercises the built CLI across setup, check, download, verify, progress, version, and REPL flows. -When you add a new command or change command behavior, update or add E2E tests for the user-visible behavior. Command-level unit tests are useful, but they do not replace the cross-platform CLI coverage provided by the E2E suite. - -Typical local flow: - -```bash -uv run pyinstaller --onefile main.py --name gitmastery -export GITMASTERY_BINARY="$PWD/dist/gitmastery" -uv run pytest tests/e2e/ -v -``` +When you add a new command or change command behavior, update or add E2E tests for the user-visible behavior. See [E2E testing flow](/developers/docs/app/e2e-testing-flow) for a full guide. diff --git a/docs/app/progress-integration.md b/docs/app/progress-integration.md index edd638c..0e26d67 100644 --- a/docs/app/progress-integration.md +++ b/docs/app/progress-integration.md @@ -1,52 +1,65 @@ --- title: Progress tracking parent: App -nav_order: 4 +nav_order: 3 --- # Progress tracking -The app is responsible for writing and syncing exercise progress based on verification results. - -Progress data is part of the wider Git-Mastery ecosystem and can be consumed by other repositories such as `progress-dashboard`. +The app records exercise progress locally after every `verify` run, and can optionally sync it to a GitHub-hosted progress repository. ## Local progress -`gitmastery setup` creates a `progress/` folder inside the Git-Mastery root. +`gitmastery setup` creates a `progress/` folder inside the Git-Mastery root with an empty `progress.json`. -After each `gitmastery verify` run, the app appends a new entry to `progress/progress.json` with: +After each `gitmastery verify`, the app appends a new entry: -- `exercise_name` -- `started_at` -- `completed_at` -- `comments` -- `status` +```json +{ + "exercise_name": "branch-bender", + "started_at": 1700000000.0, + "completed_at": 1700000010.0, + "comments": ["Great work!"], + "status": "Completed" +} +``` -The app keeps a history of attempts, but `gitmastery progress show` displays the latest known entry for each exercise. +The `status` field maps from `GitAutograderStatus`: -## Remote sync +| `GitAutograderStatus` | Written as | +| --------------------- | -------------- | +| `SUCCESSFUL` | `"Completed"` | +| `UNSUCCESSFUL` | `"Incomplete"` | +| `ERROR` | `"Error"` | -When a user runs `gitmastery progress sync on`, the app: +{: .note } +If the exercise already has a `Completed` entry, subsequent attempts are not recorded. -1. Checks Git and GitHub CLI prerequisites -2. Creates or reuses the user's fork of the progress repository -3. Replaces the local `progress/` folder with a clone of that fork -4. Merges local and remote progress entries -5. Pushes changes and opens a PR if needed +`gitmastery progress show` displays the latest entry per exercise. -When remote sync is enabled, later `verify` and `progress reset` runs also update the remote fork. +## Remote sync -## Turning sync off +```mermaid +flowchart TD + A([gitmastery progress sync on]) --> B[Check Git + GitHub CLI] + B --> C[Fork git-mastery/progress] + C --> D[Clone fork into progress/] + D --> E[Merge local + remote entries] + E --> F[Push to fork] + F --> G{PR exists?} + G -- No --> H[Open PR to git-mastery/progress] + G -- Yes --> I([Done]) + H --> I +``` -`gitmastery progress sync off` deletes the remote progress fork, switches `.gitmastery.json` back to local-only tracking, and recreates a plain local `progress/` folder containing the existing progress entries. +Once sync is on, every subsequent `verify` run pushes the new progress entry to the fork. `progress reset` also removes the exercise entry from the fork. -## Reset behavior +`gitmastery progress sync off` removes the fork from GitHub, switches `.gitmastery.json` back to local-only mode, and recreates a plain local `progress/` folder preserving existing entries. -`gitmastery progress reset` does two things for the current exercise: +## Reset -- recreates the exercise workspace -- removes saved progress entries for that exercise from `progress.json` +`gitmastery progress reset` removes the current exercise's entries from `progress.json` and recreates the exercise workspace. {: .reference } -See [Download and verification flow](/developers/docs/app/download-and-verify-flow) for the command orchestration that happens before progress is updated. +See [Download and verification flow](/developers/docs/app/download-and-verify-flow) for how progress is updated during the verify step. diff --git a/docs/app/publishing-flow.md b/docs/app/publishing-flow.md new file mode 100644 index 0000000..586ced8 --- /dev/null +++ b/docs/app/publishing-flow.md @@ -0,0 +1,120 @@ +--- +title: Publishing flow +parent: App +nav_order: 5 +--- + +# Publishing flow + +Releases are fully automated. Merging a labelled PR to `main` triggers a version bump, which in turn triggers a multi-platform build and publish. + +## How to trigger a release + +Label the PR with one of: + +| Label | Effect | +| ------------ | -------------------------------------------------- | +| `bump:major` | Bumps the major version (e.g. `v1.2.3` → `v2.0.0`) | +| `bump:minor` | Bumps the minor version (e.g. `v1.2.3` → `v1.3.0`) | +| `bump:patch` | Bumps the patch version (e.g. `v1.2.3` → `v1.2.4`) | + +When the labelled PR is merged to `main`, the pipeline runs automatically. + +{: .warning } +Do not push version tags manually. The automated pipeline manages tag creation. Manually pushed tags may trigger duplicate publish jobs. + +## Pipeline overview + +```mermaid +flowchart TD + A([PR merged to main with bump label]) --> B[bump-version.yml] + B --> C[Shared action creates version tag] + C --> D[publish.yml triggered] + D --> E[prepare job — get latest tag] + E --> F{should_publish?} + F -- No --> G([Stop]) + F -- Yes --> H[Build jobs run in parallel] + + H --> H1[linux-build\namd64 + arm64] + H --> H2[arch-build\namd64] + H --> H3[windows\namd64] + H --> H4[macos-build\namd64 + arm64] + + H1 --> I1[debian-build] + I1 --> I2[debian-publish-apt] + + H2 --> J1[arch-publish\nAUR] + + H3 --> K1[winget-publish] + + H4 --> L1[macos-publish\nHomebrew tap] + L1 --> L2[macos-test\nSmoke test via brew install] +``` + +## Workflow files + +| File | Purpose | +| ------------------------------------ | --------------------------------------------------------------------------------------------- | +| `.github/workflows/bump-version.yml` | Listens for closed PRs on `main`, delegates to the shared `git-mastery/actions` bump workflow | +| `.github/workflows/publish.yml` | Triggered after the bump tag is created; builds and publishes all platform targets | + +## Build and publish targets + +### Linux (amd64 and arm64) + +1. `linux-build`: builds the binary with PyInstaller on `ubuntu-latest` and `ubuntu-24.04-arm`, writes the version into `app/version.py`, and uploads both binaries to the GitHub Release. +2. `debian-build`: downloads the Linux binary artifact, packages it as a `.deb` file using `dpkg-buildpackage`, and uploads the `.deb` to the GitHub Release. +3. `debian-publish-apt`: pushes the `.deb` to the `gitmastery-apt-repo` via a reusable workflow so it is available via APT. + +### Arch Linux (amd64) + +1. `arch-build`: builds the binary inside an `archlinux:base-devel` Docker container, uploads it to the GitHub Release. +2. `arch-publish`: clones the AUR package repository via SSH, updates `PKGBUILD` and `.SRCINFO`, and force-pushes to publish the new version on the AUR. + +### Windows (amd64) + +1. `windows`: builds `gitmastery.exe` on `windows-latest`, uploads it to the GitHub Release. +2. `winget-publish`: submits the new version to WinGet via the `vedantmgoyal9/winget-releaser` action. + +### macOS (amd64 and arm64) + +1. `macos-build`: builds architecture-specific binaries (`gitmastery-amd64`, `gitmastery-arm64`) on `macos-15-intel` and `macos-latest`, computes SHA256 checksums, and uploads both to the GitHub Release. +2. `macos-publish`: clones the `git-mastery/homebrew-gitmastery` tap repository, writes a new `gitmastery.rb` formula with the correct download URLs and checksums, and pushes to the tap. +3. `macos-test`: runs a smoke test on both macOS architectures by installing via `brew tap git-mastery/gitmastery && brew install gitmastery && gitmastery --help`. + +## How the version is embedded + +During every build job, the version is written directly into `app/version.py` before PyInstaller runs: + +```bash +# Unix +echo "__version__ = \"$REF_NAME\"" > app/version.py + +# Windows (PowerShell) +'__version__ = "{0}"' -f $env:REF_NAME | Out-File app/version.py -Encoding utf8 +``` + +This means the version in the binary always matches the release tag and does not rely on runtime package metadata. + +## The `prepare` gate + +The `publish.yml` workflow starts with a `prepare` job that calls the shared `get-latest-tag` workflow. This job outputs `should_publish` and `ref_name`. All downstream build jobs check `if: needs.prepare.outputs.should_publish == 'true'` before running. + +This gate prevents accidental re-runs of publish jobs when the workflow is triggered manually (`workflow_dispatch`) without a new tag. + +## Secrets and environments + +| Secret | Used by | +| ----------------- | ------------------------------------------------------------- | +| `GITHUB_TOKEN` | Creating GitHub Releases (automatic, no configuration needed) | +| `ORG_PAT` | Updating the Homebrew tap and WinGet submission | +| `SSH_PRIVATE_KEY` | Pushing to the AUR repository | + +The `arch-publish` job runs in the `Main` environment, which gates secret access. + +## Contributor expectations + +- **Do not modify `app/version.py` manually** — it is overwritten during every release build. +- **Do not change publishing logic without review** — changes to `publish.yml` affect all distribution channels simultaneously. +- **Label PRs correctly** — missing or wrong bump labels mean no release is triggered after merge. +- **Packaging changes** (Debian control files, AUR PKGBUILD, Homebrew formula) should be tested locally where possible and reviewed carefully before merging. diff --git a/docs/exercises/index.md b/docs/exercises/index.md index eca466d..93fe631 100644 --- a/docs/exercises/index.md +++ b/docs/exercises/index.md @@ -2,6 +2,7 @@ title: Exercises nav_order: 4 has_children: true +has_toc: false --- # Exercises @@ -16,13 +17,3 @@ The [`exercises`](https://github.com/git-mastery/exercises) repository contains - How verification works - How to test `verify.py` reliably - Shared utilities used during exercise download - -## Suggested reading - -1. [How to add a hands-on](/developers/docs/exercises/hands-on) -2. [How to add an exercise](/developers/docs/exercises/exercise) -3. [Exercise format reference](/developers/docs/exercises/exercise-structure) -4. [Download flow](/developers/docs/exercises/download-workflow) -5. [Verification flow](/developers/docs/exercises/verification-workflow) -6. [Testing guide](/developers/docs/exercises/testing-patterns) -7. [Exercise utilities reference](/developers/docs/exercises/exercise-utils) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 631ca5d..264903e 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -2,6 +2,7 @@ title: Getting started nav_order: 3 has_children: true +has_toc: false --- # Getting started diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index a4c8664..1f90de3 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -35,7 +35,7 @@ Use the guidance on this page as the default when setting up repositories locall 4. Set up pre-commit hooks using LeftHook - {: .warning-title } + {: .note } > Note > @@ -72,7 +72,7 @@ Use the guidance on this page as the default when setting up repositories locall 4. Set up pre-commit hooks using LeftHook - {: .warning-title } + {: .note } > Note > diff --git a/docs/libraries/git-autograder.md b/docs/libraries/git-autograder.md index 4f66b09..cb5d739 100644 --- a/docs/libraries/git-autograder.md +++ b/docs/libraries/git-autograder.md @@ -6,15 +6,15 @@ nav_order: 2 # git-autograder reference -`git-autograder` is the verification library used by Git-Mastery exercise `verify.py` scripts. +`git-autograder` is the grading library that loads a Git-Mastery exercise attempt and turns repository or answer checks into structured verification results. -## Typical use in Git-Mastery +## Installation -- Load and inspect a student's exercise repository -- Produce structured grading comments and statuses -- Provide reusable helpers over raw Git access +```bash +pip install git-autograder +``` -## Basic example +## Basic structure of a `verify.py` ```python from git_autograder import ( @@ -23,64 +23,279 @@ from git_autograder import ( GitAutograderStatus, ) +SOME_ERROR = "You haven't done X yet." + def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: - return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) + main = exercise.repo.branches.branch("main") + + if not main.user_commits: + raise exercise.wrong_answer([SOME_ERROR]) + + return exercise.to_output(["Well done!"], GitAutograderStatus.SUCCESSFUL) ``` -## Core concepts +The app calls `verify(exercise)` directly. All exception handling and output formatting is done by the app. -### `GitAutograderExercise` +--- -This is the main entrypoint used by exercise verification scripts. +## `GitAutograderExercise` -It: +The main object passed to every `verify(exercise)` function. -- reads `.gitmastery-exercise.json` -- loads the student's working repository -- exposes `exercise_name`, `config`, and `repo` -- provides `to_output(...)` and `wrong_answer(...)` +| Attribute / Method | Description | +|---|---| +| `exercise.exercise_name` | Exercise identifier (from config) | +| `exercise.exercise_path` | Path to the exercise root directory | +| `exercise.config` | Parsed `.gitmastery-exercise.json` as `ExerciseConfig` | +| `exercise.repo` | `GitAutograderRepo` (or `NullGitAutograderRepo` for `ignore` exercises) | +| `exercise.answers` | Parsed `answers.txt` as `GitAutograderAnswers` | +| `exercise.to_output(comments, status)` | Build the return value | +| `exercise.wrong_answer(comments)` | Raise a grading failure | +| `exercise.read_config(key)` | Read a key from `.gitmastery-exercise.json` | +| `exercise.write_config(key, value)` | Write a key to `.gitmastery-exercise.json` | -### `GitAutograderOutput` +### Exception types -Verification scripts return a `GitAutograderOutput` containing: +| Exception | When to use | +|---|---| +| `GitAutograderWrongAnswerException` | The student's attempt is incorrect → `UNSUCCESSFUL` | +| `GitAutograderInvalidStateException` | The exercise is in an invalid state → `ERROR` | -- `status` -- `started_at` -- `completed_at` -- `comments` -- `exercise_name` +Use `raise exercise.wrong_answer([...])` for grading failures. Reserve bare exceptions for unexpected errors only. -### `GitAutograderStatus` +--- -The current statuses are: +## `exercise.repo` — repository helpers -- `SUCCESSFUL` -- `UNSUCCESSFUL` -- `ERROR` +`exercise.repo` is a `GitAutograderRepo` for most exercises. For `ignore` and `local-ignore` exercises it is a `NullGitAutograderRepo` that raises on any Git access. -## Repository modes +### `exercise.repo.branches` — `BranchHelper` -For normal exercises, `exercise.repo` wraps a real Git repository. +```python +branch = exercise.repo.branches.branch("main") # raises if missing +branch = exercise.repo.branches.branch_or_none("main") # returns None if missing +exists = exercise.repo.branches.has_branch("feature/login") +``` -For `repo_type: ignore`, `exercise.repo` becomes a null repository wrapper. Accessing Git-specific properties on it raises an error, which helps catch verification code that assumes a repository exists when it should not. +#### `GitAutograderBranch` -## Answers support +| Property / Method | Description | +|---|---| +| `branch.name` | Branch name | +| `branch.commits` | All commits (newest first) | +| `branch.user_commits` | Commits after the start tag (student's work) | +| `branch.latest_commit` | Most recent commit | +| `branch.latest_user_commit` | Most recent student commit | +| `branch.start_commit` | The Git-Mastery start tag commit | +| `branch.reflog` | List of `GitAutograderReflogEntry` | +| `branch.has_non_empty_commits()` | True if any student commit changed files | +| `branch.has_edited_file(path)` | True if the file was modified since the start tag | +| `branch.has_added_file(path)` | True if the file was added since the start tag | +| `branch.checkout()` | Checkout this branch | -`GitAutograderExercise.answers` parses `answers.txt` from the exercise root on demand. +Example — check that the student committed on `main`: -It supports question-and-answer style validation and can accumulate validation rules before calling `validate()`. +```python +main = exercise.repo.branches.branch("main") +if not main.user_commits: + raise exercise.wrong_answer(["You have no commits on main yet."]) +``` -## Typical verification flow +### `exercise.repo.commits` — `CommitHelper` -1. Construct `GitAutograderExercise` -2. Inspect repository state through `exercise.repo` -3. Optionally inspect `exercise.answers` -4. Raise `exercise.wrong_answer([...])` for incorrect submissions -5. Return `exercise.to_output([...], GitAutograderStatus.SUCCESSFUL)` on success +```python +commit = exercise.repo.commits.commit("HEAD") +commit = exercise.repo.commits.commit_or_none("abc1234") +``` -{: .reference } +#### `GitAutograderCommit` + +| Property / Method | Description | +|---|---| +| `commit.hexsha` | Full commit SHA | +| `commit.stats` | GitPython `Stats` object (files changed) | +| `commit.parents` | List of `GitAutograderCommit` | +| `commit.branches` | Branch names that contain this commit | +| `commit.is_child(parent)` | True if this commit descends from `parent` | +| `commit.file_change_type(file)` | `"A"`, `"M"`, `"D"`, or `None` | +| `commit.file(path)` | Context manager yielding the file contents at this commit | +| `commit.checkout()` | Detach HEAD to this commit | + +### `exercise.repo.remotes` — `RemoteHelper` + +```python +remote = exercise.repo.remotes.remote("origin") # raises if missing +remote = exercise.repo.remotes.remote_or_none("origin") # returns None +exists = exercise.repo.remotes.has_remote("origin") +``` + +#### `GitAutograderRemote` + +Wraps a GitPython `Remote`. + +| Method | Description | +|---|---| +| `remote.remote` | The underlying GitPython `Remote` object | +| `remote.is_for_repo(owner, repo_name)` | True if the remote URL points to `owner/repo_name` on GitHub (supports both HTTPS and SSH URLs) | +| `remote.track_branches(branches)` | Check out remote-tracking branches locally | + +Example — verify the remote points to the right repository: + +```python +origin = exercise.repo.remotes.remote("origin") +if not origin.is_for_repo("git-mastery", "exercises"): + raise exercise.wrong_answer(["Your remote 'origin' does not point to the correct repository."]) +``` + +### `exercise.repo.files` — `FileHelper` + +```python +# Open a file if it exists +with exercise.repo.files.file_or_none("notes.txt") as f: + content = f.read() if f else None + +# Open a file (raises if missing) +with exercise.repo.files.file("notes.txt") as f: + content = f.read() + +# List untracked files +untracked = exercise.repo.files.untracked_files() +``` + +### Raw GitPython access + +If no helper covers your use case, access the underlying GitPython repo directly: + +```python +exercise.repo.repo # GitPython Repo object +``` + +--- + +## `exercise.answers` — answer file support + +For exercises where students fill in an `answers.txt` file. + +### `answers.txt` format + +``` +Q: What does git add do? +A: Stages changes for the next commit + +Q: What does git commit do? +A: Records staged changes to the repository +``` + +### Usage -See [How to add an exercise](/developers/docs/exercises/exercise) and [Verification flow](/developers/docs/exercises/verification-workflow) for how `git-autograder` is used inside the main Git-Mastery exercise flow. +`answers.question(q)` and `answers.question_or_none(q)` return a `GitAutograderAnswersRecord` with two attributes: + +| Attribute | Description | +|---|---| +| `record.question` | The question string | +| `record.answer` | The student's answer string | + +```python +answers = exercise.answers + +# Get a specific answer (raises GitAutograderInvalidStateException if missing) +record = answers.question("What does git add do?") +print(record.answer) + +# Safe access — returns None if the question is not present +record = answers.question_or_none("What does git add do?") +if record is None: + raise exercise.wrong_answer(["Missing answer for 'What does git add do?'"]) + +# Validate answers with rules, then call validate() to apply them all at once +from git_autograder.answers.rules.not_empty_rule import NotEmptyRule +from git_autograder.answers.rules.has_exact_value_rule import HasExactValueRule +from git_autograder.answers.rules.contains_value_rule import ContainsValueRule + +answers.add_validation("What does git add do?", NotEmptyRule()) +answers.add_validation("Name a git command", HasExactValueRule("git commit")) +answers.validate() # raises GitAutograderWrongAnswerException if any rule fails +``` + +### Available answer rules + +| Rule | Description | +|---|---| +| `NotEmptyRule` | Answer must not be blank | +| `HasExactValueRule(value)` | Answer must equal `value` exactly (case-insensitive) | +| `ContainsValueRule(value)` | Answer must contain `value` (case-insensitive) | +| `ContainsListRule(values)` | Answer must contain all values in the list | +| `HasExactListRule(values)` | Answer must match all values in the list exactly | + +All rules are in `git_autograder.answers.rules`. + +### In tests + +Mock `answers.txt` content via `GitAutograderTestLoader`: + +```python +with loader.start(mock_answers={"What does git add do?": "Stages changes"}) as (test, rs): + output = test.run() + assert_output(output, GitAutograderStatus.SUCCESSFUL) +``` + +--- + +## `GitAutograderStatus` + +| Value | Meaning | +|---|---| +| `SUCCESSFUL` | Student completed the exercise correctly | +| `UNSUCCESSFUL` | Student's attempt is incorrect or incomplete | +| `ERROR` | Exercise is in an invalid or unexpected state | + +--- + +## `GitAutograderOutput` + +Built by `exercise.to_output(comments, status)`. Contains: + +| Field | Type | Description | +|---|---|---| +| `exercise_name` | `str` | Exercise identifier | +| `started_at` | `datetime` | When `GitAutograderExercise` was constructed | +| `completed_at` | `datetime` | When `to_output` was called | +| `comments` | `List[str]` | Feedback shown to the student | +| `status` | `GitAutograderStatus` | Final result | + +--- + +## Full example — branch check + +```python +from git_autograder import ( + GitAutograderExercise, + GitAutograderOutput, + GitAutograderStatus, +) + +NOT_ON_MAIN = "You aren't on the main branch. Run 'git checkout main'." +NO_COMMITS = "You haven't committed your changes yet." +SUCCESS = "Great work! Your changes are committed to main." + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + try: + active = exercise.repo.repo.active_branch.name + except TypeError: + raise exercise.wrong_answer(["You are in a detached HEAD state."]) + + if active != "main": + raise exercise.wrong_answer([NOT_ON_MAIN]) + + main = exercise.repo.branches.branch("main") + if not main.user_commits: + raise exercise.wrong_answer([NO_COMMITS]) + + return exercise.to_output([SUCCESS], GitAutograderStatus.SUCCESSFUL) +``` + +{: .reference } -For implementation details, refer to the [`git-autograder` repository](https://github.com/git-mastery/git-autograder). +See [How to add an exercise](/developers/docs/exercises/exercise) and [Verification flow](/developers/docs/exercises/verification-workflow) for how this fits into the full exercise lifecycle. diff --git a/docs/libraries/index.md b/docs/libraries/index.md index b6b17c3..ef1d549 100644 --- a/docs/libraries/index.md +++ b/docs/libraries/index.md @@ -2,6 +2,7 @@ title: Shared libraries nav_order: 6 has_children: true +has_toc: false --- # Shared libraries diff --git a/docs/libraries/repo-smith.md b/docs/libraries/repo-smith.md index 0a488dc..9e201af 100644 --- a/docs/libraries/repo-smith.md +++ b/docs/libraries/repo-smith.md @@ -6,101 +6,249 @@ nav_order: 1 # repo-smith reference -`repo-smith` is a YAML-based library for initializing Git repositories for unit testing. +`repo-smith` is a test-support library that creates and mutates local and remote Git repository states through helper objects, so exercise verification scenarios can be set up deterministically in tests. -## Typical use in Git-Mastery +{: .warning } -- Build repository states for `test_verify.py` -- Simulate specific Git histories and repository layouts -- Keep verification tests readable through declarative setup +The YAML-based spec format from repo-smith v1 is deprecated. All Git-Mastery exercises use the Python v2 API directly through `create_repo_smith` and the `RepoSmith` helper objects. -## Example usage +## Installation + +```bash +pip install -U repo-smith +``` + +## Core API + +### `create_repo_smith` + +The main entrypoint. Returns a context manager that yields a `RepoSmith` instance. ```python -from repo_smith.initialize_repo import initialize_repo +from repo_smith.repo_smith import create_repo_smith + +with create_repo_smith(verbose=False) as rs: + rs.git.commit(message="Initial commit", allow_empty=True) +``` + +#### Options + +| Option | Type | Description | +|---|---|---| +| `verbose` | `bool` | Print commands as they run | +| `existing_path` | `str` | Use an existing directory instead of creating a temp one | +| `clone_from` | `str` | Clone from a URL before starting | +| `null_repo` | `bool` | Create a `RepoSmith` with no underlying repository | +### `RepoSmith` + +Yielded by `create_repo_smith`. Provides three built-in helpers: + +| Attribute | Description | +|---|---| +| `rs.git` | Git operations | +| `rs.files` | File system operations | +| `rs.gh` | GitHub CLI operations | +| `rs.repo` | Underlying `GitPython` `Repo` object | + +Custom helpers can be registered with `rs.add_helper(MyHelper)` and accessed with `rs.helper(MyHelper)`. + +--- + +## `rs.git` — Git operations + +### Commit and stage + +```python +rs.git.add("notes.txt") +rs.git.add(["a.txt", "b.txt"]) +rs.git.add(all=True) -def test_dummy(): - repo_initializer = initialize_repo("tests/specs/basic_spec.yml") - with repo_initializer.initialize() as repo: - print(repo) +rs.git.commit(message="Initial commit", allow_empty=True) +rs.git.commit(message="Add file") ``` -## Why Git-Mastery uses it +### Branches -`repo-smith` keeps exercise verification tests declarative. Instead of building test repositories imperatively in Python, contributors can describe the repository history and filesystem state in YAML. +```python +rs.git.checkout("feature/login", branch=True) # create and switch +rs.git.checkout("main") # switch to existing branch +rs.git.branch("feature/payments") # create without switching +rs.git.branch("new-name", old_branch="old-name", move=True) # rename +rs.git.branch("old-branch", delete=True) # delete +``` -## Spec structure +### Merge -A spec contains: +```python +rs.git.merge("feature/login", no_ff=True) +rs.git.merge("feature/dashboard") +``` -- optional `name` -- optional `description` -- `initialization` - - optional `clone-from` - - ordered `steps` +### Tags -All steps run sequentially from top to bottom. +```python +rs.git.tag("v1.0") +rs.git.tag("v1.0", "abc1234") # tag a specific commit +``` -## Supported step types +### Reset and revert -Current step types include: +```python +rs.git.reset("HEAD~1", hard=True) +rs.git.revert("HEAD") +``` -- `commit` -- `add` -- `tag` -- `new-file` -- `edit-file` -- `delete-file` -- `append-file` -- `bash` -- `branch` -- `branch-rename` -- `branch-delete` -- `checkout` -- `remote` -- `reset` -- `revert` -- `merge` -- `fetch` +### Remotes and push/fetch -## Features worth knowing +```python +rs.git.remote_add("origin", "https://github.com/example/repo.git") +rs.git.remote_rename("origin", "upstream") +rs.git.remote_remove("origin") + +rs.git.push("origin", "main", set_upstream=True) +rs.git.push("origin", ":old-branch") # the colon prefix deletes the remote branch +rs.git.fetch("origin") +rs.git.fetch(all=True) +``` + +### Restore -### `clone-from` +```python +rs.git.restore("notes.txt") +rs.git.restore("notes.txt", staged=True) +``` -Use `initialization.clone-from` when the test should begin from an existing repository instead of an empty freshly initialized one. +--- -### Step IDs and hooks +## `rs.files` — File operations -When a step has an `id`, tests can attach pre-hooks and post-hooks around that specific step. +```python +rs.files.create_or_update("notes.txt", "hello world") # create or overwrite +rs.files.create_or_update("empty.txt") # create empty file +rs.files.append("notes.txt", "\nmore content") +rs.files.delete("notes.txt") +rs.files.delete("some-dir/") +rs.files.mkdir("subdir") +rs.files.cd("subdir") +``` -This is useful when you need to assert intermediate repository state, not just the final result. +--- + +## `rs.gh` — GitHub CLI operations + +Used for exercises that test remote repository behavior. + +```python +rs.gh.repo_create(owner=None, repo="my-repo", public=True) +rs.gh.repo_clone(owner="git-mastery", repo="exercises", directory="local-dir") +rs.gh.repo_fork(owner="git-mastery", repo="exercises", clone=True) +rs.gh.repo_delete(owner=None, repo="my-repo") +rs.gh.repo_view(owner="git-mastery", repo="exercises") +``` + +--- + +## Custom helpers + +Custom helpers extend `RepoSmith` with domain-specific operations. Subclass `Helper` and register it before use: ```python -from repo_smith.initialize_repo import initialize_repo +from git import Repo +from repo_smith.helpers.helper import Helper -def test_with_hook() -> None: - repo_initializer = initialize_repo("tests/specs/hooks.yml") +class GitMasteryHelper(Helper): + def __init__(self, repo: Repo, verbose: bool) -> None: + super().__init__(repo, verbose) - def before_commit(repo) -> None: - print(repo.working_tree_dir) + def create_start_tag(self) -> None: + """Creates the git-mastery-start- tag required by git-autograder.""" + all_commits = list(self.repo.iter_commits()) + first_commit = list(reversed(all_commits))[0] + tag = f"git-mastery-start-{first_commit.hexsha[:7]}" + self.repo.create_tag(tag) - repo_initializer.add_pre_hook("first-commit", before_commit) - with repo_initializer.initialize() as repo: - print(repo) +# Register once on the RepoSmith instance, then call via rs.helper(...) +rs.add_helper(GitMasteryHelper) +rs.helper(GitMasteryHelper).create_start_tag() ``` -## Typical Git-Mastery test pattern +{: .note } +`GitMasteryHelper` is already defined in `exercise_utils.test` and is registered automatically when using `GitAutograderTestLoader`. You only need to implement it yourself if you are calling `create_repo_smith` directly outside of exercises. -1. Write one or more YAML specs under `tests/specs/` -2. Build the repository state with `initialize_repo(...)` -3. Run the exercise's `verify(...)` function against that state -4. Assert on comments and final status +--- -{: .reference } +## Typical test pattern in exercises + +Tests in `exercises` do not call `create_repo_smith` directly. Instead they use `GitAutograderTestLoader` from `exercise_utils.test`, which sets up the exercise environment and provides `rs` automatically. + +### Basic test + +```python +from exercise_utils.test import GitAutograderTestLoader, GitMasteryHelper, assert_output +from git_autograder import GitAutograderStatus +from .verify import verify, NO_MERGES, MISSING_MERGES + +REPOSITORY_NAME = "branch-bender" + +loader = GitAutograderTestLoader(REPOSITORY_NAME, verify) -See [Testing guide](/developers/docs/exercises/testing-patterns) for how this is typically wrapped inside `exercises` tests. -For the full field-level specification, refer to `repo-smith/specification.md` in the [`repo-smith` repository](https://github.com/git-mastery/repo-smith). +def test_successful_merge() -> None: + with loader.start() as (test, rs): + rs.git.commit(message="Initial commit", allow_empty=True) + rs.helper(GitMasteryHelper).create_start_tag() + + rs.git.checkout("feature/login", branch=True) + rs.git.commit(message="Add login", allow_empty=True) + rs.git.checkout("main") + rs.git.merge("feature/login", no_ff=True) + + output = test.run() + assert_output(output, GitAutograderStatus.SUCCESSFUL) + + +def test_no_merges() -> None: + with loader.start() as (test, rs): + rs.git.commit(message="Initial commit", allow_empty=True) + rs.helper(GitMasteryHelper).create_start_tag() + + output = test.run() + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [NO_MERGES]) +``` + +### With a remote repository + +```python +def test_remote_branch_rename() -> None: + with loader.start(include_remote_repo=True) as (test, rs, rs_remote): + remote_path = str(rs_remote.repo.git_dir) + + rs.git.commit(message="Initial commit", allow_empty=True) + rs.git.remote_add("origin", remote_path) + rs.git.branch("feature/old") + rs.git.push("origin", "feature/old") + + # Simulate the student's action + rs.git.branch("feature/new", old_branch="feature/old", move=True) + rs.git.push("origin", "feature/new") + rs.git.push("origin", ":feature/old") + + output = test.run() + assert_output(output, GitAutograderStatus.SUCCESSFUL) +``` + +### Starting from a cloned repository + +```python +def test_from_clone() -> None: + with loader.start(clone_from="https://github.com/git-mastery/some-repo") as (test, rs): + output = test.run() + assert_output(output, GitAutograderStatus.SUCCESSFUL) +``` + +{: .reference } + +See [Testing guide](/developers/docs/exercises/testing-patterns) for the full `GitAutograderTestLoader` API. From 1a18c7861f5dad3bf7bf62a8e2215dcd5381f9c6 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:45:17 +0800 Subject: [PATCH 7/9] Refactor and update documentation across multiple files, enhancing clarity and removing outdated content --- .../{how-to-add-a-command.md => command.md} | 26 +++++++++++-------- docs/app/progress-integration.md | 16 ++++++------ docs/app/publishing-flow.md | 19 ++++++-------- docs/exercises/exercise.md | 5 +--- docs/exercises/hands-on.md | 3 --- docs/getting-started/setup.md | 4 --- docs/overview/index.md | 1 + 7 files changed, 33 insertions(+), 41 deletions(-) rename docs/app/{how-to-add-a-command.md => command.md} (81%) diff --git a/docs/app/how-to-add-a-command.md b/docs/app/command.md similarity index 81% rename from docs/app/how-to-add-a-command.md rename to docs/app/command.md index 0df72c9..09677aa 100644 --- a/docs/app/how-to-add-a-command.md +++ b/docs/app/command.md @@ -10,6 +10,10 @@ This guide walks through adding a new top-level command to the `gitmastery` CLI. We'll use a simple `greet` command as an example throughout. +{: .warning } + +> This is for demonstration purposes. The `greet` command is not a real feature and should not be merged into the codebase. When adding real commands, follow the same steps but implement the actual functionality needed. + --- ## 1. Create the command file @@ -41,12 +45,12 @@ def greet(name: str) -> None: ### Output helpers -| Helper | When to use | -|---|---| -| `info(msg)` | Normal status messages | -| `success(msg)` | Command completed successfully | -| `warn(msg)` | Non-fatal issues or warnings | -| `error(msg)` | Fatal issues — exits immediately | +| Helper | When to use | +| -------------- | -------------------------------- | +| `info(msg)` | Normal status messages | +| `success(msg)` | Command completed successfully | +| `warn(msg)` | Non-fatal issues or warnings | +| `error(msg)` | Fatal issues — exits immediately | --- @@ -120,11 +124,11 @@ def test_greet(runner: BinaryRunner, gitmastery_root: Path) -> None: ### `RunResult` assertion methods -| Method | Description | -|---|---| -| `.assert_success()` | Asserts exit code is 0 | -| `.assert_stdout_contains(text)` | Asserts stdout contains an exact substring | -| `.assert_stdout_matches(pattern)` | Asserts stdout matches a regex pattern | +| Method | Description | +| --------------------------------- | ------------------------------------------ | +| `.assert_success()` | Asserts exit code is 0 | +| `.assert_stdout_contains(text)` | Asserts stdout contains an exact substring | +| `.assert_stdout_matches(pattern)` | Asserts stdout matches a regex pattern | --- diff --git a/docs/app/progress-integration.md b/docs/app/progress-integration.md index 0e26d67..4b16dee 100644 --- a/docs/app/progress-integration.md +++ b/docs/app/progress-integration.md @@ -41,14 +41,14 @@ If the exercise already has a `Completed` entry, subsequent attempts are not rec ```mermaid flowchart TD - A([gitmastery progress sync on]) --> B[Check Git + GitHub CLI] - B --> C[Fork git-mastery/progress] - C --> D[Clone fork into progress/] - D --> E[Merge local + remote entries] - E --> F[Push to fork] - F --> G{PR exists?} - G -- No --> H[Open PR to git-mastery/progress] - G -- Yes --> I([Done]) + A(["gitmastery progress sync on"]) --> B["Check Git and GitHub CLI"] + B --> C["Fork git-mastery/progress"] + C --> D["Clone fork into progress/"] + D --> E["Merge local and remote entries"] + E --> F["Push to fork"] + F --> G{"PR exists?"} + G -- No --> H["Open PR to git-mastery/progress"] + G -- Yes --> I(["Done"]) H --> I ``` diff --git a/docs/app/publishing-flow.md b/docs/app/publishing-flow.md index 586ced8..e58b25b 100644 --- a/docs/app/publishing-flow.md +++ b/docs/app/publishing-flow.md @@ -20,9 +20,6 @@ Label the PR with one of: When the labelled PR is merged to `main`, the pipeline runs automatically. -{: .warning } -Do not push version tags manually. The automated pipeline manages tag creation. Manually pushed tags may trigger duplicate publish jobs. - ## Pipeline overview ```mermaid @@ -30,25 +27,25 @@ flowchart TD A([PR merged to main with bump label]) --> B[bump-version.yml] B --> C[Shared action creates version tag] C --> D[publish.yml triggered] - D --> E[prepare job — get latest tag] + D --> E["prepare job - get latest tag"] E --> F{should_publish?} F -- No --> G([Stop]) F -- Yes --> H[Build jobs run in parallel] - H --> H1[linux-build\namd64 + arm64] - H --> H2[arch-build\namd64] - H --> H3[windows\namd64] - H --> H4[macos-build\namd64 + arm64] + H --> H1["linux-build
amd64 + arm64"] + H --> H2["arch-build
amd64"] + H --> H3["windows
amd64"] + H --> H4["macos-build
amd64 + arm64"] H1 --> I1[debian-build] I1 --> I2[debian-publish-apt] - H2 --> J1[arch-publish\nAUR] + H2 --> J1["arch-publish
AUR"] H3 --> K1[winget-publish] - H4 --> L1[macos-publish\nHomebrew tap] - L1 --> L2[macos-test\nSmoke test via brew install] + H4 --> L1["macos-publish
Homebrew tap"] + L1 --> L2["macos-test
Smoke test via brew install"] ``` ## Workflow files diff --git a/docs/exercises/exercise.md b/docs/exercises/exercise.md index 72b384c..ad9e544 100644 --- a/docs/exercises/exercise.md +++ b/docs/exercises/exercise.md @@ -6,9 +6,6 @@ nav_order: 2 # How to add an exercise -1. TOC -{:toc} - ## Before contributing If you are proposing a new exercise, instead of implementing an [already approved exercise proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22exercise%20discussion%22%20label%3A%22help%20wanted%22), make sure that you have done the following: @@ -122,7 +119,7 @@ Some examples of verifications: ### Testing verify logic -Use [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate possible student answers and verify that your grading logic correctly accepts valid attempts and flags expected mistakes. +Use [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate possible student answers and verify that your grading logic correctly accepts valid attempts and flags expected mistakes. Refer to existing `test_verify.py` files to see examples of unit testing the verification script. diff --git a/docs/exercises/hands-on.md b/docs/exercises/hands-on.md index 5126cae..eb4b7e2 100644 --- a/docs/exercises/hands-on.md +++ b/docs/exercises/hands-on.md @@ -6,9 +6,6 @@ nav_order: 1 # How to add a hands-on -1. TOC -{:toc} - ## What is a hands-on? [Lessons accompanying Git-Mastery App](https://nus-cs2103-ay2526s1.github.io/website/se-book-adapted/git-trail/index.html) contains hands-on practicals for students to get hands-on experience of the Git concepts covered by the lesson. Some of those hands-on practicals need to set up a sandbox containing the required folders, files, and repositories before the student can start. Git-Mastery app can set up that sandbox for students so that they can get to the practical part more easily. diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 1f90de3..878f7e5 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -37,8 +37,6 @@ Use the guidance on this page as the default when setting up repositories locall {: .note } - > Note - > > Recommended to install for formatting and linting support. ```bash @@ -74,8 +72,6 @@ Use the guidance on this page as the default when setting up repositories locall {: .note } - > Note - > > Recommended to install for formatting and linting support. ```bash diff --git a/docs/overview/index.md b/docs/overview/index.md index 79d2722..c01937f 100644 --- a/docs/overview/index.md +++ b/docs/overview/index.md @@ -2,6 +2,7 @@ title: Overview nav_order: 2 has_children: true +has_toc: false --- # Overview From 6a126a139b54a8b4feb0818fc832acd9937d4de2 Mon Sep 17 00:00:00 2001 From: jia xin Date: Mon, 30 Mar 2026 21:33:06 +0800 Subject: [PATCH 8/9] Clean up links and diagrams --- docs/exercises/download-workflow.md | 73 ++++++++-------------------- docs/exercises/exercise-structure.md | 2 +- docs/exercises/exercise.md | 70 +++++++++++++++++++------- docs/exercises/hands-on.md | 41 +++++++++++++--- docs/exercises/index.md | 11 +---- docs/getting-started/setup.md | 17 +++---- docs/overview/index.md | 2 +- 7 files changed, 114 insertions(+), 102 deletions(-) diff --git a/docs/exercises/download-workflow.md b/docs/exercises/download-workflow.md index defc91d..73b5b14 100644 --- a/docs/exercises/download-workflow.md +++ b/docs/exercises/download-workflow.md @@ -50,58 +50,23 @@ j --> k[Execute the download function in the hands-on file] These are handled within the app's `download.py` command through the `download_exercise` function. ```mermaid -flowchart -A[Check if exercise exists] --> B{Exercise exists?} -B -- No --> C[Error and stop the download] -B -- Yes --> D[Check if exercise folder already exists] -D -- Yes --> E[Delete existing folder] -D -- No --> F[Create exercise folder] -E --> F -F --> G[Change directory to exercise folder] -G --> H[Download base files] -H --> I[Read config file] - -I --> J{Requires Git?} -J -- Yes --> K[Check Git setup] -K -- Not setup --> L[Rollback, remove folder, stop download] -J -- No --> M[Proceed] - -M --> N{Requires GitHub?} -N -- Yes --> O[Check GitHub setup] -O -- Not setup --> P[Rollback, remove folder, stop download] -N -- No --> Q[Proceed] - -Q --> R{Config has base files?} -R -- Yes --> S[Download additional resources] -R -- No --> T[Skip resource download] - -S --> U{Repo type is managed?} -T --> U -U -- Yes --> V[Set up exercise folder] -U -- No --> Z1[Save config with timestamp and print next steps] - -V --> V1[Save metadata to .gitmastery-exercise.json] -V1 --> V2{Repo type: local or remote?} -V2 -- Local --> V3[Create local folder] -V2 -- Remote --> V4[Retrieve from GitHub] -V4 --> V5{Fork required?} -V5 -- Yes --> V6[Check or delete existing fork, create new fork, clone fork] -V5 -- No --> V7[Clone repository] -V3 --> V8[Change into repo folder] -V6 --> V8 -V7 --> V8 -V8 --> V9[Fetch resources via download.py] -V9 --> V10{Resources exist?} -V10 -- Yes --> V11[Download and save resources] -V10 -- No --> V12[Skip resource download] -V11 --> V13{Repo init enabled?} -V12 --> V13 -V13 -- Yes --> V14[Initialize repo and commit initial state] -V13 -- No --> V15[Skip repo init] -V14 --> V16[Execute setup from download.py] -V15 --> V16 -V16 --> V17[Print next steps] - -Z1 --> Z2[Download complete] -V17 --> Z2 +flowchart TD +A[Check exercise exists] -->|Not found| ERR[Error and stop] +A -->|Found| B[Prepare exercise folder] +B --> C[Download base files and read config] +C --> D{Prerequisites met?\nGit / GitHub} +D -->|No| ROLL[Rollback and stop] +D -->|Yes| E[Download additional resources if any] +E --> F{Repo type managed?} +F -->|No| G[Save config and print next steps] +F -->|Yes| H[Save metadata to .gitmastery-exercise.json] +H --> I{Repo type} +I -->|local| J[Create local repo] +I -->|remote| K{Fork required?} +K -->|Yes| L[Fork and clone] +K -->|No| M[Clone repository] +J & L & M --> N[Run download.py setup] +N --> O[Print next steps] +G --> DONE[Download complete] +O --> DONE ``` diff --git a/docs/exercises/exercise-structure.md b/docs/exercises/exercise-structure.md index 9e9b8a0..b81856d 100644 --- a/docs/exercises/exercise-structure.md +++ b/docs/exercises/exercise-structure.md @@ -60,7 +60,7 @@ We opted to use a standardized configuration for exercises because they often fo The `new.sh` script generates one for you, but you can modify the file directly: - `exercise_name`: raw exercise name that will be indexed; recommended to use [kebab case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) -- `tags`: used during indexing on the [exercise directory](https://git-mastery.github.io/exercises) +- `tags`: specify topic related to exercise - `requires_git`: performs a check to ensure that Git is installed and `user.name` and `user.email` are configured - `requires_github`: performs a check to ensure that GitHub CLI is installed and the user has authenticated - `base_files`: specifies the files from `res/` to be downloaded into the exercise root diff --git a/docs/exercises/exercise.md b/docs/exercises/exercise.md index ad9e544..185d595 100644 --- a/docs/exercises/exercise.md +++ b/docs/exercises/exercise.md @@ -6,13 +6,26 @@ nav_order: 2 # How to add an exercise +## What is an exercise? + +Exercises are graded Git challenges that students complete independently to demonstrate their understanding of Git concepts. Unlike hands-ons, which only set up a sandbox for a lesson, exercises have a full lifecycle: the app downloads a starting repository state, the student performs the required Git operations, and the app verifies their work against a grading script. + +For example, a student can run `gitmastery download branch-bender` to receive a repository in a specific state, then work through the challenge before running `gitmastery verify branch-bender` to get feedback. + +Each exercise consists of two main components: + +- **`download.py`** — sets up the initial repository state the student works from. +- **`verify.py`** — inspects the student's repository after they attempt the exercise and returns a pass or fail result with feedback. + +Some examples of how Git-Mastery app uses exercises can be found [here](https://git-mastery.org/exercises-directory/index.html). + ## Before contributing -If you are proposing a new exercise, instead of implementing an [already approved exercise proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22exercise%20discussion%22%20label%3A%22help%20wanted%22), make sure that you have done the following: +If you are proposing a new exercise, instead of implementing an [already approved exercise proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22exercise%20discussion%22%20label%3A%22good%20first%20issue%22), make sure that you have done the following: - [ ] Create an [exercise discussion](https://github.com/git-mastery/exercises/issues/new?template=exercise_discussion.yaml) - [ ] Obtain approval on the exercise -- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) +- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) (if needed) ## Create a new exercise @@ -24,7 +37,7 @@ Use the provided `new.sh` script to generate the scaffolding for a new exercise: The script will first prompt if you want to create a hands-on or exercise. -Enter `exercise` or `e` to create a new exercise. +Enter `e` to create a new exercise. Then, the script will prompt you for: @@ -65,16 +78,6 @@ These are some references for download setups for other exercises: 3. For any commands that require `gh`, make sure that the `requires_github` configuration is set to `true` so that the app automatically checks that the student has set up GitHub CLI correctly. 4. `setup(...)` may receive a `repo-smith` helper object as `rs`; use it instead of shelling out directly when possible. -### Testing downloads - -To test that your download script works, use the provided script: - -```bash -./test-download.sh -``` - -You can find the downloaded repository under `test-downloads/`. - ## Verification setup The verification process is controlled by `verify.py`. This file contains the grading logic that inspects the student's work and returns a result with a status and feedback comments. @@ -85,7 +88,7 @@ For more information about how Git-Mastery verifies exercise attempts, refer to The [`git-autograder`](https://github.com/git-mastery/git-autograder) library builds the `GitAutograderExercise` object passed to `verify(...)`. It exposes the exercise config, the working repository, and optional answer parsing. -If there is no helper for a check you need, you can still fall back to the underlying `GitPython` repository: +If there is no helper for a check you need, you can still fall back to the underlying `GitPython` repository (however, this should be avoided): ```python from git_autograder import ( @@ -96,8 +99,7 @@ from git_autograder import ( def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: - # Access the underlying GitPython repo: - exercise.repo.repo + repo = exercise.repo return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) ``` @@ -117,7 +119,20 @@ Some examples of verifications: 2. For any remote behavior to verify, provide a mock to substitute the behavior in unit tests. 3. Prefer raising `exercise.wrong_answer([...])` for incorrect submissions and reserve unexpected exceptions for genuine errors. -### Testing verify logic + +## Testing + +### Testing downloads without app + +To test that your download script works locally without the app, use the provided script: + +```bash +./test-download.sh +``` + +You can find the sandbox created by the script under `test-downloads/`. Check it manually to verify that it is as expected. + +### Testing verify logic without app Use [`repo-smith`](https://github.com/git-mastery/repo-smith) to simulate possible student answers and verify that your grading logic correctly accepts valid attempts and flags expected mistakes. @@ -133,6 +148,27 @@ You can run the unit tests of your exercise via: ./test.sh ``` +### Testing downloads and verify using app + +To test the full student workflow, push your changes to a branch in a repository and modify the `.gitmastery.json` file in your `gitmastery-exercises` directory such that `exercises_source` points to this branch. + +```json +{ + "type": "remote", + "username": "your-github-username", + "repository": "your-repository-name", + "branch": "your-branch-name" +} +``` + +Run `gitmastery download `. Check that the download is correct manually. +Navigate into the exercise folder and run `gitmastery verify`. This should cause a failure as the exercise has not been complicated. +Manually simulate a student's workflow within the exercise folder (both correct and incorrect outputs) and run `gitmastery verify` to ensure your verification logic correctly grades these scenarios. + +{: .warning } + +This will cause all subsequent `gitmastery download` and `gitmastery verify` commands run within this folder to pull exercises from your branch. After you are done testing, revert your [`.gitmastery.json`](/developers/docs/app/configuration/) file to its default state, where `exercises_source` is set to the Git-Mastery organisation repository. + ## Submitting the exercise for review Create a pull request from your fork using the provided pull request template. diff --git a/docs/exercises/hands-on.md b/docs/exercises/hands-on.md index eb4b7e2..f1cbf0c 100644 --- a/docs/exercises/hands-on.md +++ b/docs/exercises/hands-on.md @@ -8,21 +8,21 @@ nav_order: 1 ## What is a hands-on? -[Lessons accompanying Git-Mastery App](https://nus-cs2103-ay2526s1.github.io/website/se-book-adapted/git-trail/index.html) contains hands-on practicals for students to get hands-on experience of the Git concepts covered by the lesson. Some of those hands-on practicals need to set up a sandbox containing the required folders, files, and repositories before the student can start. Git-Mastery app can set up that sandbox for students so that they can get to the practical part more easily. +[Lessons accompanying Git-Mastery App](https://git-mastery.org/lessons/index.html) contains hands-on practicals for students to get hands-on experience of the Git concepts covered by the lesson. Some of those hands-on practicals need to set up a sandbox containing the required folders, files, and repositories before the student can start. Git-Mastery app can set up that sandbox for students so that they can get to the practical part more easily. For example, a student can run `gitmastery download hp-init-repo` to set up a sandbox for a specific hands-on practical. -Some examples of how Git-Mastery app helps create the sandbox for a hands-on practical can be seen in [this Git Tour](https://nus-cs2103-ay2526s1.github.io/website/book/gitAndGitHub/trail/recordingFolderHistory/index.html) (see T1L3 and T1L4). +Some examples of how Git-Mastery app helps create the sandbox for a hands-on practical can be seen in [this Git Tour](https://git-mastery.org/lessons/trail/recordingFolderHistory/#git-mastery-lessons) (see T1L3 and T1L4). ## Before contributing -New contributors are recommended to start by implementing an [already approved hands-on proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22hands-on%20discussion%22%20label%3A%22help%20wanted%22). +New contributors are recommended to start by implementing an [already approved hands-on proposal](https://github.com/git-mastery/exercises/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22hands-on%20discussion%22%20label%3A%22good%20first%20issue%22). If you are proposing a new hands-on instead, make sure that you have done the following: - [ ] Create a [hands-on discussion](https://github.com/git-mastery/exercises/issues/new?template=hands_on_discussion.yaml) - [ ] Obtain approval on the hands-on -- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) +- [ ] File a [remote repository request](https://github.com/git-mastery/exercises/issues/new?template=request_exercise_repository.yaml) (if needed) ## Implementing a hands-on @@ -34,7 +34,7 @@ First, run the provided `new.sh` script to generate the scaffolding for a new ha The script will first prompt if you want to create a hands-on or exercise. -Enter `hands-on` or `h` to create a new hands-on. +Enter `h` to create a new hands-on. Then, the script will prompt you for: @@ -59,7 +59,6 @@ Git-Mastery uses the format `hp-` for hands-on names, for example ```python import os -from exercise_utils.cli import run_command from exercise_utils.file import append_to_file, create_or_update_file from exercise_utils.git import add, init @@ -87,6 +86,11 @@ The setup instructions go under the `download` function. `__requires_git__` and `__requires_github__` tell the app whether to run automatic verification that the student has set up Git and GitHub CLI correctly. +{: .warning } + +> We are currently working on doing a one-time migration to reposmith for download functions, tracked in this [issue](https://github.com/git-mastery/exercises/issues/268). +> For now, we will still use this current syntax. + {: .reference } For more information about how Git-Mastery downloads a hands-on, refer to the [Download flow](/developers/docs/exercises/download-workflow). @@ -108,9 +112,11 @@ These are some references for download setups for other hands-ons: 1. Any operations should use OS-agnostic options, for example `shutil.rmtree` instead of `run_command(["rm"])`. 2. For any commands that require `gh`, make sure that the `__requires_github__` variable in the download setup is set to `True` so that the app automatically checks that the student has set up GitHub CLI correctly. -### Testing downloads +## Testing + +### Testing downloads without app -To test that your download script works, use the provided script: +To test that your download script works locally without the app, use the provided script: ```bash ./test-download.sh @@ -118,6 +124,25 @@ To test that your download script works, use the provided script: You can find the sandbox created by the script under `test-downloads/`. Check it manually to verify that it is as expected. +### Testing downloads using app + +To test the full student workflow, push your changes to a branch in a repository and modify the `.gitmastery.json` file in your `gitmastery-exercises` directory such that `exercises_source` points to this branch. + +```json +{ + "type": "remote", + "username": "your-github-username", + "repository": "your-repository-name", + "branch": "your-branch-name" +} +``` + +Run `gitmastery download hp-`. Check that the download is correct manually. + +{: .warning } + +This will cause all subsequent `gitmastery download` commands run within this folder to pull exercises from your branch. After you are done testing, revert your [`.gitmastery.json`](/developers/docs/app/configuration/) file to its default state, where `exercises_source` is set to the Git-Mastery organisation repository. + ## Submitting the hands-on for review Create a pull request from your fork using the provided pull request template. diff --git a/docs/exercises/index.md b/docs/exercises/index.md index 93fe631..028aa60 100644 --- a/docs/exercises/index.md +++ b/docs/exercises/index.md @@ -2,18 +2,9 @@ title: Exercises nav_order: 4 has_children: true -has_toc: false +has_toc: true --- # Exercises The [`exercises`](https://github.com/git-mastery/exercises) repository contains both hands-ons and graded exercises. - -## What this section covers - -- How to contribute hands-ons -- How to contribute exercises -- How exercise downloads are structured -- How verification works -- How to test `verify.py` reliably -- Shared utilities used during exercise download diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 878f7e5..ba12fb5 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -8,7 +8,11 @@ nav_order: 1 Git-Mastery is standardizing on a `uv`-first local development workflow. -Use the guidance on this page as the default when setting up repositories locally. Some repositories are still in transition, and may be using `pip` and `venv` for local development. +Use the guidance on this page as the default when setting up repositories locally. + +{: .warning } + +> Some repositories are still in transition, and may be using `pip` and `venv` for local development. ## Prerequisites @@ -16,6 +20,7 @@ Use the guidance on this page as the default when setting up repositories locall - Python 3.13+ - `uv` (refer to [installation guide](https://docs.astral.sh/uv/getting-started/installation/)) - GitHub CLI (`gh`) installed and authenticated when working on flows that interact with GitHub +- Git-Mastery app installed and configured (refer to [the setup guide](https://git-mastery.org/companion-app/index.html#installation-and-setup)) ## Repository setup (uv) @@ -77,13 +82,3 @@ Use the guidance on this page as the default when setting up repositories locall ```bash lefthook install ``` - -## GitHub-dependent work - -If you are working on downloads, remote exercises, or progress sync flows, ensure `gh auth status` succeeds before testing. - -Git-Mastery's GitHub-dependent commands also expect the `delete_repo` scope for flows that create and remove forks. - -```bash -gh auth refresh -s delete_repo -``` diff --git a/docs/overview/index.md b/docs/overview/index.md index c01937f..656254a 100644 --- a/docs/overview/index.md +++ b/docs/overview/index.md @@ -15,7 +15,7 @@ These are the main repositories that make up the Git-Mastery ecosystem, and are - [`exercises`](https://github.com/git-mastery/exercises): hands-ons, exercises, download setup, and verification tests - [`app`](https://github.com/git-mastery/app): the `gitmastery` CLI used by students and contributors -- [`repo-smith`](https://github.com/git-mastery/repo-smith): test-support library that creates Git repository states through helper objects so exercise verification scenarios can be set up deterministically in tests +- [`repo-smith`](https://github.com/git-mastery/repo-smith): test support library that creates Git repository states through helper objects so exercise verification scenarios can be set up deterministically in tests - [`git-autograder`](https://github.com/git-mastery/git-autograder): grading library that loads a Git-Mastery exercise attempt and turns repository or answer checks into structured verification result ## Supporting repositories From 10754172ab38736c987340dd4eaaced258775e80 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Wed, 1 Apr 2026 00:05:56 +0800 Subject: [PATCH 9/9] Update uv sync instructions --- docs/getting-started/setup.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index ba12fb5..c3e7cae 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -34,8 +34,13 @@ Use the guidance on this page as the default when setting up repositories locall 3. Run the following command to set up virtual environment and install dependencies: + {: .note } + + > The `--all-groups` flag ensures all dependencies are installed, including those for development and testing. + > Development and testing dependencies are needed for running pre-commit hooks and tests locally. + ```bash - uv sync + uv sync --all-groups ``` 4. Set up pre-commit hooks using LeftHook @@ -82,3 +87,15 @@ Use the guidance on this page as the default when setting up repositories locall ```bash lefthook install ``` + +## Troubleshooting + +### Pre-commit hooks not running as expected after migration to `uv` + +1. Delete existing hooks in `.git/hooks/` to remove any old `pip`-based hooks. + + ``` + rm -rf .git/hooks/pre-commit + ``` + +2. Re-run `uv run lefthook install` to set up the new hooks.