From 76973d93ab8c1756ee7a42f71c6d16383ba1b5f9 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:07:39 +0800 Subject: [PATCH 01/14] [tags-update] Implement download and verify --- tags_update/.gitmastery-exercise.json | 16 ++++++++ tags_update/README.md | 8 ++++ tags_update/__init__.py | 0 tags_update/download.py | 9 +++++ tags_update/verify.py | 57 +++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 tags_update/.gitmastery-exercise.json create mode 100644 tags_update/README.md create mode 100644 tags_update/__init__.py create mode 100644 tags_update/download.py create mode 100644 tags_update/verify.py diff --git a/tags_update/.gitmastery-exercise.json b/tags_update/.gitmastery-exercise.json new file mode 100644 index 00000000..da019c11 --- /dev/null +++ b/tags_update/.gitmastery-exercise.json @@ -0,0 +1,16 @@ +{ + "exercise_name": "tags-update", + "tags": [ + "git-tag" + ], + "requires_git": true, + "requires_github": true, + "base_files": {}, + "exercise_repo": { + "repo_type": "remote", + "repo_name": "duty-roster", + "repo_title": "gm-duty-roster", + "create_fork": false, + "init": null + } +} \ No newline at end of file diff --git a/tags_update/README.md b/tags_update/README.md new file mode 100644 index 00000000..a697fa0e --- /dev/null +++ b/tags_update/README.md @@ -0,0 +1,8 @@ +# tags-update + +The `duty-roster` repo contains text files that track which people are assigned for duties on which days of the week. Some of tags added earlier needs found to be incorrect. + +## Task + +1. To make tag names consistent, change `first-update` tag to `january-update`. +2. The `april-update` tag is currently pointing to the commit that updates the duty roster for May. Move it to the correct commit. diff --git a/tags_update/__init__.py b/tags_update/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tags_update/download.py b/tags_update/download.py new file mode 100644 index 00000000..78de9c5f --- /dev/null +++ b/tags_update/download.py @@ -0,0 +1,9 @@ +from exercise_utils.cli import run_command + +__resources__ = {} + + +def setup(verbose: bool = False): + run_command(["git", "tag", "first-update", "HEAD~4"], verbose) + run_command(["git", "tag", "april-update"], verbose) + run_command(["git", "tag", "may-update"], verbose) diff --git a/tags_update/verify.py b/tags_update/verify.py new file mode 100644 index 00000000..d6d3e5b1 --- /dev/null +++ b/tags_update/verify.py @@ -0,0 +1,57 @@ +from git_autograder import ( + GitAutograderExercise, + GitAutograderOutput, + GitAutograderStatus, +) + +MISSING_JANUARY_TAG = "The 'january-update' tag is missing! You need to rename 'first-update' to 'january-update'." +WRONG_JANUARY_TAG_COMMIT = "The 'january-update' tag is pointing to the wrong commit! It should point to the January commit." +MISSING_APRIL_TAG = "The 'april-update' tag is missing!" +WRONG_APRIL_TAG_COMMIT = "The 'april-update' tag is pointing to the wrong commit! It should point to the April commit, not the May commit." +OLD_FIRST_UPDATE_TAG = "The old 'first-update' tag still exists! You need to delete it after renaming to 'january-update'." +SUCCESS_MESSAGE = "Great work! You have successfully updated the tags to point to the correct commits." +MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" + + +def find_commit_by_message(exercise: GitAutograderExercise, message_fragment: str): + """Find a commit containing the given message fragment.""" + commits = list(exercise.repo.repo.iter_commits(all=True)) + for commit in commits: + if message_fragment in commit.message: + return commit + return None + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + tags = exercise.repo.repo.tags + + # Verify that the tags exist and that the old first-update tag is deleted + if "first-update" in tags: + raise exercise.wrong_answer([OLD_FIRST_UPDATE_TAG]) + if "january-update" not in tags: + raise exercise.wrong_answer([MISSING_JANUARY_TAG]) + if "april-update" not in tags: + raise exercise.wrong_answer([MISSING_APRIL_TAG]) + + # Get correct commits that the tags should point to + january_commit = find_commit_by_message(exercise, "January") + if january_commit is None: + raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) + + april_commit = find_commit_by_message(exercise, "April") + if april_commit is None: + raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) + + # Verify that the tags point to the correct commits + january_tag_commit = tags["january-update"].commit + if january_tag_commit.hexsha != january_commit.hexsha: + raise exercise.wrong_answer([WRONG_JANUARY_TAG_COMMIT]) + + april_tag_commit = tags["april-update"].commit + if april_tag_commit.hexsha != april_commit.hexsha: + raise exercise.wrong_answer([WRONG_APRIL_TAG_COMMIT]) + + return exercise.to_output( + [SUCCESS_MESSAGE], + GitAutograderStatus.SUCCESSFUL, + ) \ No newline at end of file From f4f0551f809f4e7656f09950c23c7e726da9fd79 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:07:53 +0800 Subject: [PATCH 02/14] [tags-update] Add tests --- tags_update/tests/__init__.py | 0 tags_update/tests/specs/base.yml | 28 +++++++++++++ .../tests/specs/missing_january_commit.yml | 24 +++++++++++ tags_update/tests/specs/missing_tags.yml | 22 ++++++++++ .../tests/specs/old_tag_still_exists.yml | 28 +++++++++++++ tags_update/tests/specs/wrong_april_tag.yml | 28 +++++++++++++ tags_update/tests/test_verify.py | 41 +++++++++++++++++++ 7 files changed, 171 insertions(+) create mode 100644 tags_update/tests/__init__.py create mode 100644 tags_update/tests/specs/base.yml create mode 100644 tags_update/tests/specs/missing_january_commit.yml create mode 100644 tags_update/tests/specs/missing_tags.yml create mode 100644 tags_update/tests/specs/old_tag_still_exists.yml create mode 100644 tags_update/tests/specs/wrong_april_tag.yml create mode 100644 tags_update/tests/test_verify.py diff --git a/tags_update/tests/__init__.py b/tags_update/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tags_update/tests/specs/base.yml b/tags_update/tests/specs/base.yml new file mode 100644 index 00000000..09e2d69c --- /dev/null +++ b/tags_update/tests/specs/base.yml @@ -0,0 +1,28 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: tag + tag-name: january-update + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: tag + tag-name: april-update + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: may-update diff --git a/tags_update/tests/specs/missing_january_commit.yml b/tags_update/tests/specs/missing_january_commit.yml new file mode 100644 index 00000000..98a0f8bb --- /dev/null +++ b/tags_update/tests/specs/missing_january_commit.yml @@ -0,0 +1,24 @@ +initialization: + steps: + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: tag + tag-name: january-update + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: tag + tag-name: april-update + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: may-update diff --git a/tags_update/tests/specs/missing_tags.yml b/tags_update/tests/specs/missing_tags.yml new file mode 100644 index 00000000..f804239b --- /dev/null +++ b/tags_update/tests/specs/missing_tags.yml @@ -0,0 +1,22 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: commit + empty: true + message: Update roster for May + id: may-commit diff --git a/tags_update/tests/specs/old_tag_still_exists.yml b/tags_update/tests/specs/old_tag_still_exists.yml new file mode 100644 index 00000000..b9f2935d --- /dev/null +++ b/tags_update/tests/specs/old_tag_still_exists.yml @@ -0,0 +1,28 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: tag + tag-name: first-update + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: april-update + - type: tag + tag-name: may-update diff --git a/tags_update/tests/specs/wrong_april_tag.yml b/tags_update/tests/specs/wrong_april_tag.yml new file mode 100644 index 00000000..9333399d --- /dev/null +++ b/tags_update/tests/specs/wrong_april_tag.yml @@ -0,0 +1,28 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: tag + tag-name: january-update + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: april-update + - type: tag + tag-name: may-update diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py new file mode 100644 index 00000000..97988400 --- /dev/null +++ b/tags_update/tests/test_verify.py @@ -0,0 +1,41 @@ +from git_autograder import GitAutograderTestLoader, assert_output +from git_autograder.status import GitAutograderStatus + +from ..verify import ( + verify, + MISSING_JANUARY_TAG, + WRONG_APRIL_TAG_COMMIT, + OLD_FIRST_UPDATE_TAG, + SUCCESS_MESSAGE, + MISSING_COMMIT_MESSAGE, +) + +REPOSITORY_NAME = "tags-update" + +loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) + + +def test_base(): + with loader.load("specs/base.yml", "start") as output: + print(output) + assert_output(output, GitAutograderStatus.SUCCESSFUL, [SUCCESS_MESSAGE]) + + +def test_missing_tags(): + with loader.load("specs/missing_tags.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_JANUARY_TAG]) + + +def test_wrong_april_tag(): + with loader.load("specs/wrong_april_tag.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT]) + + +def test_old_tag_still_exists(): + with loader.load("specs/old_tag_still_exists.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [OLD_FIRST_UPDATE_TAG]) + + +def test_missing_january_commit(): + with loader.load("specs/missing_january_commit.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_COMMIT_MESSAGE.format(message="January")]) From dba79931b2c3ea187c1bbd7427c6104fec8da3e8 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:10:27 +0800 Subject: [PATCH 03/14] [tags-update] Add ending line --- tags_update/verify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags_update/verify.py b/tags_update/verify.py index d6d3e5b1..c372b729 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -54,4 +54,4 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: return exercise.to_output( [SUCCESS_MESSAGE], GitAutograderStatus.SUCCESSFUL, - ) \ No newline at end of file + ) From 0502312486c67f68c7e43e3e5e37f70c52e29623 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:29:53 +0800 Subject: [PATCH 04/14] [tags-update] Add more strict verification --- tags_update/tests/specs/missing_january_commit.yml | 2 +- tags_update/verify.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tags_update/tests/specs/missing_january_commit.yml b/tags_update/tests/specs/missing_january_commit.yml index 98a0f8bb..b0dc52ae 100644 --- a/tags_update/tests/specs/missing_january_commit.yml +++ b/tags_update/tests/specs/missing_january_commit.yml @@ -3,7 +3,7 @@ initialization: - type: commit empty: true message: Update duty roster for February - id: february-commit + id: start - type: tag tag-name: january-update - type: commit diff --git a/tags_update/verify.py b/tags_update/verify.py index c372b729..1c6f1cf9 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -13,11 +13,11 @@ MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" -def find_commit_by_message(exercise: GitAutograderExercise, message_fragment: str): - """Find a commit containing the given message fragment.""" +def find_commit_by_message(exercise: GitAutograderExercise, message: str): + """Find a commit with the given message.""" commits = list(exercise.repo.repo.iter_commits(all=True)) for commit in commits: - if message_fragment in commit.message: + if message.strip() == commit.message.strip(): return commit return None @@ -34,11 +34,11 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([MISSING_APRIL_TAG]) # Get correct commits that the tags should point to - january_commit = find_commit_by_message(exercise, "January") + january_commit = find_commit_by_message(exercise, "Add January duty roster") if january_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) - april_commit = find_commit_by_message(exercise, "April") + april_commit = find_commit_by_message(exercise, "Update duty roster for April") if april_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) From 23b6763803171e61edb36744c6f4bfce8ebd5ea4 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:33:33 +0800 Subject: [PATCH 05/14] [tags-update] Enhance tagging functionality --- exercise_utils/git.py | 12 +++++++++--- tags_update/download.py | 10 ++++------ tags_update/tests/test_verify.py | 1 - tags_update/verify.py | 5 ++++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/exercise_utils/git.py b/exercise_utils/git.py index ca4ef985..3700e9c1 100644 --- a/exercise_utils/git.py +++ b/exercise_utils/git.py @@ -5,9 +5,15 @@ from exercise_utils.cli import run, run_command -def tag(tag_name: str, verbose: bool) -> None: - """Tags the latest commit with the given tag_name.""" - run_command(["git", "tag", tag_name], verbose) +def tag(tag_name: str, verbose: bool, commit_ref: str | None = None) -> None: + """Tags the given commit with the given tag_name. + + If no commit_ref is provided, the latest commit is tagged. + """ + if commit_ref: + run_command(["git", "tag", tag_name, commit_ref], verbose) + else: + run_command(["git", "tag", tag_name], verbose) def tag_with_options(tag_name: str, options: List[str], verbose: bool) -> None: diff --git a/tags_update/download.py b/tags_update/download.py index 78de9c5f..ee4a61ea 100644 --- a/tags_update/download.py +++ b/tags_update/download.py @@ -1,9 +1,7 @@ -from exercise_utils.cli import run_command - -__resources__ = {} +from exercise_utils.git import tag def setup(verbose: bool = False): - run_command(["git", "tag", "first-update", "HEAD~4"], verbose) - run_command(["git", "tag", "april-update"], verbose) - run_command(["git", "tag", "may-update"], verbose) + tag("first-update", verbose, "HEAD~4") + tag("april-update", verbose) + tag("may-update", verbose) diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index 97988400..5913ab85 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -17,7 +17,6 @@ def test_base(): with loader.load("specs/base.yml", "start") as output: - print(output) assert_output(output, GitAutograderStatus.SUCCESSFUL, [SUCCESS_MESSAGE]) diff --git a/tags_update/verify.py b/tags_update/verify.py index 1c6f1cf9..1420775d 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -1,3 +1,6 @@ +from typing import Optional +from git.objects.commit import Commit + from git_autograder import ( GitAutograderExercise, GitAutograderOutput, @@ -13,7 +16,7 @@ MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" -def find_commit_by_message(exercise: GitAutograderExercise, message: str): +def find_commit_by_message(exercise: GitAutograderExercise, message: str) -> Optional[Commit]: """Find a commit with the given message.""" commits = list(exercise.repo.repo.iter_commits(all=True)) for commit in commits: From 7d4f4f180fb1c0cb6051e30ee2bc3b63278be216 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:47:27 +0800 Subject: [PATCH 06/14] [tags-update] Reorder verification process and add more tests --- tags_update/tests/specs/wrong_january_tag.yml | 28 +++++++++++++++++++ tags_update/tests/test_verify.py | 6 ++++ tags_update/verify.py | 24 +++++++++------- 3 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 tags_update/tests/specs/wrong_january_tag.yml diff --git a/tags_update/tests/specs/wrong_january_tag.yml b/tags_update/tests/specs/wrong_january_tag.yml new file mode 100644 index 00000000..7a3f0323 --- /dev/null +++ b/tags_update/tests/specs/wrong_january_tag.yml @@ -0,0 +1,28 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: tag + tag-name: january-update + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: tag + tag-name: april-update + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: may-update diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index 5913ab85..082522fa 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -4,6 +4,7 @@ from ..verify import ( verify, MISSING_JANUARY_TAG, + WRONG_JANUARY_TAG_COMMIT, WRONG_APRIL_TAG_COMMIT, OLD_FIRST_UPDATE_TAG, SUCCESS_MESSAGE, @@ -25,6 +26,11 @@ def test_missing_tags(): assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_JANUARY_TAG]) +def test_wrong_january_tag(): + with loader.load("specs/wrong_january_tag.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_JANUARY_TAG_COMMIT]) + + def test_wrong_april_tag(): with loader.load("specs/wrong_april_tag.yml", "start") as output: assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT]) diff --git a/tags_update/verify.py b/tags_update/verify.py index 1420775d..30a40e05 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -28,28 +28,32 @@ def find_commit_by_message(exercise: GitAutograderExercise, message: str) -> Opt def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: tags = exercise.repo.repo.tags - # Verify that the tags exist and that the old first-update tag is deleted + # Ensure first-update tag does not exist if "first-update" in tags: - raise exercise.wrong_answer([OLD_FIRST_UPDATE_TAG]) + raise exercise.wrong_answer([OLD_FIRST_UPDATE_TAG]) + + # Ensure january-update tag exists if "january-update" not in tags: raise exercise.wrong_answer([MISSING_JANUARY_TAG]) - if "april-update" not in tags: - raise exercise.wrong_answer([MISSING_APRIL_TAG]) - # Get correct commits that the tags should point to + # Ensure january-update tag points to the correct commit january_commit = find_commit_by_message(exercise, "Add January duty roster") if january_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) - april_commit = find_commit_by_message(exercise, "Update duty roster for April") - if april_commit is None: - raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) - - # Verify that the tags point to the correct commits january_tag_commit = tags["january-update"].commit if january_tag_commit.hexsha != january_commit.hexsha: raise exercise.wrong_answer([WRONG_JANUARY_TAG_COMMIT]) + # Ensure april-update tag exists + if "april-update" not in tags: + raise exercise.wrong_answer([MISSING_APRIL_TAG]) + + # Ensure april-update tag points to the correct commit + april_commit = find_commit_by_message(exercise, "Update duty roster for April") + if april_commit is None: + raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) + april_tag_commit = tags["april-update"].commit if april_tag_commit.hexsha != april_commit.hexsha: raise exercise.wrong_answer([WRONG_APRIL_TAG_COMMIT]) From bf52be70899b6eaf4ebdb4f198d816e90b3b55f2 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:43:50 +0800 Subject: [PATCH 07/14] [tags-update] Refactor commit retrieval method in verification process --- tags_update/verify.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tags_update/verify.py b/tags_update/verify.py index 30a40e05..abeeee81 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -1,10 +1,10 @@ from typing import Optional -from git.objects.commit import Commit from git_autograder import ( GitAutograderExercise, GitAutograderOutput, GitAutograderStatus, + GitAutograderCommit, ) MISSING_JANUARY_TAG = "The 'january-update' tag is missing! You need to rename 'first-update' to 'january-update'." @@ -16,11 +16,11 @@ MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" -def find_commit_by_message(exercise: GitAutograderExercise, message: str) -> Optional[Commit]: +def get_commit_from_message(exercise: GitAutograderExercise, message: str) -> Optional[GitAutograderCommit]: """Find a commit with the given message.""" - commits = list(exercise.repo.repo.iter_commits(all=True)) + commits = list(exercise.repo.branches.branch("main").commits) for commit in commits: - if message.strip() == commit.message.strip(): + if message.strip() == commit.commit.message.strip(): return commit return None @@ -37,7 +37,7 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([MISSING_JANUARY_TAG]) # Ensure january-update tag points to the correct commit - january_commit = find_commit_by_message(exercise, "Add January duty roster") + january_commit = get_commit_from_message(exercise, "Add January duty roster") if january_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) @@ -50,7 +50,7 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([MISSING_APRIL_TAG]) # Ensure april-update tag points to the correct commit - april_commit = find_commit_by_message(exercise, "Update duty roster for April") + april_commit = get_commit_from_message(exercise, "Update duty roster for April") if april_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) From 4f15a4008a7d425e9c5498850779adc15e1990f8 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Sat, 6 Dec 2025 11:57:59 +0800 Subject: [PATCH 08/14] [tags-update] Update get_commit_from_message --- tags_update/tests/test_verify.py | 3 +-- tags_update/verify.py | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index 082522fa..7914462b 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -1,5 +1,4 @@ -from git_autograder import GitAutograderTestLoader, assert_output -from git_autograder.status import GitAutograderStatus +from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output from ..verify import ( verify, diff --git a/tags_update/verify.py b/tags_update/verify.py index abeeee81..a6bcb747 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional from git_autograder import ( GitAutograderExercise, @@ -16,9 +16,8 @@ MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" -def get_commit_from_message(exercise: GitAutograderExercise, message: str) -> Optional[GitAutograderCommit]: - """Find a commit with the given message.""" - commits = list(exercise.repo.branches.branch("main").commits) +def get_commit_from_message(commits: List[GitAutograderCommit], message: str) -> Optional[GitAutograderCommit]: + """Find a commit with the given message from a list of commits.""" for commit in commits: if message.strip() == commit.commit.message.strip(): return commit @@ -37,7 +36,8 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([MISSING_JANUARY_TAG]) # Ensure january-update tag points to the correct commit - january_commit = get_commit_from_message(exercise, "Add January duty roster") + main_branch_commits = exercise.repo.branches.branch("main").commits + january_commit = get_commit_from_message(main_branch_commits, "Add January duty roster") if january_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) @@ -50,7 +50,7 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([MISSING_APRIL_TAG]) # Ensure april-update tag points to the correct commit - april_commit = get_commit_from_message(exercise, "Update duty roster for April") + april_commit = get_commit_from_message(main_branch_commits, "Update duty roster for April") if april_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) From 8903401d70dab44c1e751464c8eaf8f64ad76de0 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:45:10 +0800 Subject: [PATCH 09/14] [tags-update] Refactor to use tags_with_options --- exercise_utils/git.py | 12 +++--------- tags_update/download.py | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/exercise_utils/git.py b/exercise_utils/git.py index 3700e9c1..ca4ef985 100644 --- a/exercise_utils/git.py +++ b/exercise_utils/git.py @@ -5,15 +5,9 @@ from exercise_utils.cli import run, run_command -def tag(tag_name: str, verbose: bool, commit_ref: str | None = None) -> None: - """Tags the given commit with the given tag_name. - - If no commit_ref is provided, the latest commit is tagged. - """ - if commit_ref: - run_command(["git", "tag", tag_name, commit_ref], verbose) - else: - run_command(["git", "tag", tag_name], verbose) +def tag(tag_name: str, verbose: bool) -> None: + """Tags the latest commit with the given tag_name.""" + run_command(["git", "tag", tag_name], verbose) def tag_with_options(tag_name: str, options: List[str], verbose: bool) -> None: diff --git a/tags_update/download.py b/tags_update/download.py index ee4a61ea..1b7ee737 100644 --- a/tags_update/download.py +++ b/tags_update/download.py @@ -1,7 +1,7 @@ -from exercise_utils.git import tag +from exercise_utils.git import tag, tag_with_options -def setup(verbose: bool = False): - tag("first-update", verbose, "HEAD~4") +def setup(verbose: bool = False): + tag_with_options("v1.0", ["HEAD~4"], verbose) tag("april-update", verbose) tag("may-update", verbose) From 262dcf87682804858eecc1c19622764f72aec58d Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:47:05 +0800 Subject: [PATCH 10/14] [tags-update] Remove unnecessary comments in verify.py --- tags_update/verify.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tags_update/verify.py b/tags_update/verify.py index a6bcb747..b2202ead 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -16,7 +16,9 @@ MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" -def get_commit_from_message(commits: List[GitAutograderCommit], message: str) -> Optional[GitAutograderCommit]: +def get_commit_from_message( + commits: List[GitAutograderCommit], message: str +) -> Optional[GitAutograderCommit]: """Find a commit with the given message from a list of commits.""" for commit in commits: if message.strip() == commit.commit.message.strip(): @@ -27,37 +29,38 @@ def get_commit_from_message(commits: List[GitAutograderCommit], message: str) -> def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: tags = exercise.repo.repo.tags - # Ensure first-update tag does not exist if "first-update" in tags: raise exercise.wrong_answer([OLD_FIRST_UPDATE_TAG]) - - # Ensure january-update tag exists + if "january-update" not in tags: raise exercise.wrong_answer([MISSING_JANUARY_TAG]) - + # Ensure january-update tag points to the correct commit main_branch_commits = exercise.repo.branches.branch("main").commits - january_commit = get_commit_from_message(main_branch_commits, "Add January duty roster") + january_commit = get_commit_from_message( + main_branch_commits, "Add January duty roster" + ) if january_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) - + january_tag_commit = tags["january-update"].commit if january_tag_commit.hexsha != january_commit.hexsha: raise exercise.wrong_answer([WRONG_JANUARY_TAG_COMMIT]) - # Ensure april-update tag exists if "april-update" not in tags: raise exercise.wrong_answer([MISSING_APRIL_TAG]) - + # Ensure april-update tag points to the correct commit - april_commit = get_commit_from_message(main_branch_commits, "Update duty roster for April") + april_commit = get_commit_from_message( + main_branch_commits, "Update duty roster for April" + ) if april_commit is None: raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) - + april_tag_commit = tags["april-update"].commit if april_tag_commit.hexsha != april_commit.hexsha: raise exercise.wrong_answer([WRONG_APRIL_TAG_COMMIT]) - + return exercise.to_output( [SUCCESS_MESSAGE], GitAutograderStatus.SUCCESSFUL, From 26055aeba40f4874f36ae8d28b26227fd5e10f9d Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:47:44 +0800 Subject: [PATCH 11/14] tags-update] Run ruff format --- tags_update/tests/test_verify.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index 7914462b..a406a4dc 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -27,12 +27,16 @@ def test_missing_tags(): def test_wrong_january_tag(): with loader.load("specs/wrong_january_tag.yml", "start") as output: - assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_JANUARY_TAG_COMMIT]) + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_JANUARY_TAG_COMMIT] + ) def test_wrong_april_tag(): with loader.load("specs/wrong_april_tag.yml", "start") as output: - assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT]) + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT] + ) def test_old_tag_still_exists(): @@ -42,4 +46,8 @@ def test_old_tag_still_exists(): def test_missing_january_commit(): with loader.load("specs/missing_january_commit.yml", "start") as output: - assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_COMMIT_MESSAGE.format(message="January")]) + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [MISSING_COMMIT_MESSAGE.format(message="January")], + ) From e0457b480dd75379ad24727a4a07024bed7070fa Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:33:34 +0800 Subject: [PATCH 12/14] [tags-update] Update README and instructions --- tags_update/README.md | 9 +-------- tags_update/download.py | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tags_update/README.md b/tags_update/README.md index a697fa0e..ef60e8be 100644 --- a/tags_update/README.md +++ b/tags_update/README.md @@ -1,8 +1 @@ -# tags-update - -The `duty-roster` repo contains text files that track which people are assigned for duties on which days of the week. Some of tags added earlier needs found to be incorrect. - -## Task - -1. To make tag names consistent, change `first-update` tag to `january-update`. -2. The `april-update` tag is currently pointing to the commit that updates the duty roster for May. Move it to the correct commit. +See https://git-mastery.github.io/lessons/tag/exercise-tags-update.html diff --git a/tags_update/download.py b/tags_update/download.py index 1b7ee737..15632159 100644 --- a/tags_update/download.py +++ b/tags_update/download.py @@ -2,6 +2,6 @@ def setup(verbose: bool = False): - tag_with_options("v1.0", ["HEAD~4"], verbose) + tag_with_options("first-update", ["HEAD~4"], verbose) tag("april-update", verbose) tag("may-update", verbose) From 0e49103d66e56537660650412fe6531639c90639 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:51:33 +0800 Subject: [PATCH 13/14] [tags-update] Improve verify.py and add more tests --- tags_update/tests/specs/missing_april_tag.yml | 27 ++++++++++++++ ...ssing_tags.yml => missing_january_tag.yml} | 5 +++ tags_update/tests/test_verify.py | 36 +++++++++++++------ tags_update/verify.py | 34 +++++++++++------- 4 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 tags_update/tests/specs/missing_april_tag.yml rename tags_update/tests/specs/{missing_tags.yml => missing_january_tag.yml} (84%) diff --git a/tags_update/tests/specs/missing_april_tag.yml b/tags_update/tests/specs/missing_april_tag.yml new file mode 100644 index 00000000..08afd681 --- /dev/null +++ b/tags_update/tests/specs/missing_april_tag.yml @@ -0,0 +1,27 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: tag + tag-name: january-update + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: may-update + diff --git a/tags_update/tests/specs/missing_tags.yml b/tags_update/tests/specs/missing_january_tag.yml similarity index 84% rename from tags_update/tests/specs/missing_tags.yml rename to tags_update/tests/specs/missing_january_tag.yml index f804239b..b22f535b 100644 --- a/tags_update/tests/specs/missing_tags.yml +++ b/tags_update/tests/specs/missing_january_tag.yml @@ -20,3 +20,8 @@ initialization: empty: true message: Update roster for May id: may-commit + - type: tag + tag-name: april-update + - type: tag + tag-name: may-update + diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index a406a4dc..9bf4e7fa 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -3,6 +3,7 @@ from ..verify import ( verify, MISSING_JANUARY_TAG, + MISSING_APRIL_TAG, WRONG_JANUARY_TAG_COMMIT, WRONG_APRIL_TAG_COMMIT, OLD_FIRST_UPDATE_TAG, @@ -20,22 +21,21 @@ def test_base(): assert_output(output, GitAutograderStatus.SUCCESSFUL, [SUCCESS_MESSAGE]) -def test_missing_tags(): - with loader.load("specs/missing_tags.yml", "start") as output: - assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_JANUARY_TAG]) - - -def test_wrong_january_tag(): - with loader.load("specs/wrong_january_tag.yml", "start") as output: +def test_missing_january_tag(): + with loader.load("specs/missing_january_tag.yml", "start") as output: assert_output( - output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_JANUARY_TAG_COMMIT] + output, + GitAutograderStatus.UNSUCCESSFUL, + [MISSING_JANUARY_TAG], ) -def test_wrong_april_tag(): - with loader.load("specs/wrong_april_tag.yml", "start") as output: +def test_missing_april_tag(): + with loader.load("specs/missing_april_tag.yml", "start") as output: assert_output( - output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT] + output, + GitAutograderStatus.UNSUCCESSFUL, + [MISSING_APRIL_TAG], ) @@ -44,6 +44,13 @@ def test_old_tag_still_exists(): assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [OLD_FIRST_UPDATE_TAG]) +def test_wrong_january_tag(): + with loader.load("specs/wrong_january_tag.yml", "start") as output: + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_JANUARY_TAG_COMMIT] + ) + + def test_missing_january_commit(): with loader.load("specs/missing_january_commit.yml", "start") as output: assert_output( @@ -51,3 +58,10 @@ def test_missing_january_commit(): GitAutograderStatus.UNSUCCESSFUL, [MISSING_COMMIT_MESSAGE.format(message="January")], ) + + +def test_wrong_april_tag(): + with loader.load("specs/wrong_april_tag.yml", "start") as output: + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT] + ) diff --git a/tags_update/verify.py b/tags_update/verify.py index b2202ead..6a6250e0 100644 --- a/tags_update/verify.py +++ b/tags_update/verify.py @@ -7,13 +7,13 @@ GitAutograderCommit, ) -MISSING_JANUARY_TAG = "The 'january-update' tag is missing! You need to rename 'first-update' to 'january-update'." -WRONG_JANUARY_TAG_COMMIT = "The 'january-update' tag is pointing to the wrong commit! It should point to the January commit." -MISSING_APRIL_TAG = "The 'april-update' tag is missing!" -WRONG_APRIL_TAG_COMMIT = "The 'april-update' tag is pointing to the wrong commit! It should point to the April commit, not the May commit." -OLD_FIRST_UPDATE_TAG = "The old 'first-update' tag still exists! You need to delete it after renaming to 'january-update'." +MISSING_JANUARY_TAG = "You are missing the 'january-update' tag." +WRONG_JANUARY_TAG_COMMIT = "The 'january-update' tag is pointing to the wrong commit. It should point to the January commit." +MISSING_APRIL_TAG = "You are missing the 'april-update' tag." +WRONG_APRIL_TAG_COMMIT = "The 'april-update' tag is pointing to the wrong commit. It should point to the April commit." +OLD_FIRST_UPDATE_TAG = "The 'first-update' tag still exists." SUCCESS_MESSAGE = "Great work! You have successfully updated the tags to point to the correct commits." -MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message" +MISSING_COMMIT_MESSAGE = "Could not find a commit with '{message}' in the message." def get_commit_from_message( @@ -27,35 +27,43 @@ def get_commit_from_message( def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + comments: List[str] = [] tags = exercise.repo.repo.tags + main_branch_commits = exercise.repo.branches.branch("main").commits + # Verify first-update is renamed to january-update if "first-update" in tags: - raise exercise.wrong_answer([OLD_FIRST_UPDATE_TAG]) + comments.append(OLD_FIRST_UPDATE_TAG) if "january-update" not in tags: - raise exercise.wrong_answer([MISSING_JANUARY_TAG]) + comments.append(MISSING_JANUARY_TAG) + + if comments: + raise exercise.wrong_answer(comments) - # Ensure january-update tag points to the correct commit - main_branch_commits = exercise.repo.branches.branch("main").commits january_commit = get_commit_from_message( main_branch_commits, "Add January duty roster" ) if january_commit is None: - raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="January")]) + raise exercise.wrong_answer( + [MISSING_COMMIT_MESSAGE.format(message="January")] + ) january_tag_commit = tags["january-update"].commit if january_tag_commit.hexsha != january_commit.hexsha: raise exercise.wrong_answer([WRONG_JANUARY_TAG_COMMIT]) + # Verify april-update is moved to correct commit if "april-update" not in tags: raise exercise.wrong_answer([MISSING_APRIL_TAG]) - # Ensure april-update tag points to the correct commit april_commit = get_commit_from_message( main_branch_commits, "Update duty roster for April" ) if april_commit is None: - raise exercise.wrong_answer([MISSING_COMMIT_MESSAGE.format(message="April")]) + raise exercise.wrong_answer( + [MISSING_COMMIT_MESSAGE.format(message="April")] + ) april_tag_commit = tags["april-update"].commit if april_tag_commit.hexsha != april_commit.hexsha: From f59e80f8328f60a3e70e6ed0dc001604b24e64f7 Mon Sep 17 00:00:00 2001 From: jovnc <95868357+jovnc@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:01:42 +0800 Subject: [PATCH 14/14] [tags-update] Add more repo-smith tests --- .../specs/first_update_tag_not_renamed.yml | 28 +++++++++++++++++++ tags_update/tests/test_verify.py | 5 ++++ 2 files changed, 33 insertions(+) create mode 100644 tags_update/tests/specs/first_update_tag_not_renamed.yml diff --git a/tags_update/tests/specs/first_update_tag_not_renamed.yml b/tags_update/tests/specs/first_update_tag_not_renamed.yml new file mode 100644 index 00000000..7ad209de --- /dev/null +++ b/tags_update/tests/specs/first_update_tag_not_renamed.yml @@ -0,0 +1,28 @@ +initialization: + steps: + - type: commit + empty: true + message: Add January duty roster + id: start + - type: tag + tag-name: first-update + - type: commit + empty: true + message: Update duty roster for February + id: february-commit + - type: commit + empty: true + message: Update roster for March + id: march-commit + - type: commit + empty: true + message: Update duty roster for April + id: april-commit + - type: tag + tag-name: april-update + - type: commit + empty: true + message: Update roster for May + id: may-commit + - type: tag + tag-name: may-update diff --git a/tags_update/tests/test_verify.py b/tags_update/tests/test_verify.py index 9bf4e7fa..2565530c 100644 --- a/tags_update/tests/test_verify.py +++ b/tags_update/tests/test_verify.py @@ -65,3 +65,8 @@ def test_wrong_april_tag(): assert_output( output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_APRIL_TAG_COMMIT] ) + + +def test_first_update_tag_not_renamed(): + with loader.load("specs/first_update_tag_not_renamed.yml", "start") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [OLD_FIRST_UPDATE_TAG, MISSING_JANUARY_TAG])