From c3d279813742362b1acfc3ba01815fcd4f4a0428 Mon Sep 17 00:00:00 2001 From: LittleCoinCoin Date: Thu, 2 Oct 2025 19:18:49 +0900 Subject: [PATCH 1/4] test: add version command test suite Add comprehensive test suite for hatch --version command implementation: - test_get_hatch_version_retrieves_from_metadata: Unit test for version retrieval - test_get_hatch_version_handles_package_not_found: Unit test for error handling - test_version_command_displays_correct_format: Integration test for CLI flow - test_import_hatch_without_version_attribute: Integration test for import safety - test_no_conflict_with_package_version_flag: Regression test for flag conflicts Tests follow CrackingShells testing standards using wobble framework. All 5 tests pass with 100% pass rate. Related to: Phase 2 test definition (test_definition_v1.md) --- tests/test_cli_version.py | 122 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/test_cli_version.py diff --git a/tests/test_cli_version.py b/tests/test_cli_version.py new file mode 100644 index 0000000..43d4361 --- /dev/null +++ b/tests/test_cli_version.py @@ -0,0 +1,122 @@ +""" +Test suite for hatch --version command implementation. + +This module tests the version command functionality: +- Version retrieval from importlib.metadata +- Error handling for PackageNotFoundError +- CLI version display format +- Import safety after removing __version__ +- No conflicts with existing flags + +Tests follow CrackingShells testing standards using wobble framework. +""" + +import unittest +import sys +from pathlib import Path +from unittest.mock import patch, MagicMock +from io import StringIO + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from hatch.cli_hatch import main, get_hatch_version + +try: + from wobble.decorators import regression_test, integration_test +except ImportError: + # Fallback decorators if wobble not available + def regression_test(func): + return func + + def integration_test(scope="component"): + def decorator(func): + return func + return decorator + + +class TestVersionCommand(unittest.TestCase): + """Test suite for hatch --version command implementation.""" + + @regression_test + def test_get_hatch_version_retrieves_from_metadata(self): + """Test get_hatch_version() retrieves version from importlib.metadata.""" + with patch('hatch.cli_hatch.version', return_value='0.7.0-dev.3') as mock_version: + result = get_hatch_version() + + self.assertEqual(result, '0.7.0-dev.3') + mock_version.assert_called_once_with('hatch') + + @regression_test + def test_get_hatch_version_handles_package_not_found(self): + """Test get_hatch_version() handles PackageNotFoundError gracefully.""" + from importlib.metadata import PackageNotFoundError + + with patch('hatch.cli_hatch.version', side_effect=PackageNotFoundError()): + result = get_hatch_version() + + self.assertEqual(result, 'unknown (development mode)') + + @integration_test(scope="component") + def test_version_command_displays_correct_format(self): + """Test version command displays correct format via CLI.""" + test_args = ['hatch', '--version'] + + with patch('sys.argv', test_args): + with patch('hatch.cli_hatch.get_hatch_version', return_value='0.7.0-dev.3'): + with patch('sys.stdout', new_callable=StringIO) as mock_stdout: + with self.assertRaises(SystemExit) as cm: + main() + + # argparse action='version' exits with code 0 + self.assertEqual(cm.exception.code, 0) + + # Verify output format: "hatch 0.7.0-dev.3" + output = mock_stdout.getvalue().strip() + self.assertRegex(output, r'hatch\s+0\.7\.0-dev\.3') + + @integration_test(scope="component") + def test_import_hatch_without_version_attribute(self): + """Test that importing hatch module works without __version__ attribute.""" + try: + import hatch + + # Import should succeed + self.assertIsNotNone(hatch) + + # __version__ should not exist (removed in implementation) + self.assertFalse(hasattr(hatch, '__version__'), + "hatch.__version__ should not exist after cleanup") + + except ImportError as e: + self.fail(f"Failed to import hatch module: {e}") + + @regression_test + def test_no_conflict_with_package_version_flag(self): + """Test that --version (Hatch) doesn't conflict with -v (package version).""" + # Test package add command with -v flag (package version specification) + test_args = ['hatch', 'package', 'add', 'test-package', '-v', '1.0.0'] + + with patch('sys.argv', test_args): + with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env: + mock_env_instance = MagicMock() + mock_env.return_value = mock_env_instance + mock_env_instance.add_package_to_environment.return_value = True + + try: + main() + except SystemExit as e: + # Should execute successfully (exit code 0) + self.assertEqual(e.code, 0) + + # Verify package add was called with version argument + mock_env_instance.add_package_to_environment.assert_called_once() + call_args = mock_env_instance.add_package_to_environment.call_args + + # Version argument should be '1.0.0' + self.assertEqual(call_args[0][2], '1.0.0') # Third positional arg is version + + +if __name__ == '__main__': + unittest.main() + From 9f0aad3684a794019aa1b6033ac4b9645a92d6af Mon Sep 17 00:00:00 2001 From: LittleCoinCoin Date: Thu, 2 Oct 2025 19:19:02 +0900 Subject: [PATCH 2/4] refactor: remove outdated __version__ from hatch/__init__.py Remove hardcoded version string (__version__ = '0.4.0') from hatch/__init__.py. This eliminates version duplication and establishes pyproject.toml as the single source of truth for version information. Version will now be retrieved dynamically via importlib.metadata.version('hatch'). Breaking change: Code that relied on hatch.__version__ will need to use importlib.metadata.version('hatch') instead. Related to: Phase 1 analysis (version_command_analysis_v1.md) --- hatch/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hatch/__init__.py b/hatch/__init__.py index 60c0aaa..e7f401b 100644 --- a/hatch/__init__.py +++ b/hatch/__init__.py @@ -5,8 +5,6 @@ and interacting with the Hatch registry. """ -__version__ = "0.4.0" - from .cli_hatch import main from .environment_manager import HatchEnvironmentManager from .package_loader import HatchPackageLoader, PackageLoaderError From d1a0e2dfb5963724294b3e0c84e0b7f96aefbe61 Mon Sep 17 00:00:00 2001 From: LittleCoinCoin Date: Thu, 2 Oct 2025 19:19:13 +0900 Subject: [PATCH 3/4] feat: add --version flag to CLI argument parser Implement hatch --version command to display application version: - Add importlib.metadata imports (version, PackageNotFoundError) - Add get_hatch_version() helper function with error handling - Add --version argument to main parser using argparse action='version' The version is retrieved dynamically from package metadata via importlib.metadata.version('hatch'). Falls back to 'unknown (development mode)' if package is not installed. Output format: 'hatch ' (e.g., 'hatch 0.6.1') No short form (-v) to avoid conflict with existing package version flag in subcommands. Related to: Phase 1 analysis (version_command_analysis_v1.md) --- hatch/cli_hatch.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hatch/cli_hatch.py b/hatch/cli_hatch.py index 6207e9d..0146cb6 100644 --- a/hatch/cli_hatch.py +++ b/hatch/cli_hatch.py @@ -13,6 +13,7 @@ import sys from pathlib import Path from typing import Optional, List +from importlib.metadata import version, PackageNotFoundError from hatch.environment_manager import HatchEnvironmentManager from hatch_validator import HatchPackageValidator @@ -20,6 +21,20 @@ from hatch.template_generator import create_package_template from hatch.mcp_host_config import MCPHostConfigurationManager, MCPHostType, MCPHostRegistry, MCPServerConfig + +def get_hatch_version() -> str: + """Get Hatch version from package metadata. + + Returns: + str: Version string from package metadata, or 'unknown (development mode)' + if package is not installed. + """ + try: + return version('hatch') + except PackageNotFoundError: + return 'unknown (development mode)' + + def parse_host_list(host_arg: str): """Parse comma-separated host list or 'all'.""" if not host_arg: @@ -944,6 +959,14 @@ def main(): # Create argument parser parser = argparse.ArgumentParser(description="Hatch package manager CLI") + + # Add version argument + parser.add_argument( + '--version', + action='version', + version=f'%(prog)s {get_hatch_version()}' + ) + subparsers = parser.add_subparsers(dest="command", help="Command to execute") # Create template command From ac326e0a5bed84f9ce8d38976cd9dbfafdc24685 Mon Sep 17 00:00:00 2001 From: LittleCoinCoin Date: Thu, 2 Oct 2025 20:13:58 +0900 Subject: [PATCH 4/4] docs: add --version flag documentation and installation verification Update documentation to include the new --version flag: - CLI Reference: Add --version to global options table with usage example - Getting Started: Update Step 3 verification to use 'hatch --version' first - Installation Tutorial: Add version check to installation verification steps - README: Add version check after installation instructions The --version flag provides a quick way to verify successful installation and check the installed Hatch version. Related to: feature/hatch-version-command --- README.md | 6 ++++++ docs/articles/users/CLIReference.md | 8 ++++++++ docs/articles/users/GettingStarted.md | 8 ++++++-- .../users/tutorials/01-getting-started/01-installation.md | 8 +++++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 000d493..18d784f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,12 @@ cd Hatch pip install -e . ``` +Verify installation: + +```bash +hatch --version +``` + ### Create a package template ```bash diff --git a/docs/articles/users/CLIReference.md b/docs/articles/users/CLIReference.md index 8e789db..d45f6d6 100644 --- a/docs/articles/users/CLIReference.md +++ b/docs/articles/users/CLIReference.md @@ -30,10 +30,18 @@ These flags are accepted by the top-level parser and apply to all commands unles | Flag | Type | Description | Default | |------|------|-------------|---------| +| `--version` | flag | Show program version and exit | n/a | | `--envs-dir` | path | Directory to store environments | `~/.hatch/envs` | | `--cache-ttl` | int | Cache time-to-live in seconds | `86400` (1 day) | | `--cache-dir` | path | Directory to store cached packages | `~/.hatch/cache` | +Example: + +```bash +hatch --version +# Output: hatch 0.6.1 +``` + ## Commands Each top-level command has its own table. Use the Syntax line before the table to see how to call it. diff --git a/docs/articles/users/GettingStarted.md b/docs/articles/users/GettingStarted.md index ea67713..509b787 100644 --- a/docs/articles/users/GettingStarted.md +++ b/docs/articles/users/GettingStarted.md @@ -105,10 +105,14 @@ pip install -e . Test that Hatch is working: ```bash -hatch --help +hatch --version ``` -You should see available commands. +You should see the installed version (e.g., `hatch 0.6.1`). You can also view available commands: + +```bash +hatch --help +``` ## First Steps diff --git a/docs/articles/users/tutorials/01-getting-started/01-installation.md b/docs/articles/users/tutorials/01-getting-started/01-installation.md index b6d091b..3ba2232 100644 --- a/docs/articles/users/tutorials/01-getting-started/01-installation.md +++ b/docs/articles/users/tutorials/01-getting-started/01-installation.md @@ -28,7 +28,13 @@ This article covers the installation of Hatch, a package manager for Model Conte pip install -e . ``` -3. Verify the installation by checking the available commands: +3. Verify the installation by checking the version: + + ```bash + hatch --version + ``` + + You should see output like `hatch 0.6.1`. You can also view available commands: ```bash hatch --help