hatch-calvar-sample 2026.1.19.2
Sample project demonstrating CalVer (YYYY.MM.DD.MICRO) with hatch
Requires Python: >=3.8

# hatch-calvar-sample

[![PyPI - Version](https://img.shields.io/pypi/v/hatch-calvar-sample.svg)](https://pypi.org/project/hatch-calvar-sample)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/hatch-calvar-sample.svg)](https://pypi.org/project/hatch-calvar-sample)

Sample hatch-based Python project demonstrating **Calendar Versioning (CalVer)** with format `YYYY.MM.DD.MICRO` and automated PyPI release workflows.

## Overview

This project serves as a proof-of-concept for implementing CalVer versioning with the hatch build system. It demonstrates:

- **Calendar Versioning (YYYY.MM.DD.MICRO)** calculated from git tags
- **Dynamic versioning** with hatch build system
- **Version checking CLI tool** with multiple commands
- **Automated PyPI release** via GitHub Actions
- **Complete release workflow** automation

### Version Format

The project uses CalVer format: `YYYY.MM.DD.MICRO`

- `YYYY` - 4-digit year (e.g., 2024)
- `MM` - 2-digit month (01-12)
- `DD` - 2-digit day (01-31)
- `MICRO` - Sequential number for releases on the same day (1, 2, 3, ...)

Examples: `2024.01.18.1`, `2024.01.18.2`, `2024.03.15.1`

## Installation

```console
pip install hatch-calvar-sample
```

## Features

### Version Calculation

The project includes a script that automatically calculates the next CalVer version based on:
- Current UTC date
- Existing git tags matching the CalVer pattern
- Automatic MICRO increment for same-day releases

### CLI Tool

The `calver-check` CLI provides multiple commands for version management:

```bash
# Calculate next version
calver-check calc

# Check current version from different sources
calver-check check

# Validate version format
calver-check validate 2024.01.18.1

# Compare two versions
calver-check compare 2024.01.18.1 2024.01.18.2

# Show version information
calver-check info
```

All commands support `--json` flag for machine-readable output:

```bash
calver-check calc --json
```

## Usage Examples

### Calculating Next Version

```bash
# Using the script directly
python scripts/calc_version.py

# Using the CLI tool
calver-check calc

# With validation
python scripts/calc_version.py --validate --pep440
```

### Checking Current Version

```bash
# Check version from package metadata
calver-check check

# Check with JSON output
calver-check check --json
```

### Validating Version Format

```bash
# Validate a version string
calver-check validate 2024.01.18.1

# Invalid version will exit with error
calver-check validate 2024.1.18.1
```

### Comparing Versions

```bash
# Compare two versions
calver-check compare 2024.01.18.1 2024.01.18.2
# Output: 2024.01.18.1 < 2024.01.18.2
```

## Release Process

### Fully Automated Release Workflow

The project uses GitHub Actions for **fully automated** PyPI releases. **No manual tagging required!** The workflow consists of two stages:

#### Stage 1: Auto-tag on PR Merge

When a pull request is merged to `main` or `master`:

1. Automatically calculates next CalVer version using `scripts/calc_version.py`
2. Creates a git tag with format `vYYYY.MM.DD.MICRO`
3. Pushes the tag to the repository

#### Stage 2: Build and Publish

When a git tag matching `v*` is pushed:

1. Extracts version from tag (strips `v` prefix)
2. Validates CalVer format
3. Builds package with hatch
4. Validates distributions with twine
5. Publishes to PyPI using Trusted Publishing

### Automated Release Steps

1. **Open a pull request** with your changes
2. **Merge the pull request** to `main`/`master`
3. **GitHub Actions automatically:**
   - Calculates next CalVer version (e.g., `2024.01.18.1`)
   - Creates tag `v2024.01.18.1`
   - Builds the package
   - Validates version format
   - Publishes to PyPI

No manual tagging required! The release happens automatically when PRs are merged.

### Manual Release (Optional)

If you need to manually create a release tag (for hotfixes, etc.):

1. **Calculate next version:**
   ```bash
   calver-check calc
   # Output: 2024.01.18.1
   ```

2. **Create and push git tag:**
   ```bash
   git tag v2024.01.18.1 -m "Release 2024.01.18.1"
   git push origin v2024.01.18.1
   ```

The tag push will trigger the same build and publish workflow.

### Using Makefile

For convenience, use the Makefile targets:

```bash
# Calculate next version
make version-calc

# Check current version
make version-check

# Validate version format
make version-validate VERSION=2024.01.18.1

# Create and push release tag
make release-tag

# Build package for testing
make build-test
```

## Project Structure

```
hatch-calvar-sample/
├── pyproject.toml              # Hatch configuration with dynamic versioning
├── README.md                   # This file
├── CHANGELOG.md                # Changelog with CalVer format
├── LICENSE.txt                 # MIT license
├── Makefile                    # Convenient make targets
├── .github/
│   └── workflows/
│       ├── auto-tag.yml        # Auto-create tag on PR merge
│       └── release.yml         # Automated PyPI release workflow
├── scripts/
│   └── calc_version.py         # Version calculation script
├── src/
│   └── hatch_calvar_sample/
│       ├── __init__.py         # Package with __version__
│       ├── __about__.py        # Version metadata
│       ├── VERSION             # Version file (generated during build)
│       └── cli.py              # Version checking CLI implementation
└── tests/
    ├── test_version_calc.py    # Tests for version calculation
    └── test_version_cli.py     # Tests for CLI tool
```

## Configuration

### Dynamic Versioning

The project uses hatch's dynamic versioning feature configured in `pyproject.toml`:

```toml
[tool.hatch.version]
path = "src/hatch_calvar_sample/VERSION"
```

The VERSION file is created during the release workflow with the version extracted from the git tag.

### Version Metadata in Code

The package reads version from `importlib.metadata` when installed, with fallbacks:

1. Package metadata (when installed)
2. VERSION file (for development builds)
3. Environment variable `HATCH_CALVER_VERSION`

Access version programmatically:

```python
from hatch_calvar_sample import __version__

print(__version__)  # Output: 2024.01.18.1
```

## Development

### Setting Up Development Environment

```bash
# Clone the repository
git clone https://github.com/QAToolist/hatch-calvar-sample.git
cd hatch-calvar-sample

# Install in development mode
pip install -e .

# Install development dependencies
pip install pytest
```

### Running Tests

```bash
# Run all tests
pytest

# Run specific test file
pytest tests/test_version_calc.py

# Run with coverage
pytest --cov=hatch_calvar_sample --cov=scripts
```

### Local Testing

1. **Test version calculation:**
   ```bash
   python scripts/calc_version.py
   ```

2. **Test build process:**
   ```bash
   # Create VERSION file manually
   echo "2024.01.18.1" > src/hatch_calvar_sample/VERSION

   # Build package
   hatch build

   # Check built package
   twine check dist/*
   ```

3. **Test installation:**
   ```bash
   pip install -e .
   python -c "import hatch_calvar_sample; print(hatch_calvar_sample.__version__)"
   ```

## Version Calculation Logic

The version calculation script:

1. Gets current UTC date → `YYYY.MM.DD`
2. Fetches all git tags (`git fetch --tags`)
3. Parses tags matching CalVer pattern (`YYYY.MM.DD.MICRO` or `vYYYY.MM.DD.MICRO`)
4. Filters tags with the same date
5. Extracts MICRO numbers
6. Calculates next MICRO = `max(existing) + 1` or `1` if none exist
7. Returns: `YYYY.MM.DD.MICRO`

### Edge Cases Handled

- No tags → Returns `YYYY.MM.DD.1`
- Multiple tags same date → Increments MICRO correctly
- Date boundary crossing → Resets MICRO to 1 for new date
- Invalid tag formats → Skipped gracefully
- Timezone handling → Uses UTC for consistency

## PEP 440 Compliance

CalVer format `YYYY.MM.DD.MICRO` is PEP 440 compliant as a release segment. The format:

- Uses numeric components
- Follows semantic ordering (newer dates > older dates)
- Valid for PyPI distribution

Validate PEP 440 compliance:

```bash
calver-check validate 2024.01.18.1
python scripts/calc_version.py --pep440
```

## Troubleshooting

### Version Not Found

If `__version__` is not available:

1. Ensure package is installed: `pip install -e .`
2. Check VERSION file exists: `ls src/hatch_calvar_sample/VERSION`
3. Verify git tags: `git tag`

### Build Errors

If build fails:

1. Verify VERSION file exists with valid format
2. Check `pyproject.toml` dynamic version configuration
3. Ensure hatch is installed: `pip install hatchling`

### CLI Not Found

If `calver-check` command is not available:

1. Reinstall package: `pip install -e .`
2. Check entry point in `pyproject.toml`
3. Verify PATH includes Python scripts directory

## Contributing

This is a sample project for demonstration purposes. If you find it useful or want to adapt it for your project:

1. Review the implementation details in scripts and source code
2. Check the GitHub Actions workflow for CI/CD patterns
3. Adapt the configuration for your project structure

## License

`hatch-calvar-sample` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

## References

- [Hatch Documentation](https://hatch.pypa.io/)
- [Calendar Versioning (CalVer)](https://calver.org/)
- [PEP 440 - Version Identification](https://peps.python.org/pep-0440/)
- [GitHub Actions](https://docs.github.com/en/actions)


## Demo Change