Skip to content

home / reference / versioning

Versioning

This document outlines Tux's Semantic Versioning 2.0.0 implementation, version detection system, and release process.

Semantic Versioning

Tux follows SemVer 2.0.0 with version format MAJOR.MINOR.PATCH:

  • MAJOR (X.y.z): Breaking API changes (command syntax, config formats, plugin interfaces)
  • MINOR (x.Y.z): Backward-compatible additions (new commands, features, options)
  • PATCH (x.y.Z): Backward-compatible fixes (bugs, security patches)

Public API includes: Discord commands, configuration schemas, plugin APIs, and database compatibility.

Pre-releases: 1.0.0-alpha, 1.0.0-rc.1 (lower precedence than releases) Build metadata: 1.0.0+build.123 (ignored in comparisons)

Version System

Tux uses a unified version system (src/tux/shared/version.py) that provides:

  • Multi-source detection: Environment variables, VERSION file, git tags with clear priority
  • Full SemVer support: Validation, comparison, and parsing using official regex patterns
  • Performance optimized: Version detected once and cached for the application lifetime
  • Robust error handling: Graceful fallbacks ensure the app always starts
  • Build metadata: Git SHA, Python version, and other build information

Version Detection

Version is determined dynamically at runtime (pyproject.toml uses placeholder 0.0.0). Priority order:

  1. TUX_VERSION env var: Runtime override for testing/deployments
  2. VERSION file: Docker containers and production builds
  3. git describe --tags --always: Development with git history (supports pre-releases)
  4. "dev" fallback: Ensures app always starts

Git tags automatically strip v prefix and handle pre-release versions (v1.2.3-alpha1.2.3-alpha).

API Reference

Python
from tux.shared.version import (
    # Core functions
    get_version(),           # Get current detected version
    get_build_info(),        # Get build metadata

    # Validation & comparison
    is_semantic_version(v),  # Validate semver format
    compare_versions(v1, v2), # Compare versions (-1, 0, 1)
    satisfies_constraint(v, constraint), # Check semver constraints (>, >=, <, <=, ==, !=)

    # Version manipulation
    bump_version(v, 'major'|'minor'|'patch'), # Increment versions
    get_version_info(v),     # Parse version components

    # Build metadata
    generate_build_metadata(sha, date), # Create build metadata strings
)

Features: Full SemVer 2.0.0 compliance, automatic normalization, basic constraint checking (comparison operators), version bumping utilities.

SemVer Implementation Notes

  • Pre-1.0.0: API may change at any time (SemVer item 4)
  • 1.0.0: Defines stable public API with strict SemVer compliance
  • Version precedence: 1.0.0-alpha < 1.0.0 < 2.0.0
  • Build metadata: Parsed and generated via generate_build_metadata() (format: sha.{short-sha}.{YYYYMMDD})

Release Cycle and Git Tagging

The release process is centered around Git tags and follows semantic versioning principles. All releases must be tagged with proper SemVer versions.

Creating Releases

To create a new version, create and push an annotated Git tag:

Bash
# Patch release (bug fixes)
git tag -a v1.2.3 -m "Release v1.2.3: Fix user permission bug"

# Minor release (new features)
git tag -a v1.3.0 -m "Release v1.3.0: Add new moderation commands"

# Major release (breaking changes)
git tag -a v2.0.0 -m "Release v2.0.0: Complete config format overhaul"

git push origin v1.2.3

Creating Pre-release Tags

Use proper SemVer pre-release identifiers for unstable releases:

Bash
# Release candidates
git tag -a v1.2.3-rc.1 -m "Release candidate v1.2.3-rc.1"
git push origin v1.2.3-rc.1

# Beta versions
git tag -a v1.2.3-beta.1 -m "Beta v1.2.3-beta.1"

# Alpha versions
git tag -a v1.2.3-alpha.1 -m "Alpha v1.2.3-alpha.1"

Pre-release Precedence

Pre-release versions have lower precedence than normal versions. For example, 1.2.3-alpha < 1.2.3.

Development Versions

Between releases, any new commits will result in a development version string (e.g., 1.2.3-5-g567def8), indicating the number of commits since the last tag. These are automatically generated by git describe and follow SemVer conventions.

Tag Naming Convention

  • Always prefix with v (e.g., v1.2.3, not 1.2.3)
  • Use valid SemVer identifiers only
  • Include descriptive commit messages
  • Push tags immediately after creation

Release Guidelines

Reaching 1.0.0

Release when the public API (commands, configs, plugins) is stable and production-ready. Pre-1.0.0 allows any changes.

Breaking Changes

Require major version bump. Always deprecate first (minor release) before removal (major release). Even small API changes need major bumps.

Deprecation Process

  1. Add deprecation warnings in minor release
  2. Document migration path
  3. Remove deprecated features in next major release

Docker Image Tagging

Our Docker build process is designed to bake the version directly into the image, ensuring traceability and consistency with the unified version system.

Build Process

The Containerfile uses build arguments to create a VERSION file inside the image:

Docker
ARG VERSION=""
ARG GIT_SHA=""
ARG BUILD_DATE=""

RUN set -eux; \
    if [ -n "$VERSION" ]; then \
        echo "Using provided version: $VERSION"; \
        echo "$VERSION" > /app/VERSION; \
    else \
        echo "No version provided, using fallback"; \
        echo "dev" > /app/VERSION; \
    fi; \
    echo "Building version: $(cat /app/VERSION)"

Building Versioned Images

To build a versioned image, pass the VERSION argument:

Bash
# Recommended command to build a production image
docker build \
  --build-arg VERSION=$(git describe --tags --always) \
  --target production \
  -t your-registry/tux:latest .

You can also tag the image with the specific version:

Bash
# Tag with the specific version for better tracking
VERSION_TAG=$(git describe --tags --always)
docker build \
  --build-arg VERSION=$VERSION_TAG \
  --target production \
  -t your-registry/tux:$VERSION_TAG \
  -t your-registry/tux:latest .

Note: The version system automatically handles the v prefix from git tags (e.g., v1.2.3 becomes 1.2.3) to ensure clean, semver-compatible version strings.

GitHub Actions Integration

Our GitHub Actions workflows automatically handle version generation:

  • PR Builds: Generate versions like pr-123-abc1234
  • Release Builds: Use the git tag version (e.g., 1.2.3)
  • Docker Builds: Pass the generated version as build arguments

This ensures that even in a detached production environment without Git, the application reports the correct version it was built from.

Testing the Version System

The version system includes comprehensive tests (tests/unit/test_version_system.py) that cover:

  • Version detection from all sources
  • Priority order validation
  • Edge cases and error handling
  • Semantic version validation
  • Build information generation
  • Integration with other components

Run the tests with:

Bash
uv run pytest tests/unit/test_version_system.py -v

Troubleshooting

Common Issues

  1. Version shows as "dev":
  2. Check if you're in a git repository
  3. Verify the VERSION file exists and contains a valid version
  4. Ensure TUX_VERSION environment variable is not set to an empty value

  5. Git describe fails:

  6. Ensure you have at least one git tag
  7. Check git repository integrity
  8. Verify git is available in the environment

  9. Docker version mismatch:

  10. Ensure VERSION build arg is passed correctly
  11. Check that the VERSION file is created in the container
  12. Verify the Containerfile version generation logic

Debugging

You can debug version detection by checking the version system directly:

Python
from tux.shared.version import VersionManager

manager = VersionManager()
print(f"Detected version: {manager.get_version()}")
print(f"Build info: {manager.get_build_info()}")
print(f"Is semantic version: {manager.is_semantic_version()}")

This unified version system ensures consistent, reliable versioning across all environments while maintaining the flexibility needed for different deployment scenarios.