hatch-calvar-sample 2026.1.19.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ # SPDX-FileCopyrightText: 2026-present QAToolist <qatoolist@gmail.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Version metadata for hatch_calvar_sample package."""
5
+
6
+ # Import version function with fallback for Python < 3.8
7
+ try:
8
+ from importlib.metadata import version as _version_func
9
+ except ImportError:
10
+ # Python < 3.8
11
+ from importlib_metadata import (
12
+ version as _version_func, # type: ignore[no-untyped-call]
13
+ )
14
+
15
+
16
+ def get_package_version(package_name: str) -> str:
17
+ """Get version from package metadata."""
18
+ return _version_func(package_name)
19
+
20
+
21
+ # Try to get version from package metadata (when installed)
22
+ try:
23
+ __version__ = get_package_version("hatch-calvar-sample")
24
+ except Exception:
25
+ # Fallback: try reading from VERSION file (for development builds)
26
+ import os
27
+ from pathlib import Path
28
+
29
+ version_file = Path(__file__).parent / "VERSION"
30
+ if version_file.exists():
31
+ __version__ = version_file.read_text().strip()
32
+ else:
33
+ # Last resort: read from environment variable
34
+ __version__ = os.environ.get("HATCH_CALVER_VERSION", "0.0.0.dev0")
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2026-present QAToolist <qatoolist@gmail.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Sample hatch project demonstrating CalVer (YYYY.MM.DD.MICRO) with hatch."""
5
+
6
+ from hatch_calvar_sample.__about__ import __version__
7
+
8
+ __all__ = ["__version__"]
@@ -0,0 +1,313 @@
1
+ """CLI tool for CalVer version management."""
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ # Import version function with fallback for Python < 3.8
10
+ try:
11
+ from importlib.metadata import version as _version_func
12
+ except ImportError:
13
+ # Python < 3.8
14
+ from importlib_metadata import (
15
+ version as _version_func, # type: ignore[no-untyped-call]
16
+ )
17
+
18
+
19
+ # Import version calculation functions from script
20
+ # We'll need to make these importable or duplicate the logic
21
+ # For now, we'll import from the script directory
22
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent / "scripts"))
23
+ try:
24
+ from calc_version import (
25
+ calculate_next_version,
26
+ check_pep440_compliance,
27
+ get_git_tags,
28
+ parse_calver_tag,
29
+ validate_version_format,
30
+ )
31
+ except ImportError:
32
+ # Fallback if script not available
33
+ def calculate_next_version():
34
+ return "0.0.0.0" # nosec B104
35
+
36
+ def validate_version_format(version: str) -> bool:
37
+ return bool(version)
38
+
39
+ def check_pep440_compliance(version: str) -> bool:
40
+ return True
41
+
42
+ def parse_calver_tag(tag: str):
43
+ return None
44
+
45
+ def get_git_tags():
46
+ return []
47
+
48
+
49
+ def get_package_version_from_metadata() -> Optional[str]:
50
+ """Get version from installed package metadata.
51
+
52
+ Returns:
53
+ Version string or None if not available
54
+ """
55
+ try:
56
+ return _version_func("hatch-calvar-sample")
57
+ except Exception:
58
+ return None
59
+
60
+
61
+ def version_calc(args: argparse.Namespace) -> int:
62
+ """Calculate next version.
63
+
64
+ Args:
65
+ args: Parsed command-line arguments
66
+
67
+ Returns:
68
+ Exit code (0 for success, 1 for error)
69
+ """
70
+ try:
71
+ version = calculate_next_version()
72
+ except Exception as e:
73
+ print(f"Error calculating version: {e}", file=sys.stderr)
74
+ return 1
75
+
76
+ if args.json:
77
+ output = {"version": version}
78
+ print(json.dumps(output))
79
+ else:
80
+ print(version)
81
+
82
+ return 0
83
+
84
+
85
+ def version_check(args: argparse.Namespace) -> int:
86
+ """Check current version from different sources.
87
+
88
+ Args:
89
+ args: Parsed command-line arguments
90
+
91
+ Returns:
92
+ Exit code (0 for success, 1 for error)
93
+ """
94
+ versions = {}
95
+
96
+ # Check package metadata
97
+ pkg_version = get_package_version_from_metadata()
98
+ if pkg_version:
99
+ versions["package"] = pkg_version
100
+
101
+ # Check git tags
102
+ git_tags = get_git_tags()
103
+ calver_tags = [tag for tag in git_tags if parse_calver_tag(tag)]
104
+ if calver_tags:
105
+ # Get latest CalVer tag
106
+ parsed_tags = [(tag, parse_calver_tag(tag)) for tag in calver_tags]
107
+ parsed_tags.sort(key=lambda x: x[1], reverse=True) # Sort by date/micro
108
+ latest_tag = parsed_tags[0][0]
109
+ # Remove 'v' prefix if present
110
+ if latest_tag.startswith("v"):
111
+ versions["git_tag"] = latest_tag[1:]
112
+ else:
113
+ versions["git_tag"] = latest_tag
114
+
115
+ # Check VERSION file
116
+ version_file = (
117
+ Path(__file__).parent.parent.parent / "src" / "hatch_calvar_sample" / "VERSION"
118
+ )
119
+ if version_file.exists():
120
+ versions["file"] = version_file.read_text().strip()
121
+
122
+ if args.json:
123
+ output = {"versions": versions}
124
+ print(json.dumps(output, indent=2))
125
+ else:
126
+ if versions:
127
+ print("Current versions:")
128
+ for source, ver in versions.items():
129
+ print(f" {source}: {ver}")
130
+ else:
131
+ print("No version information found", file=sys.stderr)
132
+ return 1
133
+
134
+ return 0
135
+
136
+
137
+ def version_validate(args: argparse.Namespace) -> int:
138
+ """Validate version format.
139
+
140
+ Args:
141
+ args: Parsed command-line arguments
142
+
143
+ Returns:
144
+ Exit code (0 for valid, 1 for invalid)
145
+ """
146
+ if not args.version:
147
+ print("Error: version argument required", file=sys.stderr)
148
+ return 1
149
+
150
+ version = args.version
151
+
152
+ # Validate format
153
+ is_valid_format = validate_version_format(version)
154
+
155
+ # Check PEP 440 compliance
156
+ is_pep440 = False
157
+ if is_valid_format:
158
+ is_pep440 = check_pep440_compliance(version)
159
+
160
+ if args.json:
161
+ output = {
162
+ "version": version,
163
+ "valid_format": is_valid_format,
164
+ "pep440_compliant": is_pep440,
165
+ }
166
+ print(json.dumps(output))
167
+ return 0 if (is_valid_format and is_pep440) else 1
168
+ else:
169
+ if not is_valid_format:
170
+ print(f"Invalid CalVer format: {version}", file=sys.stderr)
171
+ return 1
172
+
173
+ if not is_pep440:
174
+ print(f"Version not PEP 440 compliant: {version}", file=sys.stderr)
175
+ return 1
176
+
177
+ print(f"Version '{version}' is valid and PEP 440 compliant")
178
+ return 0
179
+
180
+
181
+ def version_compare(args: argparse.Namespace) -> int:
182
+ """Compare two versions.
183
+
184
+ Args:
185
+ args: Parsed command-line arguments
186
+
187
+ Returns:
188
+ Exit code (0 for success, 1 for error)
189
+ """
190
+ if len(args.versions) != 2:
191
+ print("Error: exactly two versions required for comparison", file=sys.stderr)
192
+ return 1
193
+
194
+ v1_str, v2_str = args.versions
195
+
196
+ # Parse versions
197
+ v1 = parse_calver_tag(v1_str)
198
+ v2 = parse_calver_tag(v2_str)
199
+
200
+ if not v1:
201
+ print(f"Error: invalid version format: {v1_str}", file=sys.stderr)
202
+ return 1
203
+
204
+ if not v2:
205
+ print(f"Error: invalid version format: {v2_str}", file=sys.stderr)
206
+ return 1
207
+
208
+ # Compare (year, month, day, micro)
209
+ if v1 < v2:
210
+ result = "<"
211
+ elif v1 > v2:
212
+ result = ">"
213
+ else:
214
+ result = "=="
215
+
216
+ if args.json:
217
+ output = {
218
+ "version1": v1_str,
219
+ "version2": v2_str,
220
+ "comparison": result,
221
+ }
222
+ print(json.dumps(output))
223
+ else:
224
+ print(f"{v1_str} {result} {v2_str}")
225
+
226
+ return 0
227
+
228
+
229
+ def version_info(args: argparse.Namespace) -> int:
230
+ """Show version information.
231
+
232
+ Args:
233
+ args: Parsed command-line arguments
234
+
235
+ Returns:
236
+ Exit code (0 for success, 1 for error)
237
+ """
238
+ info = {}
239
+
240
+ # Get next version
241
+ try:
242
+ next_version = calculate_next_version()
243
+ parsed = parse_calver_tag(next_version)
244
+ if parsed:
245
+ year, month, day, micro = parsed
246
+ info["next_version"] = next_version
247
+ info["date"] = f"{year:04d}-{month:02d}-{day:02d}"
248
+ info["micro"] = micro
249
+ except Exception as e:
250
+ info["next_version_error"] = str(e)
251
+
252
+ # Get current package version
253
+ pkg_version = get_package_version_from_metadata()
254
+ if pkg_version:
255
+ info["current_package_version"] = pkg_version
256
+
257
+ if args.json:
258
+ print(json.dumps(info, indent=2))
259
+ else:
260
+ print("Version Information:")
261
+ for key, value in info.items():
262
+ print(f" {key}: {value}")
263
+
264
+ return 0
265
+
266
+
267
+ def main():
268
+ """Main CLI entry point."""
269
+ parser = argparse.ArgumentParser(
270
+ description="CalVer version management CLI", prog="calver-check"
271
+ )
272
+ parser.add_argument("--json", action="store_true", help="Output in JSON format")
273
+
274
+ subparsers = parser.add_subparsers(dest="command", help="Command")
275
+
276
+ # calc command
277
+ calc_parser = subparsers.add_parser("calc", help="Calculate next CalVer version")
278
+ calc_parser.set_defaults(func=version_calc)
279
+
280
+ # check command
281
+ check_parser = subparsers.add_parser(
282
+ "check", help="Check current version from different sources"
283
+ )
284
+ check_parser.set_defaults(func=version_check)
285
+
286
+ # validate command
287
+ validate_parser = subparsers.add_parser(
288
+ "validate", help="Validate version format and PEP 440 compliance"
289
+ )
290
+ validate_parser.add_argument("version", help="Version string to validate")
291
+ validate_parser.set_defaults(func=version_validate)
292
+
293
+ # compare command
294
+ compare_parser = subparsers.add_parser("compare", help="Compare two versions")
295
+ compare_parser.add_argument("versions", nargs=2, help="Two versions to compare")
296
+ compare_parser.set_defaults(func=version_compare)
297
+
298
+ # info command
299
+ info_parser = subparsers.add_parser("info", help="Show version information")
300
+ info_parser.set_defaults(func=version_info)
301
+
302
+ args = parser.parse_args()
303
+
304
+ if not args.command:
305
+ parser.print_help()
306
+ return 1
307
+
308
+ # Call the appropriate function
309
+ return args.func(args)
310
+
311
+
312
+ if __name__ == "__main__":
313
+ sys.exit(main())
@@ -0,0 +1,421 @@
1
+ Metadata-Version: 2.4
2
+ Name: hatch-calvar-sample
3
+ Version: 2026.1.19.1
4
+ Summary: Sample project demonstrating CalVer (YYYY.MM.DD.MICRO) with hatch
5
+ Project-URL: Documentation, https://github.com/QAToolist/hatch-calvar-sample#readme
6
+ Project-URL: Issues, https://github.com/QAToolist/hatch-calvar-sample/issues
7
+ Project-URL: Source, https://github.com/QAToolist/hatch-calvar-sample
8
+ Author-email: QAToolist <qatoolist@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.txt
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: Implementation :: CPython
19
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
20
+ Requires-Python: >=3.8
21
+ Provides-Extra: dev
22
+ Requires-Dist: bandit>=1.7.0; extra == 'dev'
23
+ Requires-Dist: black>=24.1.0; extra == 'dev'
24
+ Requires-Dist: isort>=5.13.0; extra == 'dev'
25
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
26
+ Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
27
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
30
+ Provides-Extra: lint
31
+ Requires-Dist: bandit>=1.7.0; extra == 'lint'
32
+ Requires-Dist: black>=24.1.0; extra == 'lint'
33
+ Requires-Dist: isort>=5.13.0; extra == 'lint'
34
+ Requires-Dist: mypy>=1.0.0; extra == 'lint'
35
+ Requires-Dist: pre-commit>=3.0.0; extra == 'lint'
36
+ Requires-Dist: ruff>=0.1.0; extra == 'lint'
37
+ Provides-Extra: test
38
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
39
+ Requires-Dist: pytest>=7.0.0; extra == 'test'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # hatch-calvar-sample
43
+
44
+ [![PyPI - Version](https://img.shields.io/pypi/v/hatch-calvar-sample.svg)](https://pypi.org/project/hatch-calvar-sample)
45
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/hatch-calvar-sample.svg)](https://pypi.org/project/hatch-calvar-sample)
46
+
47
+ Sample hatch-based Python project demonstrating **Calendar Versioning (CalVer)** with format `YYYY.MM.DD.MICRO` and automated PyPI release workflows.
48
+
49
+ ## Overview
50
+
51
+ This project serves as a proof-of-concept for implementing CalVer versioning with the hatch build system. It demonstrates:
52
+
53
+ - **Calendar Versioning (YYYY.MM.DD.MICRO)** calculated from git tags
54
+ - **Dynamic versioning** with hatch build system
55
+ - **Version checking CLI tool** with multiple commands
56
+ - **Automated PyPI release** via GitHub Actions
57
+ - **Complete release workflow** automation
58
+
59
+ ### Version Format
60
+
61
+ The project uses CalVer format: `YYYY.MM.DD.MICRO`
62
+
63
+ - `YYYY` - 4-digit year (e.g., 2024)
64
+ - `MM` - 2-digit month (01-12)
65
+ - `DD` - 2-digit day (01-31)
66
+ - `MICRO` - Sequential number for releases on the same day (1, 2, 3, ...)
67
+
68
+ Examples: `2024.01.18.1`, `2024.01.18.2`, `2024.03.15.1`
69
+
70
+ ## Installation
71
+
72
+ ```console
73
+ pip install hatch-calvar-sample
74
+ ```
75
+
76
+ ## Features
77
+
78
+ ### Version Calculation
79
+
80
+ The project includes a script that automatically calculates the next CalVer version based on:
81
+ - Current UTC date
82
+ - Existing git tags matching the CalVer pattern
83
+ - Automatic MICRO increment for same-day releases
84
+
85
+ ### CLI Tool
86
+
87
+ The `calver-check` CLI provides multiple commands for version management:
88
+
89
+ ```bash
90
+ # Calculate next version
91
+ calver-check calc
92
+
93
+ # Check current version from different sources
94
+ calver-check check
95
+
96
+ # Validate version format
97
+ calver-check validate 2024.01.18.1
98
+
99
+ # Compare two versions
100
+ calver-check compare 2024.01.18.1 2024.01.18.2
101
+
102
+ # Show version information
103
+ calver-check info
104
+ ```
105
+
106
+ All commands support `--json` flag for machine-readable output:
107
+
108
+ ```bash
109
+ calver-check calc --json
110
+ ```
111
+
112
+ ## Usage Examples
113
+
114
+ ### Calculating Next Version
115
+
116
+ ```bash
117
+ # Using the script directly
118
+ python scripts/calc_version.py
119
+
120
+ # Using the CLI tool
121
+ calver-check calc
122
+
123
+ # With validation
124
+ python scripts/calc_version.py --validate --pep440
125
+ ```
126
+
127
+ ### Checking Current Version
128
+
129
+ ```bash
130
+ # Check version from package metadata
131
+ calver-check check
132
+
133
+ # Check with JSON output
134
+ calver-check check --json
135
+ ```
136
+
137
+ ### Validating Version Format
138
+
139
+ ```bash
140
+ # Validate a version string
141
+ calver-check validate 2024.01.18.1
142
+
143
+ # Invalid version will exit with error
144
+ calver-check validate 2024.1.18.1
145
+ ```
146
+
147
+ ### Comparing Versions
148
+
149
+ ```bash
150
+ # Compare two versions
151
+ calver-check compare 2024.01.18.1 2024.01.18.2
152
+ # Output: 2024.01.18.1 < 2024.01.18.2
153
+ ```
154
+
155
+ ## Release Process
156
+
157
+ ### Fully Automated Release Workflow
158
+
159
+ The project uses GitHub Actions for **fully automated** PyPI releases. **No manual tagging required!** The workflow consists of two stages:
160
+
161
+ #### Stage 1: Auto-tag on PR Merge
162
+
163
+ When a pull request is merged to `main` or `master`:
164
+
165
+ 1. Automatically calculates next CalVer version using `scripts/calc_version.py`
166
+ 2. Creates a git tag with format `vYYYY.MM.DD.MICRO`
167
+ 3. Pushes the tag to the repository
168
+
169
+ #### Stage 2: Build and Publish
170
+
171
+ When a git tag matching `v*` is pushed:
172
+
173
+ 1. Extracts version from tag (strips `v` prefix)
174
+ 2. Validates CalVer format
175
+ 3. Builds package with hatch
176
+ 4. Validates distributions with twine
177
+ 5. Publishes to PyPI using Trusted Publishing
178
+
179
+ ### Automated Release Steps
180
+
181
+ 1. **Open a pull request** with your changes
182
+ 2. **Merge the pull request** to `main`/`master`
183
+ 3. **GitHub Actions automatically:**
184
+ - Calculates next CalVer version (e.g., `2024.01.18.1`)
185
+ - Creates tag `v2024.01.18.1`
186
+ - Builds the package
187
+ - Validates version format
188
+ - Publishes to PyPI
189
+
190
+ No manual tagging required! The release happens automatically when PRs are merged.
191
+
192
+ ### Manual Release (Optional)
193
+
194
+ If you need to manually create a release tag (for hotfixes, etc.):
195
+
196
+ 1. **Calculate next version:**
197
+ ```bash
198
+ calver-check calc
199
+ # Output: 2024.01.18.1
200
+ ```
201
+
202
+ 2. **Create and push git tag:**
203
+ ```bash
204
+ git tag v2024.01.18.1 -m "Release 2024.01.18.1"
205
+ git push origin v2024.01.18.1
206
+ ```
207
+
208
+ The tag push will trigger the same build and publish workflow.
209
+
210
+ ### Using Makefile
211
+
212
+ For convenience, use the Makefile targets:
213
+
214
+ ```bash
215
+ # Calculate next version
216
+ make version-calc
217
+
218
+ # Check current version
219
+ make version-check
220
+
221
+ # Validate version format
222
+ make version-validate VERSION=2024.01.18.1
223
+
224
+ # Create and push release tag
225
+ make release-tag
226
+
227
+ # Build package for testing
228
+ make build-test
229
+ ```
230
+
231
+ ## Project Structure
232
+
233
+ ```
234
+ hatch-calvar-sample/
235
+ ├── pyproject.toml # Hatch configuration with dynamic versioning
236
+ ├── README.md # This file
237
+ ├── CHANGELOG.md # Changelog with CalVer format
238
+ ├── LICENSE.txt # MIT license
239
+ ├── Makefile # Convenient make targets
240
+ ├── .github/
241
+ │ └── workflows/
242
+ │ ├── auto-tag.yml # Auto-create tag on PR merge
243
+ │ └── release.yml # Automated PyPI release workflow
244
+ ├── scripts/
245
+ │ └── calc_version.py # Version calculation script
246
+ ├── src/
247
+ │ └── hatch_calvar_sample/
248
+ │ ├── __init__.py # Package with __version__
249
+ │ ├── __about__.py # Version metadata
250
+ │ ├── VERSION # Version file (generated during build)
251
+ │ └── cli.py # Version checking CLI implementation
252
+ └── tests/
253
+ ├── test_version_calc.py # Tests for version calculation
254
+ └── test_version_cli.py # Tests for CLI tool
255
+ ```
256
+
257
+ ## Configuration
258
+
259
+ ### Dynamic Versioning
260
+
261
+ The project uses hatch's dynamic versioning feature configured in `pyproject.toml`:
262
+
263
+ ```toml
264
+ [tool.hatch.version]
265
+ path = "src/hatch_calvar_sample/VERSION"
266
+ ```
267
+
268
+ The VERSION file is created during the release workflow with the version extracted from the git tag.
269
+
270
+ ### Version Metadata in Code
271
+
272
+ The package reads version from `importlib.metadata` when installed, with fallbacks:
273
+
274
+ 1. Package metadata (when installed)
275
+ 2. VERSION file (for development builds)
276
+ 3. Environment variable `HATCH_CALVER_VERSION`
277
+
278
+ Access version programmatically:
279
+
280
+ ```python
281
+ from hatch_calvar_sample import __version__
282
+
283
+ print(__version__) # Output: 2024.01.18.1
284
+ ```
285
+
286
+ ## Development
287
+
288
+ ### Setting Up Development Environment
289
+
290
+ ```bash
291
+ # Clone the repository
292
+ git clone https://github.com/QAToolist/hatch-calvar-sample.git
293
+ cd hatch-calvar-sample
294
+
295
+ # Install in development mode
296
+ pip install -e .
297
+
298
+ # Install development dependencies
299
+ pip install pytest
300
+ ```
301
+
302
+ ### Running Tests
303
+
304
+ ```bash
305
+ # Run all tests
306
+ pytest
307
+
308
+ # Run specific test file
309
+ pytest tests/test_version_calc.py
310
+
311
+ # Run with coverage
312
+ pytest --cov=hatch_calvar_sample --cov=scripts
313
+ ```
314
+
315
+ ### Local Testing
316
+
317
+ 1. **Test version calculation:**
318
+ ```bash
319
+ python scripts/calc_version.py
320
+ ```
321
+
322
+ 2. **Test build process:**
323
+ ```bash
324
+ # Create VERSION file manually
325
+ echo "2024.01.18.1" > src/hatch_calvar_sample/VERSION
326
+
327
+ # Build package
328
+ hatch build
329
+
330
+ # Check built package
331
+ twine check dist/*
332
+ ```
333
+
334
+ 3. **Test installation:**
335
+ ```bash
336
+ pip install -e .
337
+ python -c "import hatch_calvar_sample; print(hatch_calvar_sample.__version__)"
338
+ ```
339
+
340
+ ## Version Calculation Logic
341
+
342
+ The version calculation script:
343
+
344
+ 1. Gets current UTC date → `YYYY.MM.DD`
345
+ 2. Fetches all git tags (`git fetch --tags`)
346
+ 3. Parses tags matching CalVer pattern (`YYYY.MM.DD.MICRO` or `vYYYY.MM.DD.MICRO`)
347
+ 4. Filters tags with the same date
348
+ 5. Extracts MICRO numbers
349
+ 6. Calculates next MICRO = `max(existing) + 1` or `1` if none exist
350
+ 7. Returns: `YYYY.MM.DD.MICRO`
351
+
352
+ ### Edge Cases Handled
353
+
354
+ - No tags → Returns `YYYY.MM.DD.1`
355
+ - Multiple tags same date → Increments MICRO correctly
356
+ - Date boundary crossing → Resets MICRO to 1 for new date
357
+ - Invalid tag formats → Skipped gracefully
358
+ - Timezone handling → Uses UTC for consistency
359
+
360
+ ## PEP 440 Compliance
361
+
362
+ CalVer format `YYYY.MM.DD.MICRO` is PEP 440 compliant as a release segment. The format:
363
+
364
+ - Uses numeric components
365
+ - Follows semantic ordering (newer dates > older dates)
366
+ - Valid for PyPI distribution
367
+
368
+ Validate PEP 440 compliance:
369
+
370
+ ```bash
371
+ calver-check validate 2024.01.18.1
372
+ python scripts/calc_version.py --pep440
373
+ ```
374
+
375
+ ## Troubleshooting
376
+
377
+ ### Version Not Found
378
+
379
+ If `__version__` is not available:
380
+
381
+ 1. Ensure package is installed: `pip install -e .`
382
+ 2. Check VERSION file exists: `ls src/hatch_calvar_sample/VERSION`
383
+ 3. Verify git tags: `git tag`
384
+
385
+ ### Build Errors
386
+
387
+ If build fails:
388
+
389
+ 1. Verify VERSION file exists with valid format
390
+ 2. Check `pyproject.toml` dynamic version configuration
391
+ 3. Ensure hatch is installed: `pip install hatchling`
392
+
393
+ ### CLI Not Found
394
+
395
+ If `calver-check` command is not available:
396
+
397
+ 1. Reinstall package: `pip install -e .`
398
+ 2. Check entry point in `pyproject.toml`
399
+ 3. Verify PATH includes Python scripts directory
400
+
401
+ ## Contributing
402
+
403
+ This is a sample project for demonstration purposes. If you find it useful or want to adapt it for your project:
404
+
405
+ 1. Review the implementation details in scripts and source code
406
+ 2. Check the GitHub Actions workflow for CI/CD patterns
407
+ 3. Adapt the configuration for your project structure
408
+
409
+ ## License
410
+
411
+ `hatch-calvar-sample` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
412
+
413
+ ## References
414
+
415
+ - [Hatch Documentation](https://hatch.pypa.io/)
416
+ - [Calendar Versioning (CalVer)](https://calver.org/)
417
+ - [PEP 440 - Version Identification](https://peps.python.org/pep-0440/)
418
+ - [GitHub Actions](https://docs.github.com/en/actions)
419
+
420
+
421
+ ## Demo Change
@@ -0,0 +1,8 @@
1
+ hatch_calvar_sample/__about__.py,sha256=xlD9nPpC2pVTXj7SnqL2D4Ii8Hx5PdoszDaQ-scX_m8,1107
2
+ hatch_calvar_sample/__init__.py,sha256=Zo7rNhiPjw6aA00bIGyizV2ke8xv24cjV3C2K6MfPDI,265
3
+ hatch_calvar_sample/cli.py,sha256=J3wnxje3vnx1Ep2YlPyBlkTBo4eUmBb8zesCHbZFj_k,8394
4
+ hatch_calvar_sample-2026.1.19.1.dist-info/METADATA,sha256=STWKCFQb3Hn8T9vfDZym1L2vs7ZuGzGgXTgoNXmscE8,11396
5
+ hatch_calvar_sample-2026.1.19.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
+ hatch_calvar_sample-2026.1.19.1.dist-info/entry_points.txt,sha256=vXoWTt7vuY493G-1mbwHnSSVt_ixdRvCZl2pHxszDjE,62
7
+ hatch_calvar_sample-2026.1.19.1.dist-info/licenses/LICENSE.txt,sha256=mTqa2fQvFlr6XRReIRjiWIxWJ43FPXEV2mhd8o_HL4k,1096
8
+ hatch_calvar_sample-2026.1.19.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ calver-check = hatch_calvar_sample.cli:main
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026-present QAToolist <qatoolist@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.