Skip to content

feat: Phase 9 — ship it (packaging, release workflow, docs) - #14

Open
getdownandcode wants to merge 7 commits into
mainfrom
feat
Open

getdownandcode wants to merge 7 commits into
mainfrom
feat

Conversation

@getdownandcode

Copy link
Copy Markdown
Owner

Overview

Phase 9 per plan.md: finalize packaging, add the release pipeline, and finish the README. PyPI dist name is gitmate-cli (verified free on both PyPI and TestPyPI via the JSON API — gitmate itself is permanently taken by an abandoned 2023 project). The CLI command and import package stay gitmate.

Changes

  1. pyproject.toml — dist gitmate-cli; authors/keywords/classifiers/URLs; [tool.hatch.build.targets.wheel] packages = ["src/gitmate"] (needed after the rename); sdist excludes internal docs (plan.md, AGENTS.md — verified absent from a fresh uv build); --version/-V flag via importlib.metadata.
  2. CHANGELOG.md — v0.1.0 release notes, curated from gitmate changelog's own output over the full history (dogfooded; the tool's template fallback did the grouping).
  3. Architecture diagram — added the Git Hooks box (install-hook / prepare-commit-msg edges), subtitle corrected from "Target" to "System" (as-built). Validated 0 errors / 0 crossings; PNG re-exported.
  4. release.yml — two-job PyPA pattern: build (pytest → uv build → uvx twine check → upload-artifact, no OIDC) → publish (needs: build, id-token: write scoped to this job only, uv publish). Manual dispatches default to TestPyPI so a mistaken run cannot touch real PyPI; tag pushes v* publish to PyPI and attach artifacts to a GitHub Release.
  5. README — badges, real-user install paths, command reference, hooks section, architecture embed, live metrics table from gitmate stats (277 invocations, $0.0087 est. spend, dev-period caveat), the hash-vs-semantic caching rationale, and the design-decisions story (graceful degradation + human-in-the-loop first).

Verification

  • 291 tests green; ruff check/format, mypy --strict clean
  • uv build inspected: sdist excludes plan.md/AGENTS.md, includes tests/docs/.pre-commit-hooks.yaml/uv.lock; wheel ships templates + both entry points
  • Name checked live via PyPI JSON API (404 on pypi.org and test.pypi.org) with a control

Open items (before merge)

  • Demo GIF — recording needs a real (non-fallback) generation; the keychain currently has no API key (gitmate config show → api_key: not set). Once gitmate config set-key is run, the GIF lands in this PR; alternatively approve a fallback-mode demo or drop the embed.
  • After merge: TestPyPI trusted-publisher registration + manual dispatch dry run, then checkpoint 2 before the real v0.1.0 tag.

- dist name gitmate-cli (import package and CLI command stay gitmate); hatch wheel packages pinned to src/gitmate
- authors, keywords, classifiers, project URLs; --version/-V via importlib.metadata
- sdist excludes internal docs (plan.md, AGENTS.md)
Curated from gitmate changelog's own output over the full history (dogfooded); the tool's template fallback did the grouping after the API path was unavailable in the build sandbox.
Phase 8 landed with crash-safe prepare-commit-msg hooks; the diagram now
shows the hook component plus install/prepare-commit-msg edges. Subtitle
corrected from 'Target' to 'System' architecture (as-built). Validated 0
errors/0 crossings; PNG re-exported. Structural check only — pixel review
pending (agent cannot view images).
build job runs tests, uv build, twine check, uploads artifacts with no OIDC;
publish job (needs build) holds id-token: write scoped to itself alone;
manual dispatches default to TestPyPI so a mistaken run cannot touch PyPI;
tag pushes publish to PyPI and attach artifacts to a GitHub release.
…tory

Real-user install paths (uv tool/pipx/pip install gitmate-cli), command
reference, hooks section, architecture embed, live metrics table from
gitmate stats with the development-period caveat, the hash-vs-semantic
caching rationale, and the design-decisions story leading with graceful
degradation and human-in-the-loop. Demo GIF embed pending recording.
- docs/demo.gif: real end-to-end recording (asciinema + agg after vhs
  could not drive ttyd on this machine): stage -> gitmate commit -> real
  gemini-3.5-flash-lite generation -> typed accept -> git log proof.
- Pin google_genai.models logger to ERROR: the SDK's once-per-process AFC
  advisory printed above every generated message; genuine errors remain.
@getdownandcode

Copy link
Copy Markdown
Owner Author

Demo GIF landed (real recording of a real generation — feat(app): return True from greet function from gemini-3.5-flash-lite, typed accept, git log proof). vhs could not drive ttyd on this machine, so the recording used plan.md's named alternative: asciinema + agg, 74 frames. Also: the google-genai SDK's once-per-process AFC advisory was printing above every generated message — quieted that one logger to ERROR (errors still audible), pinned by a test. 292 green.

…ve budget caps, slim sdist

- commit/pr-summary/changelog now print a clean error for malformed or
  hand-edited config (including UnknownModelError mid-pipeline, a
  ConfigError subclass) instead of a raw traceback
- budget_cap_usd rejects negative values at both entry points: config
  set and load_config (a negative cap permanently blocked generation)
- sdist excludes docs/, .github/, uv.lock, .agents (drops ~2.5MB);
  templates and hook assets verified to still ship, .pre-commit-hooks.yaml kept
- README: PyPI badges removed until first release, test count updated
- 5 new tests: clean-error CLI paths for all three generation commands,
  negative-cap rejection at both layers
@getdownandcode

Copy link
Copy Markdown
Owner Author

🤖 Automated PR Review: #14 - feat: Phase 9 — ship it (packaging, release workflow, docs)

1. Summary of Changes

  • Scope: Completes Phase 9 delivery for production packaging, PyPI distribution renaming (gitmate-cli), automated GitHub Actions release workflow with OIDC trusted publishing, user documentation, and clean error handling for CLI generation commands.
  • Key Files Modified: pyproject.toml, .github/workflows/release.yml, README.md, CHANGELOG.md, src/gitmate/cli.py, src/gitmate/config.py, src/gitmate/providers/gemini.py, tests/test_cli.py, tests/test_config.py, tests/test_providers.py, uv.lock

2. Analysis & Key Highlights

  • Strengths:
    • Distribution & Wheel Hygiene: Seamless package rename to gitmate-cli with correct wheel package mapping ([tool.hatch.build.targets.wheel] packages = ["src/gitmate"]). Build artifacts strictly exclude internal development artifacts (plan.md, AGENTS.md, .agents) while retaining runtime templates.
    • Production Release Pipeline: .github/workflows/release.yml implements PyPA best practices: decoupled build and publish stages, ephemeral twine check, scoped id-token: write permissions for OIDC publishing, and default TestPyPI target on manual dispatches to prevent unintended PyPI releases.
    • Hardened CLI Entry Points: Replaces raw stack traces with sanitized error messages on ConfigError across commit, pr-summary, and changelog.
  • Observations:

3. Actionable Suggestions & Concerns

  • [Severity: Medium] [src/gitmate/]: Missing PEP 561 py.typed marker file. Because gitmate includes comprehensive strict type annotations, external code or tests importing gitmate encounter [import-untyped] errors from mypy.
    # Create the marker file so typed distributions advertise type information:
    touch src/gitmate/py.typed
  • [Severity: Low] [src/gitmate/cli.py:433 / src/gitmate/config.py:168]: Validate against NaN and Infinity in budget cap parsing. float("nan") < 0 and float("inf") < 0 evaluate to False, allowing non-finite floats to bypass the non-negative check.
    import math
    
    if cap < 0 or math.isnan(cap) or math.isinf(cap):
        raise typer.BadParameter(f"'budget_cap_usd' must be a finite non-negative number (got {value!r}).")
  • [Severity: Low] [tests/test_cli.py:31]: Guard version("gitmate-cli") in test_version_flag_prints_distribution_version. If tests run in a standalone non-editable environment, version("gitmate-cli") raises PackageNotFoundError in the test assertion itself.
    try:
        expected_version = version("gitmate-cli")
    except PackageNotFoundError:
        expected_version = "unknown"
    assert expected_version in result.output
  • [Severity: Low] [src/gitmate/providers/gemini.py:22]: Avoid global logger configuration at module level. Setting logging.getLogger("google_genai.models").setLevel(logging.ERROR) at module import time applies global state mutation during library imports. Moving this call into GeminiProvider.__init__ encapsulates the SDK logger adjustment.

4. Merge Status

Verdict: READY TO MERGE
Reasoning: Packaging, CI release pipelines, and documentation are production-ready; recommend merging PR #15 into feat first to incorporate recent bugfixes before releasing to main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant