0.3.0 — GitHub provider
Feature release. Adds first-class GitHub support. semvertag now auto-tags both GitLab projects and GitHub repos from the same CLI, with auto-detection from the CI runner's environment. The package description "Auto-tag GitLab repos" becomes "Auto-tag GitLab and GitHub repos" — honest at last.
If you're on GitLab today, nothing changes: the default provider remains gitlab when no CI env signals otherwise. Drop straight to "What's new" — there are no breaking changes to the GitLab side.
What's new
GitHubProvider. Parallel toGitLabProvider, conforming to the same four-methodProviderProtocol. Talks toapi.github.com(or your GitHub Enterprise endpoint viaSEMVERTAG_GITHUB__ENDPOINT), authenticates viaAuthorization: Bearer <token>+X-GitHub-Api-Version: 2022-11-28, creates tags viaPOST /repos/{owner}/{repo}/git/refs. Same retry posture, same decoder path, same error translator shape as the GitLab side — built on thehttpware-backed infrastructure that landed in 0.2.x.- Provider auto-detection from CI env. semvertag reads
GITHUB_ACTIONSandGITLAB_CIto pick the active provider. Inside GitHub Actions:GITHUB_ACTIONS=trueis auto-set →provider=github. Inside GitLab CI:GITLAB_CI=true→provider=gitlab. Outside CI: defaults togitlab(back-compat with 0.2.x users). Explicit--provider githuborSEMVERTAG_PROVIDER=githubalways wins. - New CLI flags.
--provider github|gitlab— explicit override--repo OWNER/REPO— GitHub repo identifier (or setGITHUB_REPOSITORY/SEMVERTAG_REPO)--github-endpoint— GitHub API endpoint (for GitHub Enterprise)--tokennow routes to the active provider's token field (a--provider github --token ghp_...invocation populatesSettings.github.token)
- New env aliases picked up by
pydantic-settings:GITHUB_REPOSITORY,SEMVERTAG_REPO→Settings.repoSEMVERTAG_PROVIDER,PROVIDER→Settings.providerGITHUB_TOKEN,SEMVERTAG_GITHUB__TOKEN,SEMVERTAG_TOKEN→Settings.github.tokenSEMVERTAG_GITHUB__ENDPOINT→Settings.github.endpoint(defaulthttps://api.github.com)
- Tag-already-exists detection on GitHub. GitHub returns
422 Unprocessable Entitywith{"errors": [{"resource": "Reference", "code": "already_exists"}]}when you POST a tag ref that already exists.translate_create_tag_github_unprocessableinspects both the structuredalready_existscode (durable contract) and the"already exists"message string (safety net) and surfaces as aConfigError("Tag already exists: 'vX.Y.Z'."). - Inline GitHub Actions workflow recipe in
docs/providers/github.mdandREADME.md. Usesactions/setup-python+uvx semvertag tag. Workflow needspermissions: contents: writeto use the auto-issuedGITHUB_TOKEN; or supply a PAT withcontents: write(fine-grained) /repo/public_repo(classic) asSEMVERTAG_TOKEN.
What's new under the hood
Internal cleanups that landed alongside the provider:
_link_paginationmodule — RFC 8288 Link-header walking extracted fromproviders/gitlab.py(GitHub paginates identically; one helper now serves both)._translate_transportshared helper inproviders/_errors.py— the transport-error translation branches (DecodeError,TimeoutError,NetworkError,RetryBudgetExhaustedError, fallback) were uniformly"<provider> <description>"between GitLab and GitHub, so they parameterize cleanly onprovider_label: str. Per-status translation stays per-provider because the actionable hints differ.Settings._resolve_providervalidator — runs at construction time; auto-detects from env ifSettings.provideris unset; enforces thatprovider=githubrequiresrepoandprovider=gitlabrequiresproject_id. Replaces the runtime guards that used to live inioc.py.
Behavior notes
Settings()now requires eitherproject_id(gitlab) orrepo(github). Pre-0.3.0 you could constructSettings()with neither, and the runtime guard inioc._build_gitlab_providerwould raiseConfigError("Project id missing. ...")at provider-construction time. The new validator catches the same situation earlier with a clearer message. CI consumers were already settingCI_PROJECT_ID(auto-exported by GitLab CI) so this is enforcing what was implicitly required. Local-dev users runningsemvertag tagagainstgitlab.comwith no--project-idwill see the new validator message instead of the old one. Same exit code (2), more actionable diagnostic.- No retry-policy changes. Retry middleware, backoff, status codes,
RetryBudgetall unchanged from 0.2.x. - No CLI flag removals. All 0.2.x flags still work. Three new ones added;
--tokensemantics extended to route by active provider.
Migration
For most users, no migration needed. GitLab callers see no behavior change.
To start using GitHub:
# .github/workflows/semvertag.yml
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 }}GITHUB_ACTIONS=true auto-resolves --provider github; GITHUB_REPOSITORY auto-resolves --repo. The minimum workflow above is the complete setup for github.com-hosted public/private repos using the workflow-scoped GITHUB_TOKEN.
See docs/providers/github.md for token scopes, fine-grained vs classic PATs, GitHub Enterprise setup, and troubleshooting.
See also
- Spec:
planning/specs/2026-06-08-github-provider-design.md - Plan:
planning/plans/2026-06-08-github-provider.md - PR #4
Known follow-ups
action.ymlcomposite GitHub Action. A one-lineuses: modern-python/semvertag@vX.Y.Zis nicer DX than the inline workflow. Deferred; the inline recipe is the supported path until the composite ships.- Bitbucket provider. Same pattern; separate work.
- GitHub App authentication. PATs and the workflow-scoped
GITHUB_TOKENare supported; GitHub App JWT exchange is not.