From 18ac2e157afd514a3d86b125f1f456bca04afcbe Mon Sep 17 00:00:00 2001 From: jia xin Date: Fri, 13 Mar 2026 19:31:57 +0800 Subject: [PATCH] Move metadata into .gitmastery folder --- app/cli.py | 4 ++++ app/commands/setup_folder.py | 4 +++- app/configs/gitmastery_config.py | 34 +++++++++++++++++++++++++++----- app/hooks/in_gitmastery_root.py | 4 ++-- app/logging/setup_logging.py | 6 +++--- tests/e2e/commands/test_setup.py | 6 +++--- 6 files changed, 44 insertions(+), 14 deletions(-) diff --git a/app/cli.py b/app/cli.py index f202821..3ec5ede 100644 --- a/app/cli.py +++ b/app/cli.py @@ -6,6 +6,7 @@ from app.commands import check, download, progress, setup, verify from app.commands.version import version +from app.configs.gitmastery_config import migrate_to_gitmastery_folder from app.utils.click import ClickColor, CliContextKey, warn from app.utils.version import Version from app.version import __version__ @@ -28,6 +29,9 @@ def cli(ctx: click.Context, verbose: bool) -> None: """Git-Mastery app""" ctx.ensure_object(dict) + if migrate_to_gitmastery_folder() is not None: + warn("Migrated .gitmastery.json and .gitmastery.log into .gitmastery/ folder.") + ctx.obj[CliContextKey.VERBOSE] = verbose current_version = Version.parse_version_string(__version__) diff --git a/app/commands/setup_folder.py b/app/commands/setup_folder.py index 7bc91cb..0c1ab69 100644 --- a/app/commands/setup_folder.py +++ b/app/commands/setup_folder.py @@ -5,6 +5,7 @@ from app.commands.check.git import git from app.commands.progress.constants import PROGRESS_LOCAL_FOLDER_NAME +from app.configs.gitmastery_config import GITMASTERY_FOLDER_NAME from app.utils.click import error, info, invoke_command, prompt @@ -35,8 +36,9 @@ def setup() -> None: os.chdir(directory_path) info("Setting up your local progress tracker...") + os.makedirs(GITMASTERY_FOLDER_NAME, exist_ok=True) os.makedirs(PROGRESS_LOCAL_FOLDER_NAME, exist_ok=True) - with open(".gitmastery.json", "w") as gitmastery_file: + with open(f"{GITMASTERY_FOLDER_NAME}/config.json", "w") as gitmastery_file: gitmastery_file.write( json.dumps( { diff --git a/app/configs/gitmastery_config.py b/app/configs/gitmastery_config.py index 6a3c0c4..550caad 100644 --- a/app/configs/gitmastery_config.py +++ b/app/configs/gitmastery_config.py @@ -1,11 +1,12 @@ import json from dataclasses import dataclass from pathlib import Path -from typing import Self, Type +from typing import Optional, Self, Type -from app.configs.utils import read_config +from app.configs.utils import find_root, read_config -GITMASTERY_CONFIG_NAME = ".gitmastery.json" +GITMASTERY_FOLDER_NAME = ".gitmastery" +GITMASTERY_CONFIG_NAME = "config.json" @dataclass @@ -36,12 +37,12 @@ def to_json(self) -> str: ) def write(self) -> None: - with open(self.path / GITMASTERY_CONFIG_NAME, "w") as exercise_config_file: + with open(self.path / GITMASTERY_FOLDER_NAME / GITMASTERY_CONFIG_NAME, "w") as exercise_config_file: exercise_config_file.write(self.to_json()) @classmethod def read(cls: Type[Self], path: Path, cds: int) -> Self: - raw_config = read_config(path, GITMASTERY_CONFIG_NAME) + raw_config = read_config(path / GITMASTERY_FOLDER_NAME, GITMASTERY_CONFIG_NAME) exercises_source_raw = raw_config.get("exercises_source", {}) return cls( @@ -60,3 +61,26 @@ def read(cls: Type[Self], path: Path, cds: int) -> Self: GIT_MASTERY_EXERCISES_SOURCE = GitMasteryConfig.ExercisesSource( username="git-mastery", repository="exercises", branch="main" ) + + +def migrate_to_gitmastery_folder() -> Optional[Path]: + """ + If old-style layout is detected (.gitmastery.json at root, no .gitmastery/ folder), + migrate files into the new .gitmastery/ folder convention. + Returns the root path if migration occurred, otherwise None. + """ + old_root = find_root(".gitmastery.json") + if old_root is None: + return None + + root, _ = old_root + new_dir = root / GITMASTERY_FOLDER_NAME + if new_dir.exists(): + return None + + new_dir.mkdir() + (root / ".gitmastery.json").rename(new_dir / GITMASTERY_CONFIG_NAME) + old_log = root / ".gitmastery.log" + if old_log.exists(): + old_log.rename(new_dir / "gitmastery.log") + return root diff --git a/app/hooks/in_gitmastery_root.py b/app/hooks/in_gitmastery_root.py index 3b91e54..1a5d023 100644 --- a/app/hooks/in_gitmastery_root.py +++ b/app/hooks/in_gitmastery_root.py @@ -3,7 +3,7 @@ import click -from app.configs.gitmastery_config import GITMASTERY_CONFIG_NAME, GitMasteryConfig +from app.configs.gitmastery_config import GITMASTERY_CONFIG_NAME, GITMASTERY_FOLDER_NAME, GitMasteryConfig from app.configs.utils import find_root from app.hooks.utils import generate_cds_string from app.utils.click import CliContextKey, error @@ -18,7 +18,7 @@ def decorator(func: Callable[..., Any]) -> Callable[..., Any]: def wrapper( ctx: click.Context, *args: Tuple[Any, ...], **kwargs: Dict[str, Any] ) -> Any: - root = find_root(GITMASTERY_CONFIG_NAME) + root = find_root(f"{GITMASTERY_FOLDER_NAME}/{GITMASTERY_CONFIG_NAME}") if root is None: error( f"You are not in a Git-Mastery root folder. Navigate to an appropriate folder or use " diff --git a/app/logging/setup_logging.py b/app/logging/setup_logging.py index cfd70ca..02aa277 100644 --- a/app/logging/setup_logging.py +++ b/app/logging/setup_logging.py @@ -1,7 +1,7 @@ import logging import re -from app.configs.gitmastery_config import GITMASTERY_CONFIG_NAME +from app.configs.gitmastery_config import GITMASTERY_CONFIG_NAME, GITMASTERY_FOLDER_NAME from app.configs.utils import find_root @@ -10,11 +10,11 @@ def __init__(self) -> None: super().__init__() def emit(self, record: logging.LogRecord) -> None: - gitmastery_root = find_root(GITMASTERY_CONFIG_NAME) + gitmastery_root = find_root(f"{GITMASTERY_FOLDER_NAME}/{GITMASTERY_CONFIG_NAME}") if gitmastery_root is None: return - log_path = gitmastery_root[0] / ".gitmastery.log" + log_path = gitmastery_root[0] / GITMASTERY_FOLDER_NAME / "gitmastery.log" handler = logging.FileHandler(log_path, mode="a") # TODO: This feels inefficient for logging but I can't think of a good # alternative diff --git a/tests/e2e/commands/test_setup.py b/tests/e2e/commands/test_setup.py index ea19f50..343e54a 100644 --- a/tests/e2e/commands/test_setup.py +++ b/tests/e2e/commands/test_setup.py @@ -3,7 +3,7 @@ def test_setup(exercises_dir: Path) -> None: """ - Test that setup creates the progress directory, progress.json, .gitmastery.json and .gitmastery.log + Test that setup creates the progress directory, progress.json, .gitmastery/config.json and .gitmastery/gitmastery.log Setup command already called in conftest.py for test setup """ progress_dir = exercises_dir / "progress" @@ -12,8 +12,8 @@ def test_setup(exercises_dir: Path) -> None: progress_file = progress_dir / "progress.json" assert progress_file.is_file(), f"Expected {progress_file} to exist" - config_file = exercises_dir / ".gitmastery.json" + config_file = exercises_dir / ".gitmastery" / "config.json" assert config_file.is_file(), f"Expected {config_file} to exist" - log_file = exercises_dir / ".gitmastery.log" + log_file = exercises_dir / ".gitmastery" / "gitmastery.log" assert log_file.is_file(), f"Expected {log_file} to exist"