Skip to content

Separate project phase from PEP 440 pre-release — fix pip install without --pre #52

@djdarcy

Description

@djdarcy

Separate project maturity phase from PEP 440 pre-release — fix pip install without --pre

Problem

GTT uses PHASE = "alpha" in version.py and src/ghtraf/_version.py to signal that the project is in early development. This is a project maturity label — it hasn't changed since the project's creation and won't change until the overall project matures.

However, get_pip_version() maps this to PEP 440's a0 suffix:

# src/ghtraf/_version.py (current)
PHASE = "alpha"

def get_pip_version():
    phase_map = {"alpha": "a0", "beta": "b0"}
    base += phase_map.get(PHASE, PHASE)  # → "0.3.1a0"

PEP 440 pre-release versions (a, b, rc) are excluded from default pip resolution. When a user runs:

pip install github-traffic-tracker

pip will skip all aN versions unless:

  1. The user passes --pre, or
  2. No stable version exists at all (fallback behavior)

Right now this works by accident0.2.8a0 is the only version on PyPI, so pip falls back to it. But the moment we publish any stable-numbered version, every alpha release becomes invisible to standard pip install. And if someone pins >=0.2.8, pip will skip 0.3.0a0 through 0.9.9a0 entirely.

This is a semantic collision: we're using "alpha" to mean project maturity (informational, long-lived) but PyPI interprets it as release lifecycle (functional, per-version, hides from resolution).

Proposed solution

Port the three-field version model already proven in TeeClip:

# Before (current GTT):
PHASE = "alpha"  # Does double duty: display AND pip

# After (TeeClip model):
PHASE = None                  # Per-release lifecycle: None, "alpha", "beta", "rc1"
PRE_RELEASE_NUM = 0           # PEP 440 pre-release counter (a1, b2)
PROJECT_PHASE = "alpha"       # Project-wide maturity: "prealpha", "alpha", "beta", "stable"

PHASE controls PyPI behavior. When None, the pip version is stable-numbered (0.3.1). When set to "alpha" for a specific release, pip gets 0.3.1a1 — a genuine pre-release for testing.

PROJECT_PHASE is display-only. Shows in ghtraf --version as ALPHA 0.3.1. Never touches pip resolution. Changes once when the project reaches beta or stable.

PRE_RELEASE_NUM allows publishing multiple pre-releases of the same version (0.5.0a1, 0.5.0a2, 0.5.0a3).

TeeClip already has this working on PyPI: versions 0.1.1 (stable), 0.2.2a1 (pre-release), and 0.2.2 (stable) all coexist. pip install teeclip correctly resolves to 0.2.2.

Implementation approach

Phase 1: Version field addition

Update version.py (source of truth):

MAJOR = 0
MINOR = 3
PATCH = 1
PHASE = None  # Per-release: None (stable on PyPI), "alpha", "beta", "rc1"
PRE_RELEASE_NUM = 0  # PEP 440 number: a1, b2, rc3
PROJECT_PHASE = "alpha"  # Project-wide: "prealpha", "alpha", "beta", "stable"

Mirror changes to src/ghtraf/_version.py with new functions:

def get_display_version():
    """Human-friendly: 'ALPHA 0.3.1' or 'BETA 0.5.0' or '1.0.0'"""
    base = get_base_version()
    if PROJECT_PHASE and PROJECT_PHASE != "stable":
        return f"{PROJECT_PHASE.upper()} {base}"
    return base

def get_pip_version():
    base = f"{MAJOR}.{MINOR}.{PATCH}"
    phase_map = {"alpha": f"a{PRE_RELEASE_NUM}", "beta": f"b{PRE_RELEASE_NUM}"}
    if PHASE:
        base += phase_map.get(PHASE, PHASE)
    # ... branch/dev logic unchanged

Phase 2: Sync script update

Update scripts/sync-versions.py:

  • Read/write PROJECT_PHASE and PRE_RELEASE_NUM alongside existing fields
  • Add --project-phase CLI flag (parallel to existing --phase)
  • Update to_pep440() to use PRE_RELEASE_NUM

Phase 3: CLI integration

Update src/ghtraf/cli.py to use get_display_version() for --version output.

Add DISPLAY_VERSION module-level export for convenience.

Phase 4: First stable-numbered PyPI release

Publish next version (e.g., 0.4.0) without a0 suffix. pip install github-traffic-tracker works without --pre.

Version field semantics

Field Controls Changes when Example values
PROJECT_PHASE --version display, badges Project reaches new maturity "prealpha", "alpha", "beta", "stable"
PHASE PyPI version suffix Specific release is a pre-release None, "alpha", "beta", "rc1"
PRE_RELEASE_NUM PEP 440 pre-release counter Multiple pre-releases of same version 0, 1, 2

Default state (most releases): PHASE = None → pip gets 0.4.0 (installable).
Pre-release state (testing): PHASE = "alpha" → pip gets 0.5.0a1 (needs --pre).

Git tag convention

Tags can retain -alpha suffix for human readability: v0.4.0-alpha. Git tags are not PEP 440 constrained. The PyPI version comes from PIP_VERSION in _version.py, not the tag name.

When PROJECT_PHASE changes: tags become v0.5.0-beta, then v1.0.0.

Design considerations

  • Existing PyPI install: 0.4.0 > 0.2.8a0 in PEP 440 ordering, so pip install --upgrade works correctly. No conflict.
  • Classifier stays: Development Status :: 3 - Alpha in pyproject.toml continues to signal maturity on the PyPI page.
  • No breaking change: The PIP_VERSION attribute name and pyproject.toml reference don't change — only the computed value does.
  • Reversible: If needed, set PHASE = "alpha" to return to pre-release PyPI versions.
  • Copy-first: Port from TeeClip's _version.py — proven code, not a rewrite.

Acceptance criteria

  • version.py has PROJECT_PHASE, PRE_RELEASE_NUM, and PHASE = None
  • src/ghtraf/_version.py mirrors the three-field model with get_display_version()
  • scripts/sync-versions.py propagates all three fields and supports --project-phase flag
  • get_pip_version() returns stable version by default (e.g., 0.4.0 not 0.4.0a0)
  • get_display_version() returns ALPHA 0.4.0 (or current project phase)
  • ghtraf --version shows display version with project phase prefix
  • DISPLAY_VERSION exported as module-level convenience
  • pyproject.toml still reads PIP_VERSION correctly (no change needed)
  • Alias package pyproject.toml syncs to stable version number
  • Published to PyPI as stable-numbered version — pip install github-traffic-tracker works without --pre
  • Verified on TestPyPI before production publish

Related issues

Analysis

See 2026-02-28__16-56-20__dev-workflow-process_version-system-pypi-alignment.md for the full DEV WORKFLOW PROCESS analysis including comparison of GTT vs TeeClip version systems and evaluation of four solution approaches.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureStructural and architectural decisionsclightraf CLI tool and command-line interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions