From 937488f55eb84369dd26020f620a12008fab6168 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sat, 3 Oct 2026 17:25:22 +0300 Subject: [PATCH] docs: prose pass, fix merge-mark wording, refresh social card --- README.md | 35 +++--- docs/assets/social-card.png | Bin 11677 -> 11720 bytes docs/index.md | 26 ++-- docs/providers/github.md | 158 ++++++++++++------------ docs/providers/gitlab.md | 112 ++++++++--------- docs/strategies/branch-prefix.md | 27 ++-- docs/strategies/conventional-commits.md | 12 +- mkdocs.yml | 2 +- 8 files changed, 190 insertions(+), 182 deletions(-) diff --git a/README.md b/README.md index f035984..7c15a6b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty) -Auto-tag your GitLab or GitHub repository with semantic version tags from CI — one tool, two strategies, two providers. +Auto-tag your GitLab or GitHub repository with semantic version tags from CI. One tool covers both providers and offers two bump strategies. ## Install @@ -51,8 +51,8 @@ semvertag: ``` It runs `uvx semvertag tag` against your repo on the default branch. -semvertag inspects the head commit + tag history, decides the -appropriate semver bump, and creates the new tag via the GitLab API. +semvertag inspects the head commit and the tag history, decides the +semver bump, and creates the new tag through the GitLab API. > A one-line `include: - component: …` via the GitLab CI Catalog will > replace this snippet once the component is published. For now, paste @@ -86,28 +86,29 @@ GitHub Enterprise setup, outputs, and troubleshooting. ## Strategies -- **branch-prefix** (default): the head commit on the default branch - must be a merge commit whose subject names a `feature/` (minor), - `bugfix/`, or `hotfix/` (patch) branch. -- **conventional-commits**: parses the head commit's - [Conventional Commits](https://www.conventionalcommits.org/) - header (`feat:` minor, `fix:`/`perf:` patch, `!` or `BREAKING - CHANGE:` major). +The default `branch-prefix` strategy requires the head commit on the +default branch to be a merge commit whose subject names a `feature/` +(minor), `bugfix/`, or `hotfix/` (patch) branch. -Both are configurable via env vars. See [docs](https://semvertag.modern-python.org) +The `conventional-commits` strategy parses the head commit's +[Conventional Commits](https://www.conventionalcommits.org/) +header (`feat:` minor, `fix:`/`perf:` patch, `!` or `BREAKING +CHANGE:` major). + +Both are configurable through environment variables. See [docs](https://semvertag.modern-python.org) for the full configuration surface. ## Built with semvertag stands on other `modern-python` libraries: -- **[modern-di-typer](https://github.com/modern-python/modern-di-typer)** — - dependency-injection wiring for the Typer CLI. semvertag resolves its +- [modern-di-typer](https://github.com/modern-python/modern-di-typer) wires + dependency injection into the Typer CLI. semvertag resolves its settings, API providers, and bump strategies through a `modern_di` container ([`semvertag/ioc.py`](https://github.com/modern-python/semvertag/blob/main/semvertag/ioc.py)). -- **[httpware](https://github.com/modern-python/httpware)** — the resilient - HTTP client both providers use for the GitLab/GitHub REST calls (retries, - timeouts, typed decoding, secret redaction). +- [httpware](https://github.com/modern-python/httpware) is the HTTP client + both providers use for the GitLab/GitHub REST calls. It handles retries, + timeouts, typed decoding, and secret redaction. ## 📚 [Documentation](https://semvertag.modern-python.org) @@ -118,4 +119,4 @@ semvertag stands on other `modern-python` libraries: ## Part of `modern-python` Browse the full list of templates and libraries in -[`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index. +[`modern-python`](https://github.com/modern-python). The org profile has the categorized index. diff --git a/docs/assets/social-card.png b/docs/assets/social-card.png index 14a67227b378e90ded6cfc15442881442ec14019..11c9c841e0b2d92535d8da8cfea52828e24a552c 100644 GIT binary patch literal 11720 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 xd>-w;kodQPX;A)Z!T-?he-=S=Yr -Auto-tag your GitLab repository with semantic version tags from CI — -one tool, two strategies. +Auto-tag your GitLab repository with semantic version tags from CI, +using one of two bump strategies. -semvertag reads the head commit and tag history from your GitLab -project via the API, decides the appropriate semver bump based on the -strategy you've configured, and creates the new git tag — all from a -single command in your CI pipeline. +From a single command in your CI pipeline, semvertag reads the head +commit and tag history from your GitLab project through the API, +decides the semver bump with the strategy you've configured, and +creates the new git tag. ## Quick start @@ -52,13 +52,13 @@ SEMVERTAG_PROJECT_ID= \ semvertag ships with two bump-decision strategies: -- [**branch-prefix**](strategies/branch-prefix.md) — bump based on the - source branch of the latest merge commit (`feature/` → minor, - `bugfix/` / `hotfix/` → patch). The default. -- [**conventional-commits**](strategies/conventional-commits.md) — - bump based on the head commit's Conventional Commits message +- [branch-prefix](strategies/branch-prefix.md), the default, bumps + based on the source branch of the latest merge commit (`feature/` → minor, + `bugfix/` / `hotfix/` → patch). +- [conventional-commits](strategies/conventional-commits.md) bumps + based on the head commit's Conventional Commits message (`feat:` → minor, `fix:` / `perf:` → patch, `!` or `BREAKING CHANGE:` → major). -Both strategies are configurable via environment variables — see the -strategy pages for the full configuration surface. +Both strategies are configurable through environment variables. The +strategy pages cover the full configuration surface. diff --git a/docs/providers/github.md b/docs/providers/github.md index 017a6d9..1b84659 100644 --- a/docs/providers/github.md +++ b/docs/providers/github.md @@ -6,14 +6,14 @@ Use semvertag in GitHub Actions via the published composite action fallback for environments that can't consume the action lives at the bottom of this page. -## Quick Start +## Quick start -The minimum useful workflow: auto-tag on every push to the default +The minimum useful workflow auto-tags on every push to the default branch. -> **Required setup.** Either rely on the workflow-scoped -> `GITHUB_TOKEN` (which is auto-issued per job) — in which case the -> workflow MUST declare `permissions: contents: write` — OR provide a +> The job needs a token with write access. Either rely on the +> workflow-scoped `GITHUB_TOKEN`, which is auto-issued per job, and +> declare `permissions: contents: write` in the workflow, or provide a > fine-grained PAT with `contents: write` (single repo) or a classic > PAT with `repo` / `public_repo` scope. Store the PAT as a repo > secret named `SEMVERTAG_TOKEN`; the alias chain picks it up ahead @@ -40,19 +40,18 @@ a bump is warranted by the configured strategy, creates a new tag ref via the GitHub API. If no bump is warranted, the job exits 0 without pushing. -> **First tag.** semvertag bumps from the highest existing semver tag +> semvertag bumps from the highest existing semver tag > and never creates the first one. It reads only plain semver tags such > as `0.1.0`; a `v` prefix (`v0.1.0`) does not parse and is ignored. Until > one exists, every run reports `no_tags` and exits 0. Push one with > `git tag 0.1.0 && git push origin 0.1.0`. -> **Auto-detection.** semvertag detects GitHub Actions from the -> `GITHUB_ACTIONS=true` env var that GHA sets automatically. The -> `--provider` flag is therefore optional inside GHA — explicit -> `--provider github` is only needed when running outside GHA (e.g. -> on a developer laptop targeting a github.com repo). +> semvertag detects GitHub Actions from the `GITHUB_ACTIONS=true` env +> var that GHA sets automatically, so the `--provider` flag is optional +> inside GHA. Pass `--provider github` explicitly only when running +> outside GHA (e.g. on a developer laptop targeting a github.com repo). -> **No checkout needed.** semvertag reads the head commit and the tag +> semvertag reads the head commit and the tag > history over the GitHub API and never touches the working tree, so > the job needs neither an `actions/checkout` step nor a `fetch-depth` > setting. Add a checkout only if other steps in the same job need the @@ -73,8 +72,8 @@ Pass `--strategy` (or set `SEMVERTAG_STRATEGY`) to one of: strategy: conventional-commits ``` -> **Strategy-specific env vars** (e.g. `SEMVERTAG_BRANCH_PREFIX__MINOR`) -> remain configured on the calling step. The composite action only +> Strategy-specific env vars (e.g. `SEMVERTAG_BRANCH_PREFIX__MINOR`) +> stay configured on the calling step. The composite action only > explicitly sets `GITHUB_TOKEN` and `SEMVERTAG_STRATEGY`; every other > env var on the calling step passes through to the action's run step. > @@ -86,7 +85,7 @@ Pass `--strategy` (or set `SEMVERTAG_STRATEGY`) to one of: ## Required permissions -The job creates a tag ref, so the token it uses MUST carry write +The job creates a tag ref, so the token it uses must carry write access to the repository's contents. semvertag reads the token from these env vars in order: `SEMVERTAG_GITHUB__TOKEN`, `SEMVERTAG_TOKEN`, `GITHUB_TOKEN`. The @@ -100,7 +99,7 @@ When you give the step an `id:`, downstream steps can read three outputs: |---|---| | `tag` | The created tag (e.g. `1.2.3`), or empty string when `status` is `no-bump`. | | `bump` | `none` \| `patch` \| `minor` \| `major`. | -| `status` | `created` (tag pushed) \| `no-bump` (nothing to tag — no prior tag, already tagged, no merge commit, or non-conforming commit). On CLI error the action itself exits non-zero and this output is not written. | +| `status` | `created` (tag pushed) \| `no-bump` (nothing to tag: no prior tag, already tagged, no merge commit, or non-conforming commit). On CLI error the action itself exits non-zero and this output is not written. | Example: trigger a downstream release-notes job only when a tag was created. @@ -127,7 +126,7 @@ jobs: ## Preview the next bump -Pass `dry-run: true` to compute the bump without pushing a tag — useful in +Pass `dry-run: true` to compute the bump without pushing a tag. Use it in CI smoke tests, in PR previews, or to see what the next release would be: ```yaml @@ -158,35 +157,33 @@ Output (example): Three cases govern which token the job should use: -- **Workflow-scoped `GITHUB_TOKEN`** (preferred for most projects). +- The workflow-scoped `GITHUB_TOKEN` is preferred for most projects. GitHub Actions issues a fresh token per job; it inherits the workflow's `permissions:` block. Add `permissions: contents: write` at the workflow level (as in the snippet above). The token is auto-exported as `GITHUB_TOKEN` and picked up by the alias chain. -- **Fine-grained PAT scoped to the single repository.** Required - scope: `Contents: Read and write`. Store as a repo secret named +- A fine-grained PAT scoped to the single repository needs the + `Contents: Read and write` scope. Store it as a repo secret named `SEMVERTAG_TOKEN`; the alias chain picks it up ahead of `GITHUB_TOKEN`. Use this when the workflow runs across organizations or needs scopes the workflow token can't grant. -- **Classic PAT.** Required scope: `repo` (private repos) or - `public_repo` (public repos only). Same storage shape as the - fine-grained PAT. Less preferred — classic PATs bleed scope - across all of the user's repos. - -> **Masking caveat.** Because the alias chain reads -> `SEMVERTAG_GITHUB__TOKEN` → `SEMVERTAG_TOKEN` → `GITHUB_TOKEN` in -> order and the first set value wins, a stale `SEMVERTAG_TOKEN` left -> over from a prior PAT-based setup will silently override the -> workflow's `GITHUB_TOKEN`. If you migrate from PAT → -> workflow-token, unset `SEMVERTAG_TOKEN` from the repo's secrets. - -**GitHub Enterprise**: set `SEMVERTAG_GITHUB__ENDPOINT` (note the -double underscore — pydantic-settings uses `__` as the nested-key -delimiter, so `SEMVERTAG_GITHUB_ENDPOINT` with a single underscore is -silently ignored) as a workflow-level env or a repo secret pointing -to the instance's API root, e.g. -`https://github.example.com/api/v3`. The default is -`https://api.github.com`. +- A classic PAT needs the `repo` (private repos) or `public_repo` + (public repos only) scope and is stored the same way as the + fine-grained PAT. It is less preferred because classic PATs bleed + scope across all of the user's repos. + +> The alias chain reads `SEMVERTAG_GITHUB__TOKEN` → `SEMVERTAG_TOKEN` +> → `GITHUB_TOKEN` in order and the first set value wins, so a stale +> `SEMVERTAG_TOKEN` left over from a prior PAT-based setup will +> silently override the workflow's `GITHUB_TOKEN`. If you migrate from +> PAT → workflow-token, unset `SEMVERTAG_TOKEN` from the repo's secrets. + +For GitHub Enterprise, set `SEMVERTAG_GITHUB__ENDPOINT` as a +workflow-level env or a repo secret pointing to the instance's API +root, e.g. `https://github.example.com/api/v3`. The default is +`https://api.github.com`. Note the double underscore: pydantic-settings +uses `__` as the nested-key delimiter, so `SEMVERTAG_GITHUB_ENDPOINT` +with a single underscore is silently ignored. For most consumers on `github.com`-hosted repos with the workflow-scoped `GITHUB_TOKEN`, the minimal workflow snippet above @@ -218,10 +215,10 @@ for the full type-to-bump mapping. ## Without the composite action -If your environment can't consume the action — GitHub Enterprise +If your environment can't consume the action (GitHub Enterprise instances without Marketplace access, security-constrained orgs that forbid third-party actions, or anyone who wants explicit control over -the uv install step — paste the pure-CLI recipe instead: +the uv install step), paste the pure-CLI recipe instead: ```yaml jobs: @@ -239,44 +236,51 @@ jobs: The behavior matches the composite action exactly; only the install shape differs. Strategy is set via env (`SEMVERTAG_STRATEGY`) or CLI -flag (`--strategy …`). No outputs are produced in this shape — read +flag (`--strategy …`). This shape produces no outputs. Read the CLI stdout, or invoke `semvertag tag --json` and parse the envelope yourself. ## Troubleshooting -- **`Token rejected: 401. Verify SEMVERTAG_TOKEN is valid.`** — the - token is malformed, expired, or revoked. Verify in GitHub UI - (Settings → Developer settings → Personal access tokens) or - rotate the workflow secret. When using the composite action, - `GITHUB_TOKEN` is set automatically from the `token` input (which - defaults to `${{ github.token }}`). When using the pure-CLI recipe - in "Without the composite action", add - `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` to the run step. - -- **`Token missing scope or insufficient permission: 403`** — the - token lacks `contents: write` (fine-grained / workflow-scoped) or - `repo` / `public_repo` (classic). For workflow-scoped tokens, - add `permissions: contents: write` at the workflow level. For PATs, - re-issue with the right scope. - -- **`GitHub repo not found: repo='...'`** — `GITHUB_REPOSITORY` was - not exported, or `--repo OWNER/REPO` was not passed. Inside GHA, - `GITHUB_REPOSITORY` is auto-exported in every job; outside GHA, - set it explicitly. - -- **`Tag already exists: 'v...'`** — a previous run (or a concurrent - run) already created this tag. semvertag refuses to silently - succeed on a duplicate. Roll forward by pushing another commit - that changes the bump, or delete the duplicate tag. - -- **GitHub Enterprise, but the job connects to `api.github.com`** — - the default endpoint is `https://api.github.com`. Set - `SEMVERTAG_GITHUB__ENDPOINT` (note the double underscore) as a - workflow-level env pointing to the instance's API root, e.g. - `https://github.example.com/api/v3`. - -- **A bump-worthy push was never tagged** — the run for that push - failed or was skipped. Re-run it. Each run judges only the head - commit of its own push and does not look back, so the next push - cannot recover an earlier bump. +### `Token rejected: 401. Verify SEMVERTAG_TOKEN is valid.` + +The token is malformed, expired, or revoked. Verify it in the GitHub UI +(Settings → Developer settings → Personal access tokens) or rotate the +workflow secret. When using the composite action, `GITHUB_TOKEN` is set +automatically from the `token` input (which defaults to +`${{ github.token }}`). When using the pure-CLI recipe in "Without the +composite action", add `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` +to the run step. + +### `Token missing scope or insufficient permission: 403` + +The token lacks `contents: write` (fine-grained / workflow-scoped) or +`repo` / `public_repo` (classic). For workflow-scoped tokens, add +`permissions: contents: write` at the workflow level. For PATs, +re-issue with the right scope. + +### `GitHub repo not found: repo='...'` + +`GITHUB_REPOSITORY` was not exported, or `--repo OWNER/REPO` was not +passed. Inside GHA, `GITHUB_REPOSITORY` is auto-exported in every job; +outside GHA, set it explicitly. + +### `Tag already exists: 'v...'` + +A previous run (or a concurrent run) already created this tag. +semvertag refuses to silently succeed on a duplicate. Roll forward by +pushing another commit that changes the bump, or delete the duplicate +tag. + +### GitHub Enterprise, but the job connects to `api.github.com` + +The default endpoint is `https://api.github.com`. Set +`SEMVERTAG_GITHUB__ENDPOINT` (note the double underscore) as a +workflow-level env pointing to the instance's API root, e.g. +`https://github.example.com/api/v3`. + +### A bump-worthy push was never tagged + +The run for that push failed or was skipped. Re-run it. Each run judges +only the head commit of its own push and does not look back, so the +next push cannot recover an earlier bump. diff --git a/docs/providers/gitlab.md b/docs/providers/gitlab.md index 4d80041..25825bf 100644 --- a/docs/providers/gitlab.md +++ b/docs/providers/gitlab.md @@ -1,22 +1,21 @@ # GitLab CI Use semvertag in GitLab CI via a small inline job that installs `uv` -and runs `uvx semvertag tag`. No PyPI install in your repo, no -maintained pipeline YAML beyond the snippet below. +and runs `uvx semvertag tag`. Your repo needs no PyPI install and no +pipeline YAML beyond the snippet below. -> **Catalog component pending.** A one-line `include: - component: …` -> via the GitLab CI Catalog is the eventual delivery path — the -> descriptor lives at -> [`templates/semvertag.yml`](https://github.com/modern-python/semvertag/blob/main/templates/semvertag.yml) -> — but the component has not yet been published to gitlab.com's -> Catalog. Paste the job below into `.gitlab-ci.yml` until then. +> A one-line `include: - component: …` via the GitLab CI Catalog is the +> eventual delivery path, and its descriptor lives at +> [`templates/semvertag.yml`](https://github.com/modern-python/semvertag/blob/main/templates/semvertag.yml). +> The component has not yet been published to gitlab.com's Catalog. +> Paste the job below into `.gitlab-ci.yml` until then. -## Quick Start +## Quick start -The minimum useful pipeline: auto-tag on every push to the default +The minimum useful pipeline auto-tags on every push to the default branch. -> **Required setup.** Set `SEMVERTAG_TOKEN` as a project-level masked +> Set `SEMVERTAG_TOKEN` as a project-level masked > CI/CD variable holding a Project Access Token (or Personal Access > Token) with `api` + `write_repository` scope. `CI_JOB_TOKEN` works > on projects where the job-token write scope is opted in @@ -44,16 +43,15 @@ bump is warranted by the configured strategy, pushes a new tag to the project's `origin`. If no bump is warranted, the job exits 0 without pushing. -> **First tag.** semvertag bumps from the highest existing semver tag +> semvertag bumps from the highest existing semver tag > and never creates the first one. It reads only plain semver tags such > as `0.1.0`; a `v` prefix (`v0.1.0`) does not parse and is ignored. Until > one exists, every run reports `no_tags` and exits 0. Push one with > `git tag 0.1.0 && git push origin 0.1.0`. -> **Concurrency default.** `resource_group: semvertag` makes GitLab -> serialize concurrent `semvertag` jobs across pipelines on the same -> project — back-to-back pushes will queue rather than race the -> `create_tag` API. Drop or rename the group if you intentionally want +> `resource_group: semvertag` makes GitLab serialize concurrent +> `semvertag` jobs across pipelines on the same project, so +> back-to-back pushes queue instead of racing the `create_tag` API. Drop or rename the group if you intentionally want > concurrent tag pushes. ## Strategy @@ -73,7 +71,7 @@ block on the `include:`. The values and default match ## Required permissions -The job pushes a tag, so the token it uses MUST carry write access to +The job pushes a tag, so the token it uses must carry write access to the repository. semvertag reads the token from these env vars in order: `SEMVERTAG_GITLAB__TOKEN`, `SEMVERTAG_TOKEN`, `CI_JOB_TOKEN`, `GITLAB_TOKEN`. The first set value wins. @@ -82,13 +80,13 @@ vars in order: `SEMVERTAG_GITLAB__TOKEN`, `SEMVERTAG_TOKEN`, Two cases govern which token the job should use: -- **GitLab projects where the maintainer has opted in to job-token - write scope** (Settings → CI/CD → Token Permissions → *Allow access - from the project's token to write to the repository*). `CI_JOB_TOKEN` +- On GitLab projects where the maintainer has opted in to job-token + write scope (Settings → CI/CD → Token Permissions → *Allow access + from the project's token to write to the repository*), `CI_JOB_TOKEN` is auto-exported into every CI job and gets picked up by the alias - chain — no further configuration needed. -- **Projects that have NOT opted in**, or projects on older GitLab - versions where `CI_JOB_TOKEN` was scoped read-only by default. The + chain with no further configuration. +- On projects that have not opted in, or projects on older GitLab + versions where `CI_JOB_TOKEN` was scoped read-only by default, the consumer creates a Project Access Token (preferred; scoped to the one project) or a Personal Access Token (works but bleeds the user's scope across all their projects). Token scopes required: @@ -96,22 +94,23 @@ Two cases govern which token the job should use: variable named `SEMVERTAG_TOKEN`; the alias chain picks it up ahead of `CI_JOB_TOKEN`. -> **Masking caveat.** Because the alias chain reads -> `SEMVERTAG_GITLAB__TOKEN` → `SEMVERTAG_TOKEN` → `CI_JOB_TOKEN` → -> `GITLAB_TOKEN` in order and the first set value wins, a stale +> The alias chain reads `SEMVERTAG_GITLAB__TOKEN` → `SEMVERTAG_TOKEN` +> → `CI_JOB_TOKEN` → `GITLAB_TOKEN` in order and the first set value +> wins, so a stale > `SEMVERTAG_TOKEN` left over from a prior PAT-based setup will > silently override a freshly-rotated `CI_JOB_TOKEN`. If you migrate > from PAT → job-token, unset `SEMVERTAG_TOKEN` (or rotate its value > to empty) in the project's CI/CD variables. -**Self-hosted GitLab**: set `SEMVERTAG_GITLAB__ENDPOINT` (note the -double underscore — pydantic-settings uses `__` as the nested-key -delimiter, so `SEMVERTAG_GITLAB_ENDPOINT` with a single underscore is -silently ignored) as a project CI/CD variable pointing to the -instance's API root, e.g. `https://gitlab.example.com`. The default -is `https://gitlab.com` and is not auto-derived from `CI_SERVER_FQDN`. +For self-hosted GitLab, set `SEMVERTAG_GITLAB__ENDPOINT` as a project +CI/CD variable pointing to the instance's API root, e.g. +`https://gitlab.example.com`. The default is `https://gitlab.com` and +is not auto-derived from `CI_SERVER_FQDN`. Note the double underscore: +pydantic-settings uses `__` as the nested-key delimiter, so +`SEMVERTAG_GITLAB_ENDPOINT` with a single underscore is silently +ignored. -> **Endpoint shape.** Use scheme + host only. Do NOT append `/api/v4` +> Use scheme + host only for the endpoint. Do not append `/api/v4` > (the client adds it); a value like `https://gitlab.example.com/api/v4` > produces `…/api/v4/api/v4/…` URLs and 404s. A missing scheme > (`gitlab.example.com`) fails at request time with httpx @@ -158,25 +157,28 @@ semvertag: ## Troubleshooting -- **`Token missing scope or insufficient permission: 403`** — the - token does not have `api` + `write_repository` scope, or the - project's protected-tag rules disallow the bot from creating tags. - Verify the `SEMVERTAG_TOKEN` scopes in GitLab UI (Settings → Access - Tokens). - -- **`Project id missing. Set CI_PROJECT_ID or pass --project-id.`** — - the CI runner did not export `CI_PROJECT_ID` (the variable is - exported by every standard GitLab CI job; a custom executor that - strips CI variables would suppress it). Set `SEMVERTAG_PROJECT_ID` - as a project-level CI/CD variable as the override. - -- **Self-hosted GitLab, but the job connects to `gitlab.com`** - — the default endpoint is `https://gitlab.com` and is not - auto-derived from `CI_SERVER_FQDN`. Set - `SEMVERTAG_GITLAB__ENDPOINT` as a project-level CI/CD variable - pointing to the instance's API root. - -- **A bump-worthy push was never tagged** — the run for that push - failed or was skipped. Re-run it. Each run judges only the head - commit of its own push and does not look back, so the next push - cannot recover an earlier bump. +### `Token missing scope or insufficient permission: 403` + +The token does not have `api` + `write_repository` scope, or the +project's protected-tag rules disallow the bot from creating tags. +Verify the `SEMVERTAG_TOKEN` scopes in the GitLab UI (Settings → Access +Tokens). + +### `Project id missing. Set CI_PROJECT_ID or pass --project-id.` + +The CI runner did not export `CI_PROJECT_ID`. Every standard GitLab CI +job exports the variable, but a custom executor that strips CI +variables would suppress it. Set `SEMVERTAG_PROJECT_ID` as a +project-level CI/CD variable as the override. + +### Self-hosted GitLab, but the job connects to `gitlab.com` + +The default endpoint is `https://gitlab.com` and is not auto-derived +from `CI_SERVER_FQDN`. Set `SEMVERTAG_GITLAB__ENDPOINT` as a +project-level CI/CD variable pointing to the instance's API root. + +### A bump-worthy push was never tagged + +The run for that push failed or was skipped. Re-run it. Each run judges +only the head commit of its own push and does not look back, so the +next push cannot recover an earlier bump. diff --git a/docs/strategies/branch-prefix.md b/docs/strategies/branch-prefix.md index 5d3c2af..5ed1032 100644 --- a/docs/strategies/branch-prefix.md +++ b/docs/strategies/branch-prefix.md @@ -16,17 +16,18 @@ behavior. | anything else | none | A bump of `none` means the commit contributes nothing to the release -decision. Major bumps are not produced by `branch-prefix` — promote +decision. `branch-prefix` never produces a major bump. Promote to a new major version manually, or switch to -[Conventional Commits](conventional-commits.md) which recognizes +[Conventional Commits](conventional-commits.md), which recognizes `feat!` and `BREAKING CHANGE:`. ## Merge-commit detection -The strategy only fires on commits whose subject contains the literal -string `Merge branch` (the default `git merge` subject). Commits -without one of those marks return `none` regardless of prefix. This -means: +The strategy only fires on commits whose subject contains one of the +default merge marks: the literal string `Merge branch` (the default +`git merge` subject) or `Merge pull request` (GitHub's merge-commit +subject). Commits without one of those marks return `none` regardless +of prefix. For example: - Standard `git merge feature/foo` → subject `Merge branch 'feature/foo' into main` → bump = minor ✓ - GitHub's `Merge pull request #N from user/feature/foo` → bump = minor ✓ @@ -42,20 +43,20 @@ merge-commit conventions (e.g. squash-merge prefixes). The strategy reads its prefixes from the application's settings layer: -- `minor` — tuple of prefixes that trigger a minor bump (default +- `minor`: tuple of prefixes that trigger a minor bump (default `("feature/",)`). -- `patch` — tuple of prefixes that trigger a patch bump (default +- `patch`: tuple of prefixes that trigger a patch bump (default `("bugfix/", "hotfix/")`). -- `merge_mark_texts` — tuple of substrings that mark a subject as a +- `merge_mark_texts`: tuple of substrings that mark a subject as a merge commit (default `("Merge branch", "Merge pull request")`). -- `patch_on_non_merge_commit` — when `true`, a plain (non-merge) commit on +- `patch_on_non_merge_commit`: when `true`, a plain (non-merge) commit on the default branch bumps patch instead of producing no bump (default `false`). Set via `SEMVERTAG_BRANCH_PREFIX__PATCH_ON_NON_MERGE_COMMIT=true`. Affects only the non-merge case; a merge commit with an unrecognized prefix still produces no bump. These are set via the same pydantic-settings env-var mechanism used -for tokens / endpoints — see the provider docs for the variable +for tokens / endpoints. The provider docs describe the variable naming convention. ## Head commit only @@ -69,7 +70,7 @@ the earlier bump is not recovered. If your team commits Conventional Commits messages directly to the default branch (without merge commits), switch to -[Conventional Commits](conventional-commits.md) — that strategy +[Conventional Commits](conventional-commits.md). That strategy reads the head commit's subject and body and does not depend on merge metadata. @@ -78,5 +79,5 @@ merge metadata. The strategy is selected per project via the `strategy:` input on the relevant provider's component / action. See: -- [GitLab CI](../providers/gitlab.md) — set `strategy: branch-prefix` +- [GitLab CI](../providers/gitlab.md): set `strategy: branch-prefix` on the `include: - component:` block. diff --git a/docs/strategies/conventional-commits.md b/docs/strategies/conventional-commits.md index 024f59d..8d27e3f 100644 --- a/docs/strategies/conventional-commits.md +++ b/docs/strategies/conventional-commits.md @@ -19,7 +19,7 @@ commits since the latest tag; see [Head commit only](#head-commit-only). The grammar checked is `^(type)(?:\((scope)\))?(!)?:`. Anything not matching this pattern returns `none`. The `!` marker takes precedence -over the type — `chore!:` is a major bump even though `chore` is +over the type: `chore!:` is a major bump even though `chore` is otherwise unmapped. ## Customizing the type lists @@ -27,9 +27,9 @@ otherwise unmapped. The strategy reads its type lists from the application's settings layer: -- `minor_types` — tuple of types that trigger a minor bump (default +- `minor_types`: tuple of types that trigger a minor bump (default `("feat",)`). -- `patch_types` — tuple of types that trigger a patch bump (default +- `patch_types`: tuple of types that trigger a patch bump (default `("fix", "perf")`). Both lists are validated against the lowercase-letters-only regex @@ -58,13 +58,13 @@ earlier bump is not recovered. If your team merges via short-lived prefixed branches (`feature/...`, `bugfix/...`) and does not enforce Conventional Commits on each -commit, switch to [Branch prefix](branch-prefix.md) — it reads the -merge commit's source branch rather than the per-commit subject. +commit, switch to [Branch prefix](branch-prefix.md), which reads the +merge commit's source branch instead of the per-commit subject. ## Consumer integration The strategy is selected per project via the `strategy:` input on the relevant provider's component / action. See: -- [GitLab CI](../providers/gitlab.md) — set +- [GitLab CI](../providers/gitlab.md): set `strategy: conventional-commits` on the `include: - component:` block. diff --git a/mkdocs.yml b/mkdocs.yml index 5b7ead7..b2d8ee3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,7 +4,7 @@ repo_url: https://github.com/modern-python/semvertag docs_dir: docs edit_uri: edit/main/docs/ nav: - - Quick Start: index.md + - Quick start: index.md - Providers: - GitLab CI: providers/gitlab.md - GitHub Actions: providers/github.md