Skip to content
16 changes: 16 additions & 0 deletions tags_update/.gitmastery-exercise.json
Original file line number Diff line number Diff line change
@@ -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
}
}
8 changes: 8 additions & 0 deletions tags_update/README.md
Original file line number Diff line number Diff line change
@@ -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.
Empty file added tags_update/__init__.py
Empty file.
7 changes: 7 additions & 0 deletions tags_update/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from exercise_utils.git import tag, tag_with_options


def setup(verbose: bool = False):
tag_with_options("v1.0", ["HEAD~4"], verbose)
tag("april-update", verbose)
tag("may-update", verbose)
Empty file added tags_update/tests/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions tags_update/tests/specs/base.yml
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions tags_update/tests/specs/missing_january_commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
initialization:
steps:
- type: commit
empty: true
message: Update duty roster for February
id: start
- 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
22 changes: 22 additions & 0 deletions tags_update/tests/specs/missing_tags.yml
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions tags_update/tests/specs/old_tag_still_exists.yml
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions tags_update/tests/specs/wrong_april_tag.yml
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions tags_update/tests/specs/wrong_january_tag.yml
Original file line number Diff line number Diff line change
@@ -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
53 changes: 53 additions & 0 deletions tags_update/tests/test_verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output

from ..verify import (
verify,
MISSING_JANUARY_TAG,
WRONG_JANUARY_TAG_COMMIT,
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:
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:
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]
)


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")],
)
67 changes: 67 additions & 0 deletions tags_update/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import List, Optional

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'."
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."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't single out the May commit as the only wrong commit, as the message would be shown for tagging any commit that isn't the April commit with the april-update tag. You can create a separate logic that checks if the incorrectly tagged commit is May with this message, if you wish, and keep the general logic later on.

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 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
return None


def verify(exercise: GitAutograderExercise) -> GitAutograderOutput:
tags = exercise.repo.repo.tags

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])

# 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")])

january_tag_commit = tags["january-update"].commit
if january_tag_commit.hexsha != january_commit.hexsha:
raise exercise.wrong_answer([WRONG_JANUARY_TAG_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")])

april_tag_commit = tags["april-update"].commit
if april_tag_commit.hexsha != april_commit.hexsha:
raise exercise.wrong_answer([WRONG_APRIL_TAG_COMMIT])
Comment on lines +32 to +62
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the user could potentially struggle with multiple issues at once, what do you think of collecting the comments and displaying them all together at the end instead of raising an error at the first mistake? This would require some extra tweaking in the test verification setup. I'd like to hear your thoughts on it.


return exercise.to_output(
[SUCCESS_MESSAGE],
GitAutograderStatus.SUCCESSFUL,
)