### Pure-CLI Recipe for SemVer Tagging Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-action-yml-composite-wrapper.md This example provides a pure-CLI recipe for environments that cannot consume composite actions. It installs uvx and semvertag, then runs the tag command. Note that this recipe does not produce outputs directly; CLI stdout or JSON output must be parsed. ```yaml jobs: tag: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: "3.13" - run: pip install --quiet --no-cache-dir 'uv>=0.4,<1' - run: uvx 'semvertag>=0.3.1,<1' tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -------------------------------- ### IOC Container Setup with modern_di_typer Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md Demonstrates the module-level container setup using modern_di_typer.setup_di. This replaces manual container construction per invocation. ```python ioc.container = Container(groups=ALL_GROUPS) # No build_container() function # No skip_creator_parsing=True # No bound_type=None # kwargs={"settings": ...} only present on semvertag_use_case for Protocol disambiguation ``` -------------------------------- ### Install semvertag using uvx Source: https://github.com/modern-python/semvertag/blob/main/README.md Install the semvertag tool using the uvx command-line utility. This is the recommended installation method. ```sh uvx semvertag tag ``` -------------------------------- ### Dependency Injection Setup Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Configuration of the dependency injection container with various providers and strategies, including the current provider. ```python branch_prefix_strategy = providers.Factory(scope=Scope.APP, creator=_build_branch_prefix_strategy) conventional_commits_strategy = providers.Factory(scope=Scope.APP, creator=_build_conventional_commits_strategy) current_strategy = providers.Factory(scope=Scope.APP, creator=_build_current_strategy) class UseCasesGroup(modern_di.Group): semvertag_use_case = providers.Factory( scope=Scope.APP, creator=SemvertagUseCase, kwargs={ "provider": ProvidersGroup.current_provider, "strategy": StrategiesGroup.current_strategy, }, ) ALL_GROUPS: typing.Final[list[type[modern_di.Group]]] = [ SettingsGroup, ProvidersGroup, StrategiesGroup, UseCasesGroup, ] container: typing.Final = modern_di.Container(groups=ALL_GROUPS) ``` -------------------------------- ### CLI Command Setup with @MAIN_APP.command and @modern_di_typer.inject Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md Shows how to set up a CLI command 'tag' using modern_di_typer.setup_di and inject dependencies using @modern_di_typer.inject and FromDI. ```python modern_di_typer.setup_di(MAIN_APP, ioc.container) @MAIN_APP.command("tag") @modern_di_typer.inject def tag(semvertag_use_case: FromDI[SemvertagUseCase]): # ... implementation using semvertag_use_case pass def main(): with ioc.container: MAIN_APP() ``` -------------------------------- ### Commit Message Example Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Example commit message for adding the GitHub provider and its integration test suite. ```bash git add semvertag/providers/github.py tests/conftest.py tests/integration/test_github_provider.py git commit -m "providers/github: add GitHubProvider with integration test suite" ``` -------------------------------- ### Inline GitHub Actions Workflow Example Source: https://github.com/modern-python/semvertag/blob/main/planning/releases/0.3.0.md This example demonstrates how to set up a GitHub Actions workflow to use semvertag for tagging. It requires write permissions for the repository and can use the automatically provided `GITHUB_TOKEN` or a Personal Access Token (PAT). ```yaml name: Release on: push: branches: [ main ] jobs: tag: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install semvertag - run: uvx semvertag tag ``` -------------------------------- ### Python Configuration Class Migration Example Source: https://github.com/modern-python/semvertag/blob/main/planning/releases/0.3.1.md This example demonstrates the migration for overriding the merge mark text directly in the Python configuration class. The old `merge_mark_text` attribute is replaced by `merge_mark_texts`, which now accepts a tuple. ```python BranchPrefixConfig(merge_mark_text="X") (old) -> BranchPrefixConfig(merge_mark_texts=("X",)) (new) ``` -------------------------------- ### GitHub Actions Usage Example Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-08-action-yml-composite-wrapper-design.md This snippet shows how to use the semvertag composite action in a GitHub Actions workflow. It replaces a verbose installation and execution block with a simple two-line step. ```yaml - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: modern-python/semvertag@v0 ``` -------------------------------- ### Main Application Setup with Typer and Modern DI Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-ioc-idiomatic-modern-di-typer.md This snippet sets up the main Typer application, integrates modern dependency injection using `modern-di-typer`, and defines callbacks for version checking. It handles configuration loading, validation, and applies command-line overrides. ```python import errno import importlib.metadata import typing import modern_di_typer import pydantic import typer from semvertag import ioc from semvertag._errors import ConfigError, SemvertagError from semvertag._output import Output, build_json_output, build_rich_output from semvertag._settings import Settings, apply_cli_overlay from semvertag._use_case import SemvertagUseCase _PACKAGE_NAME: typing.Final = "semvertag" _CONFIG_ERROR_EXIT_CODE: typing.Final = 2 MAIN_APP: typing.Final = typer.Typer( name="semvertag", help=("Auto-tag GitLab/GitHub/Bitbucket repos with semantic version tags — one tool, two strategies."), no_args_is_help=True, add_completion=True, ) modern_di_typer.setup_di(MAIN_APP, ioc.container) def _version_callback(value: bool) -> None: if not value: return try: version = importlib.metadata.version(_PACKAGE_NAME) except importlib.metadata.PackageNotFoundError: version = "0" typer.echo(version) raise typer.Exit def _collect_overrides( # noqa: PLR0913 *, project_id: int | None, strategy: str | None, provider: str | None, token: str | None, default_branch: str | None, gitlab_endpoint: str | None, request_timeout: float | None, ) -> dict[str, tuple[typing.Any, str]]: overrides: dict[str, tuple[typing.Any, str]] = {} if project_id is not None: overrides["project_id"] = (project_id, "--project-id") if strategy is not None: overrides["strategy"] = (strategy, "--strategy") if provider is not None: overrides["provider"] = (provider, "--provider") if token is not None: overrides["gitlab.token"] = (pydantic.SecretStr(token), "--token") if default_branch is not None: overrides["default_branch"] = (default_branch, "--default-branch") if gitlab_endpoint is not None: overrides["gitlab.endpoint"] = (gitlab_endpoint, "--gitlab-endpoint") if request_timeout is not None: overrides["request_timeout"] = (request_timeout, "--request-timeout") return overrides def _config_error_from_validation(exc: pydantic.ValidationError) -> ConfigError: first: typing.Final = exc.errors()[0] loc: typing.Final = ".".join(str(part) for part in first.get("loc", ())) detail: typing.Final = first.get("msg", "invalid value") msg: typing.Final = f"Configuration error at '{loc}': {detail}. Check environment variables and command-line flags." return ConfigError(msg) @MAIN_APP.callback() def _main_callback( ctx: typer.Context, project_id: typing.Annotated[ int | None, typer.Option("--project-id", help="GitLab project id (or set CI_PROJECT_ID)."), ] = None, strategy: typing.Annotated[ str | None, typer.Option("--strategy", help="Bump strategy: branch-prefix | conventional-commits."), ] = None, provider: typing.Annotated[ str | None, typer.Option("--provider", help="Provider: gitlab | github | bitbucket."), ] = None, token: typing.Annotated[ str | None, typer.Option("--token", help="API token (overrides SEMVERTAG_TOKEN)."), ] = None, default_branch: typing.Annotated[ str | None, typer.Option("--default-branch", help="Default branch name override."), ] = None, gitlab_endpoint: typing.Annotated[ str | None, typer.Option("--gitlab-endpoint", help="GitLab API endpoint URL."), ] = None, request_timeout: typing.Annotated[ float | None, typer.Option("--request-timeout", help="Per-request timeout in seconds (clamped to 10)."), ] = None, _version: typing.Annotated[ bool | None, typer.Option("--version", callback=_version_callback, is_eager=True, help="Show version and exit."), ] = None, ) -> None: try: settings = Settings() try: overrides = _collect_overrides( project_id=project_id, strategy=strategy, provider=provider, token=token, default_branch=default_branch, gitlab_endpoint=gitlab_endpoint, request_timeout=request_timeout, ) settings = apply_cli_overlay(settings, overrides) except ValueError as exc: raise ConfigError(str(exc)) from exc if settings.provider != "gitlab": msg = f"Provider {settings.provider!r} not yet supported; v1.0 supports gitlab only." raise ConfigError(msg) except pydantic.ValidationError as exc: err = _config_error_from_validation(exc) typer.echo(f"Error: {err}", err=True) raise typer.Exit(code=err.exit_code) from err except ConfigError as err: typer.echo(f"Error: {err}", err=True) ``` -------------------------------- ### SemVerTag CLI Dry Run JSON Output Example Source: https://github.com/modern-python/semvertag/blob/main/docs/providers/github.md An example of the JSON output from a local semvertag CLI dry run, showing the schema version, strategy, calculated bump, status, and next tag. ```json {"schema_version":"1.0","strategy":"branch-prefix","bump":"minor","status":"dry_run","tag":"0.6.0","commit":"abc1234..."} ``` -------------------------------- ### Setup DI for Typer Application Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md Integrates the module-level DI container with the Typer application using modern_di_typer.setup_di. This should be called in the main entry point of the application. ```python # semvertag/__main__.py modern_di_typer.setup_di(MAIN_APP, ioc.container) ``` -------------------------------- ### Environment Variable Migration Example Source: https://github.com/modern-python/semvertag/blob/main/planning/releases/0.3.1.md This example shows the change in environment variable naming for overriding the merge mark text. The old singular `SEMVERTAG_BRANCH_PREFIX__MERGE_MARK_TEXT` is replaced by the plural `SEMVERTAG_BRANCH_PREFIX__MERGE_MARK_TEXTS`, which now expects a JSON-encoded tuple. ```shell SEMVERTAG_BRANCH_PREFIX__MERGE_MARK_TEXT="X" (old) -> SEMVERTAG_BRANCH_PREFIX__MERGE_MARK_TEXTS='["X"]' (new — note the plural field name + JSON-tuple env shape) ``` -------------------------------- ### GitHub Actions Workflow Setup Source: https://github.com/modern-python/semvertag/blob/main/docs/providers/github.md Minimal workflow snippet for using the GitHub-scoped GITHUB_TOKEN. Ensure 'permissions: contents: write' is set at the workflow level. ```yaml jobs: tag: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: "3.13" - run: pip install --quiet --no-cache-dir 'uv>=0.4,<1' - run: uvx 'semvertag>=0.5.0,<1' tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -------------------------------- ### Local or One-off semvertag Invocation Source: https://github.com/modern-python/semvertag/blob/main/docs/index.md This example demonstrates how to run semvertag locally or for one-off tasks using environment variables for authentication and project identification. ```sh SEMVERTAG_TOKEN= \ SEMVERTAG_PROJECT_ID= \ uvx semvertag tag ``` -------------------------------- ### Local SemVerTag CLI Dry Run Example Source: https://github.com/modern-python/semvertag/blob/main/docs/providers/github.md Demonstrates how to perform a dry run of the semvertag CLI locally to preview the next tag bump. The output is provided in JSON format. ```bash uvx 'semvertag>=0.5.0' tag --dry-run --json ``` -------------------------------- ### Callback for Configuration and Settings Context Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md The callback function parses CLI flags, builds the Settings object, and pushes it into the DI container's context. Setup errors are echoed directly as the output format is not yet determined. ```python import typer import pydantic from .config import ConfigError, Settings, apply_cli_overlay from .di import fetch_di_container @MAIN_APP.callback() def _root( ctx: typer.Context, project_id: ..., strategy: ..., provider: ..., token: ..., default_branch: ..., gitlab_endpoint: ..., request_timeout: ..., _version: ..., ) -> None: try: settings = Settings() overrides = _collect_overrides(...) # no quiet/json settings = apply_cli_overlay(settings, overrides) except (pydantic.ValidationError, ValueError, ConfigError) as err: typer.echo(f"Configuration error: {err}", err=True) raise typer.Exit(code=2) app_container = fetch_di_container(ctx) app_container.set_context(Settings, settings) ``` -------------------------------- ### GitLabProvider GET Methods Adoption Plan Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-08-httpware-decoder-adoption-design.md This outlines the planned adoption of httpware's decoder features for three GET methods within GitLabProvider. It details the specific httpware client methods to be used and the necessary adjustments to response models, including the use of `RootModel` for list types. ```markdown semvertag/providers/ ├── gitlab.py ← 3 GETs adopt response_model= / send_with_response; │ 6 validator helpers + 1 TypeVar deleted; 2 RootModel wrappers added └── _errors.py ← translate_gitlab gains a DecodeError → ProviderAPIError branch pyproject.toml ← httpware[pydantic] floored at >=0.8.2 ``` -------------------------------- ### Example Git Log Sequence Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-httpware-decoder-adoption.md This shows the expected commit history after the migration, confirming that changes related to httpware adoption are clearly documented in the commit messages. ```git providers/gitlab: adopt response_model=/send_with_response; delete in-tree validators providers/_errors: translate httpware.DecodeError to ProviderAPIError chore: floor httpware[pydantic] at >=0.8.2 (DecodeError, send_with_response) ``` -------------------------------- ### HTTPWare Client Factory for GitLab Provider Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-07-httpware-migration-design.md Demonstrates the dependency injection setup for creating an httpware.Client instance for the GitLab provider. Includes configuration for base URL, timeout, authentication headers, and retry middleware. ```python # semvertag/ioc.py import httpware def _build_gitlab_provider(settings: Settings) -> GitLabProvider: if settings.project_id is None: raise ConfigError("Project id missing. Set CI_PROJECT_ID or pass --project-id.") client = httpware.Client( base_url=settings.gitlab.endpoint, timeout=settings.request_timeout, headers={"PRIVATE-TOKEN": settings.gitlab.token.get_secret_value()}, middleware=[httpware.Retry(retry_status_codes=frozenset({408, 429, 500, 502, 503, 504}))], ) return GitLabProvider( config=settings.gitlab, project_id=settings.project_id, http=client, ) def _close_provider_client(provider: GitLabProvider) -> None: provider.http.close() class ProvidersGroup(modern_di.Group): gitlab_provider = providers.Factory( scope=Scope.APP, creator=_build_gitlab_provider, cache_settings=providers.CacheSettings(finalizer=_close_provider_client), ) ``` -------------------------------- ### Preview next bump locally Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-action-yml-dry-run.md Run the semvertag CLI with the `--dry-run` flag locally to compute the next tag without pushing. Ensure you have semvertag version 0.5.0 or higher installed. ```bash uvx 'semvertag>=0.5.0' tag --dry-run --json ``` -------------------------------- ### Configuring SemVerTag Strategy in GitHub Actions Source: https://github.com/modern-python/semvertag/blob/main/docs/providers/github.md Example of how to specify a 'conventional-commits' strategy for semvertag within a GitHub Actions workflow. Environment variables for strategy-specific configurations can also be passed. ```yaml - uses: modern-python/semvertag@v0 with: strategy: conventional-commits ``` ```yaml - uses: modern-python/semvertag@v0 env: SEMVERTAG_BRANCH_PREFIX__MINOR: '["feat/"]' ``` -------------------------------- ### Previewing Next Tag Bump with Dry Run Source: https://github.com/modern-python/semvertag/blob/main/docs/providers/github.md This example shows how to use the 'dry-run: true' option with the semvertag action to preview the next tag bump without actually creating a tag. This is useful for CI smoke tests or PR previews. ```yaml - id: semvertag uses: modern-python/semvertag@v0 with: dry-run: true ``` -------------------------------- ### Use Composite Action with Dry Run Source: https://github.com/modern-python/semvertag/blob/main/planning/releases/0.6.0.md Example of how to use the semvertag composite action with the `dry-run` input enabled. This allows previewing the next release tag without actually creating it. ```yaml - id: semvertag uses: modern-python/semvertag@v0 with: dry-run: true - if: steps.semvertag.outputs.bump != 'none' run: echo "Next release would be ${{ steps.semvertag.outputs.tag }} (${{ steps.semvertag.outputs.bump }} bump)" ``` -------------------------------- ### Basic SemverTag GitHub Actions Workflow Source: https://github.com/modern-python/semvertag/blob/main/planning/releases/0.3.0.md This is the minimal workflow configuration for automatically tagging releases using SemverTag on GitHub. It handles checkout, Python setup, dependency installation, and the tagging command. ```yaml name: semvertag on: push: branches: [main] permissions: contents: write jobs: tag: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: actions/setup-python@v5 with: { python-version: "3.13" } - run: pip install --quiet 'uv>=0.4,<1' - run: uvx 'semvertag>=0.3,<1' tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -------------------------------- ### Create docs/CNAME file Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-mkdocs-github-actions.md Create the `docs/CNAME` file with your custom domain name to point to your documentation site. ```text semvertag.modern-python.org ``` -------------------------------- ### Build MkDocs Documentation Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-settings-aliaschoices.md Run `uv run --with-requirements docs/requirements.txt mkdocs build --strict` to ensure the documentation builds without errors. ```bash uv run --with-requirements docs/requirements.txt mkdocs build --strict ``` -------------------------------- ### Add docs-deploy recipe to Justfile Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-mkdocs-github-actions.md Appends a recipe to the Justfile for deploying MkDocs documentation. This recipe uses `uvx` to ensure the correct Python environment and then runs `mkdocs gh-deploy --force`. It's crucial to note that manual invocation from a stale checkout can revert the live site. ```make # Force-pushes built site to gh-pages; CI runs this on push to main. # Manual invocation from a stale checkout will roll the live site back. docs-deploy: uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force ``` -------------------------------- ### Test Get Default Branch ProviderAPIError on Malformed Body Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Verifies that a ProviderAPIError is raised when the response body from the GitHub API for getting the default branch is not valid JSON. ```python def test_get_default_branch_raises_provider_api_error_on_malformed_body(monkeypatch: pytest.MonkeyPatch) -> None: overrides = {("GET", _REPO_PATH): httpx2.Response(200, text="not json at all")} provider, client = _make_provider(compose_handler(_github_default_handler, overrides)) with client, pytest.raises(ProviderAPIError, match="_RepoResponse response could not be decoded"): provider.get_default_branch() ``` -------------------------------- ### Build Documentation and Run Lint CI Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Commands to build the project's documentation using MkDocs and run the linting and testing suite in a CI environment. ```bash just lint-ci just test uv run --with mkdocs --with mkdocs-material mkdocs build --strict ``` -------------------------------- ### Docs Gate Check Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-action-yml-composite-wrapper.md Builds the MkDocs documentation strictly. Run from the repository root with the virtual environment active, or use 'uv run' if not. ```shell mkdocs build --strict ``` ```shell uv run mkdocs build --strict ``` -------------------------------- ### Update GitLab Provider GET Methods Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-httpware-decoder-adoption.md Refactors the GitLab provider's GET methods to utilize httpware's `send_with_response` for decoding responses. This replaces manual response validation and simplifies the iteration over paginated results. ```python self.http.send_with_response(..., response_model=_TagList) ``` -------------------------------- ### Command-Line Interface Flags Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-08-github-provider-design.md The main entry point (__main__.py) will include new flags for specifying the provider, repository, and GitHub endpoint, and route authentication tokens to the active provider. ```python parser.add_argument("--provider", choices=["gitlab", "github"]) parser.add_argument("--repo") parser.add_argument("--github-endpoint") # ... token routing logic ... ``` -------------------------------- ### Build sdist and check archive contents Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-v0-1-0-release-prep.md First, build the source distribution (sdist) using 'uv build'. Then, list the contents of the generated tarball to ensure it contains the expected 27 entries, primarily 'semvertag/' source files and top-level project files. ```bash uv build && tar -tzf dist/semvertag-0.tar.gz ``` -------------------------------- ### Main Callback Function Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md The main callback for the CLI application, handling various options and context setup. ```python @MAIN_APP.callback() def _main_callback( ctx: typer.Context, project_id: typing.Annotated[ int | None, typer.Option("--project-id", help="GitLab project id (or set CI_PROJECT_ID)."), ] = None, repo: typing.Annotated[ str | None, typer.Option("--repo", help="GitHub repo as OWNER/REPO (or set GITHUB_REPOSITORY)."), ] = None, provider: typing.Annotated[ str | None, typer.Option("--provider", help="Provider: 'github' or 'gitlab' (default: auto-detect from CI env)."), ] = None, strategy: typing.Annotated[ str | None, typer.Option("--strategy", help="Bump strategy: branch-prefix | conventional-commits."), ] = None, token: typing.Annotated[ str | None, typer.Option("--token", help="API token (overrides SEMVERTAG_TOKEN); routed to the active provider."), ] = None, default_branch: typing.Annotated[ str | None, typer.Option("--default-branch", help="Default branch name override."), ] = None, gitlab_endpoint: typing.Annotated[ str | None, typer.Option("--gitlab-endpoint", help="GitLab API endpoint URL."), ] = None, github_endpoint: typing.Annotated[ str | None, typer.Option("--github-endpoint", help="GitHub API endpoint URL."), ] = None, ) -> None: pass ``` -------------------------------- ### Build documentation Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-07-httpware-migration.md Build the project's documentation using mkdocs. The --strict flag ensures that any errors will halt the build. ```bash mkdocs build --strict ``` -------------------------------- ### Get Default Branch from GitHub Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Retrieves the default branch name for a GitHub repository. Assumes the provider and client are set up correctly. ```python def test_get_default_branch_returns_value(monkeypatch: pytest.MonkeyPatch) -> None: provider, client = _make_provider(_github_default_handler) with client: assert provider.get_default_branch() == _DEFAULT_BRANCH ``` -------------------------------- ### CLAUDE.md Project Guide Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-bmad-to-superpowers-migration-and-httpx2-wrapper.md Content for the CLAUDE.md file, outlining the project's workflow, commit message conventions, and reference directories. ```markdown # semvertag — Claude project guide ## Workflow This project uses **Superpowers** (brainstorm → plan → TDD → review). - Brainstorm specs live in `planning/specs/YYYY-MM-DD--design.md`. - Implementation plans live in `planning/plans/YYYY-MM-DD-.md`. - Use TDD by default: red, green, refactor. Tests before implementation. - Use git worktrees for feature isolation (`superpowers:using-git-worktrees`). - Use the verification gate before claiming work complete (`superpowers:verification-before-completion`). - Request code review via a subagent before landing (`superpowers:requesting-code-review`). ## Commit messages Imperative present-tense, scoped where helpful: - `providers: add HttpClient wrapper` - `docs: update README hero section` - `fix: handle empty default branch in GitLab provider` No story-numbered prefixes (`land story X.Y`, `contextualise story X.Y`). Those belong to the retired BMad workflow. ## Reference directories (do not edit) - `_archive/bmad/` — retired BMad workspace. Historical specs, PRD, architecture, retrospectives, and 14 dense story files. Read for context; do not extend, do not delete. - `_autosemver_reference/` — the original Raiffeisen-internal `autosemver` package. Behavioral reference only — port logic and test shapes from it but never `git mv` files in or take it as a starter. ``` -------------------------------- ### Canonical DI with Typer and Late-Bound Context Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md Demonstrates the core pattern for integrating a module-level DI container with Typer. It shows how to set up the container, integrate it with Typer using `setup_di`, handle callback logic to late-bind context (like Settings) using `set_context`, and inject dependencies into commands using `@inject` and `FromDI`. ```python # Module-level: schema only container = Container(groups=[AppGroup]) app = typer.Typer() modern_di_typer.setup_di(app, container) @app.callback() def _root(ctx, name="world"): settings = Settings(name=name) app_container = modern_di_typer.fetch_di_container(ctx) app_container.set_context(Settings, settings) # AFTER scope is entered @app.command() @modern_di_typer.inject def hello(greeter: Annotated[Greeter, FromDI(Greeter)]): typer.echo(greeter.greet()) # prints "hello alice" with --name alice if __name__ == "__main__": with container: # enters APP scope app() ``` -------------------------------- ### Verify Baseline Commands Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-strategy-no-bump-cleanup.md Commands to run inside the worktree to verify the clean baseline before starting the refactor. Ensures the environment is as expected. ```bash pwd git branch --show-current git status ``` -------------------------------- ### Get Default Branch from GitHub Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-08-github-provider-design.md Fetches the default branch of a GitHub repository. Raises ConfigError if the default branch is missing in the API response. ```python def get_default_branch(self) -> str: try: repo_info = self.http.get(f"{_API_PREFIX}/{self.repo}", response_model=_RepoResponse) except httpware.ClientError as exc: raise _errors.translate_github(exc, repo=self.repo) from exc if not repo_info.default_branch: raise ConfigError("Default branch missing from GitHub response. Verify the repo has a default branch.") return repo_info.default_branch ``` -------------------------------- ### Build MkDocs documentation Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-mkdocs-github-actions.md Build the MkDocs documentation locally to verify the configuration. This command checks for errors and warnings, ignoring expected `site_url` vs `repo_url` mismatches. ```bash uvx --with-requirements docs/requirements.txt mkdocs build --strict 2>&1 | tail -20 ``` -------------------------------- ### Verify Source Distribution Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-v0-1-0-release-prep.md Build the source distribution (sdist) and list its contents. Verify that only expected files are included and that extraneous files like 'action.yml', 'docs/', or 'tests/' are absent. ```bash uv build ``` ```bash tar -tzf dist/semvertag-0.tar.gz | sort ``` -------------------------------- ### Main Application Entry Point Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-ioc-idiomatic-modern-di-typer-design.md The main function initializes the DI container and runs the Typer application. It ensures the container context is active during application execution. ```python def main() -> None: with ioc.container: MAIN_APP() ``` -------------------------------- ### Get Latest Commit on Default Branch Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-08-github-provider-design.md Retrieves the latest commit SHA and message from the default branch. Raises ProviderAPIError if no commits are found on the branch. ```python def get_latest_commit_on_default_branch(self) -> Commit: default_branch: typing.Final = self.get_default_branch() try: commits = self.http.get( f"{_API_PREFIX}/{self.repo}/commits", params={"sha": default_branch, "per_page": 1}, response_model=_CommitList, ) except httpware.ClientError as exc: raise _errors.translate_github(exc, repo=self.repo) from exc if not commits.root: raise ProviderAPIError(f"No commits on default branch '{default_branch}'. The branch appears empty.") head = commits.root[0] return Commit(sha=head.sha, message=head.commit.message) ``` -------------------------------- ### Create Deploy Docs GitHub Actions workflow Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-mkdocs-github-actions.md Defines a GitHub Actions workflow named 'Deploy Docs' that triggers on pushes to the 'main' branch affecting specific paths or on manual dispatch. It uses `actions/checkout@v6`, `extractions/setup-just@v4`, and `astral-sh/setup-uv@v8.2.0` to set up the environment before running the `just docs-deploy` command. ```yaml name: Deploy Docs on: push: branches: [main] paths: - "docs/**" - "mkdocs.yml" - ".github/workflows/docs.yml" workflow_dispatch: concurrency: group: docs-deploy cancel-in-progress: true permissions: contents: write jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: extractions/setup-just@v4 - uses: astral-sh/setup-uv@v8.2.0 - run: just docs-deploy ``` -------------------------------- ### Create GitLab Provider Instance with HTTPware Client Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-07-httpware-migration.md Constructs a GitLabProvider instance and an HTTPware Client for integration testing. Uses httpx2.MockTransport and configures the client with base URL and headers. ```python _TOKEN_HEADER: typing.Final = "PRIVATE-TOKEN" def _make_provider(handler: HandlerCallable) -> tuple[GitLabProvider, httpware.Client]: transport: typing.Final = httpx2.MockTransport(handler) config: typing.Final = GitLabConfig(endpoint=GITLAB_ENDPOINT, token=pydantic.SecretStr(GITLAB_TOKEN)) client: typing.Final = httpware.Client( httpx2_client=httpx2.Client(transport=transport, base_url=GITLAB_ENDPOINT), headers={_TOKEN_HEADER: config.token.get_secret_value()}, ) provider: typing.Final = GitLabProvider(config=config, project_id=GITLAB_PROJECT_ID, http=client) return provider, client ``` -------------------------------- ### Get Git Diff Shortstat Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-strategy-no-bump-cleanup.md Display a summary of changes between the 'main' branch and the current HEAD, focusing on the number of files changed and lines added/deleted. ```bash git diff main --shortstat ``` -------------------------------- ### semvertag README.md content Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-05-31-v0-1-0-release-prep-design.md The main content of the README.md file for the semvertag project, including badges, installation instructions, CI usage, strategies, and license information. ```markdown # semvertag [![CI](https://github.com/modern-python/semvertag/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/semvertag/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/modern-python/semvertag/branch/main/graph/badge.svg)](https://codecov.io/gh/modern-python/semvertag) Auto-tag your GitLab repository with semantic version tags from CI — one tool, two strategies. ## Install ```sh uvx semvertag tag ``` ## Use it in GitLab CI Include the Catalog component in your `.gitlab-ci.yml`: ```yaml include: - component: gitlab.com/modern-python/semvertag/semvertag@v0.1.0 inputs: strategy: branch-prefix # or: conventional-commits ``` The component runs `uvx semvertag tag` against your repo on the default branch. semvertag inspects the latest commit + tag history, decides the appropriate semver bump, and creates the new tag via the GitLab API. ## Strategies - **branch-prefix** (default): the latest commit on the default branch must be a merge commit whose source branch starts with `feature/` (minor), `bugfix/`, or `hotfix/` (patch). - **conventional-commits**: parses the latest commit's [Conventional Commits](https://www.conventionalcommits.org/) header (`feat:` minor, `fix:`/`perf:` patch, `!` or `BREAKING CHANGE:` major). Both can be customized via env vars. See [docs](https://semvertag.readthedocs.io) for the full configuration surface. ## License MIT ``` -------------------------------- ### Build Docs Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-strategy-no-bump-cleanup.md Build the project documentation using `mkdocs build --strict`. This command ensures that the documentation builds cleanly and adheres to strict standards. ```bash UV_OFFLINE=1 uv run --with-requirements docs/requirements.txt mkdocs build --strict ``` -------------------------------- ### Use semvertag in GitLab CI Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-v0-1-0-release-prep.md Example of how to include the semvertag component in a .gitlab-ci.yml file. It specifies the component to include and allows for input configuration, such as setting the strategy. ```yaml include: - component: gitlab.com/modern-python/semvertag/semvertag@v0.1.0 inputs: strategy: branch-prefix # or: conventional-commits ``` -------------------------------- ### Verify Main Branch After Merge Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-ioc-idiomatic-modern-di-typer.md After merging the work to the main branch, run these commands to ensure the project is in a green state and the new DI setup is correctly reflected. ```bash git log --oneline -2 ``` ```bash just lint-ci && uv run pytest -q ``` ```bash grep -n "build_container\|skip_creator_parsing\|bound_type=None" semvertag/ ``` ```bash grep -n "modern_di_typer.setup_di\|FromDI" semvertag/__main__.py ``` ```bash uv run semvertag --help | head -20 ``` -------------------------------- ### Get Latest Commit on Default Branch from GitHub Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Fetches the latest commit SHA and message from the default branch of a GitHub repository. Requires a valid client connection. ```python def test_get_latest_commit_returns_head(monkeypatch: pytest.MonkeyPatch) -> None: provider, client = _make_provider(_github_default_handler) with client: commit = provider.get_latest_commit_on_default_branch() assert commit == Commit(sha=_DEFAULT_COMMIT_SHA, message=_DEFAULT_COMMIT_MESSAGE) ``` -------------------------------- ### Stage and commit changes for release prep Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-05-31-v0-1-0-release-prep.md This command sequence stages all changes in the repository and then commits them with a detailed message outlining the release preparation steps, including CLI flag changes, provider implementation status, versioning strategy updates, and documentation modifications. ```bash git add -A git commit -m "release: prep for v0.1.0 - Drop --provider CLI flag + Settings.provider field (only gitlab works) - Delete action.yml + docs/providers/github.md (github provider not implemented; ship clean v0.1.0; restore both when github provider lands) - publish.yml: adopt modern-di pattern of \`uv version $TAG\` at build time; drop the strict tag↔[project.version] guard - README.md + docs/index.md: replace stubs with real content - → modern-python everywhere (pyproject URL, doc examples, mkdocs repo_url + social link, GitLab template version pin) - mkdocs.yml: drop GitHub provider nav entry - templates/semvertag.yml: update version pin to >=0.1,<1 [project.version] stays \"0\" as a placeholder per the modern-di convention; the publish workflow injects the real version from the git tag." ``` -------------------------------- ### Get Default Branch from GitLab Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-07-httpware-migration.md Fetches the default branch of a GitLab project using HTTPware. Handles potential client errors and missing default branch information. ```python def get_default_branch(self) -> str: try: project = self.http.get( f"{_API_PREFIX}/{self.project_id}", response_model=_ProjectResponse, ) except httpware.ClientError as exc: raise _errors.translate_gitlab(exc, project_id=self.project_id) from exc if not project.default_branch: msg = "Default branch missing from GitLab response. Verify the project has a default branch configured." raise ConfigError(msg) return project.default_branch ``` -------------------------------- ### Target Directory Structure Source: https://github.com/modern-python/semvertag/blob/main/planning/specs/2026-06-07-httpware-migration-design.md Illustrates the planned file structure after migrating to httpware, showing deleted and new files. ```text semvertag/ ├── _transport.py ← DELETED ├── providers/ │ ├── _http.py ← DELETED │ ├── _errors.py ← NEW: translate httpware StatusError → semvertag domain errors │ └── gitlab.py ← holds httpware.Client directly; uses _errors.translate_gitlab() └── ioc.py ← builds httpware.Client with Retry middleware; TransportsGroup deleted ``` -------------------------------- ### Verify MkDocs Build Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-action-yml-composite-wrapper.md Run this command to ensure that the MkDocs documentation builds successfully without warnings after changes. This is a gate to confirm no regressions were introduced. ```bash uv run mkdocs build --strict ``` -------------------------------- ### GitLab CI Job for semvertag Source: https://github.com/modern-python/semvertag/blob/main/docs/index.md This snippet shows how to integrate semvertag into a GitLab CI pipeline. It specifies the Docker image, sets the strategy, installs the tool, and runs the tag command. ```yaml semvertag: image: python:3.13-slim variables: SEMVERTAG_STRATEGY: branch-prefix # or: conventional-commits before_script: - pip install --quiet 'uv>=0.4,<1' script: - uvx 'semvertag>=0.1,<1' tag rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' ``` -------------------------------- ### Complete mkdocs.yml configuration Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-09-mkdocs-github-actions.md The top section of your `mkdocs.yml` file after adding the `site_url` should look like this. ```yaml site_name: semvertag site_url: https://semvertag.modern-python.org repo_url: https://github.com/modern-python/semvertag docs_dir: docs edit_uri: edit/main/docs/ ``` -------------------------------- ### Test Overridable GitHub Endpoint for Enterprise Source: https://github.com/modern-python/semvertag/blob/main/planning/plans/2026-06-08-github-provider.md Checks if the GitHub configuration endpoint can be overridden, for example, for GitHub Enterprise instances, by setting the SEMVERTAG_GITHUB__ENDPOINT environment variable. Requires `pytest` and `semvertag._settings.Settings`. ```python def test_github_config_endpoint_overridable_for_enterprise(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("SEMVERTAG_GITHUB__ENDPOINT", "https://github.acme.com/api/v3") settings = Settings(provider="github", repo="owner/repo") assert settings.github.endpoint == "https://github.acme.com/api/v3" ```