Skip to content

Commit 2d02ab7

Browse files
docs(tutorials/gitlab_ci): document Project Access Token (HTTPS) workflow
Adds an alternative authentication option for the GitLab CI bump pipeline that does not require SSH keys, using a GitLab Project Access Token over HTTPS. Also shows release/tag-only jobs to avoid running packaging steps on every commit. Closes #482 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4d99415 commit 2d02ab7

1 file changed

Lines changed: 129 additions & 4 deletions

File tree

docs/tutorials/gitlab_ci.md

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,21 @@ _Goal_: Bump a new version every time that a change occurs on the `master` branc
1212
4. For simplification, we store the software version in a file called `VERSION`. You can use any file that you want as `commitizen` supports it.
1313
5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../commands/bump.md) as well.
1414

15-
### GitLab Configuration
15+
### Authentication options
16+
17+
To let `GitLab CI` runners push the bump commit and tag back to the repository, the runner needs write access. Two common approaches are documented below — pick whichever fits your environment best:
18+
19+
- **[SSH key](#option-a-ssh-key)** — push over `git@`. Requires generating a key pair, storing the private key as a CI variable, and registering the public key as a deploy key.
20+
- **[Project Access Token (HTTPS)](#option-b-project-access-token-https)** — push over `https://`. No SSH client is required; the runner authenticates using a token managed in the GitLab UI.
21+
22+
Both options assume two CI/CD variables holding the git author identity used by the bump commit:
23+
24+
- `CI_EMAIL`
25+
- `CI_USERNAME`
26+
27+
You can create them under your project's `Settings > CI/CD > Variables`.
28+
29+
### Option A: SSH key
1630

1731
To be able to change files and push new changes with `GitLab CI` runners, we need to have a `ssh` key and configure a git user.
1832

@@ -40,16 +54,14 @@ If you have more projects under the same organization, you can reuse the deploy
4054

4155
Tip: If the CI raise some errors, try to unprotect the private key.
4256

43-
### Defining GitLab CI Pipeline
57+
#### Defining the GitLab CI Pipeline (SSH)
4458

4559
1. Create a `.gitlab-ci.yaml` file that contains `stages` and `jobs` configurations. You can find more info [here](https://docs.gitlab.com/ee/ci/quick_start/).
4660

4761
2. Define `stages` and `jobs`. For this example, we define two `stages` with one `job` each one.
4862
- Test the application.
4963
- Auto bump the version. This means changing the file/s that reflects the version, creating a new commit and git tag.
5064

51-
### Stages and Jobs
52-
5365
```yaml
5466
image: docker:latest
5567

@@ -111,3 +123,116 @@ To be able to push using the GitLab runner, we have to set the SSH key, configur
111123
After merging the new changes into master, we have the final result:
112124

113125
![gitlab final ci result](../images/gitlab_ci/gitlab_final_ci_result.png)
126+
127+
### Option B: Project Access Token (HTTPS)
128+
129+
If you cannot or do not want to manage SSH keys (for example, when your runners do not have an SSH client, or when SSH egress is blocked), you can let the runner push back over `HTTPS` using a [GitLab Project Access Token](https://docs.gitlab.com/user/project/settings/project_access_tokens/). This keeps everything inside the GitLab UI — no key generation, no deploy keys.
130+
131+
!!! note "Group / personal tokens"
132+
The same approach works with [Group Access Tokens](https://docs.gitlab.com/user/group/settings/group_access_tokens/) (handy when several projects share automation) and [Personal Access Tokens](https://docs.gitlab.com/user/profile/personal_access_tokens/). Project Access Tokens are usually preferred because they are scoped to a single project.
133+
134+
!!! warning "`CI_JOB_TOKEN` is not enough"
135+
GitLab's built-in `CI_JOB_TOKEN` cannot push to the repository. You need a Project (or Group / Personal) Access Token with at least the `Developer` role and the `write_repository` scope.
136+
137+
#### Step 1: Create a Project Access Token
138+
139+
1. In your GitLab project, go to `Settings > Access Tokens`.
140+
2. Create a new token:
141+
- **Name**: e.g. `commitizen-bump`.
142+
- **Role**: `Developer` (or higher) — required to push to protected branches and tags.
143+
- **Scopes**: tick `write_repository`. `read_repository` is implied.
144+
- **Expiration date**: pick a date that suits your rotation policy.
145+
3. Click `Create project access token` and **copy the token immediately** — GitLab only shows it once.
146+
147+
#### Step 2: Expose the token to the pipeline
148+
149+
1. Open `Settings > CI/CD > Variables`.
150+
2. Add a new variable:
151+
- **Key**: `GITLAB_TOKEN` (any name works; this tutorial uses `GITLAB_TOKEN`).
152+
- **Value**: the token from Step 1.
153+
- Tick `Masked` so it does not appear in job logs.
154+
- Tick `Protected` if your bump runs only on protected branches/tags.
155+
3. While you are there, make sure `CI_EMAIL` and `CI_USERNAME` variables exist (they configure the git author for the bump commit).
156+
157+
#### Step 3: Allow the token to push to the protected branch
158+
159+
If `master` (or `main`) is protected, the token's user (a [bot user](https://docs.gitlab.com/user/project/settings/project_access_tokens/#bot-users-for-projects) automatically created with the token) needs permission to push:
160+
161+
- Go to `Settings > Repository > Protected branches`.
162+
- Make sure `Developers + Maintainers` (or at least the role you assigned to the token) is allowed to push.
163+
- Do the same under `Settings > Repository > Protected tags` if you push tags such as `v*`.
164+
165+
#### Step 4: Defining the GitLab CI Pipeline (HTTPS)
166+
167+
The pipeline below mirrors the SSH example but authenticates over HTTPS using the token. It also splits the workflow so that release-only work happens on the bump commit and packaging/publishing only happens once the resulting tag is pushed:
168+
169+
```yaml
170+
image: python:3.10
171+
172+
variables:
173+
# Use the project URL exposed by GitLab so this works for any fork/mirror.
174+
REPO_URL: "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
175+
176+
stages:
177+
- test
178+
- bump
179+
- release
180+
181+
test:
182+
stage: test
183+
script:
184+
- pip install -U pip
185+
- pip install -e .
186+
- python -m pytest
187+
rules:
188+
# Run on every branch and merge request, but skip the bump commit itself.
189+
- if: $CI_COMMIT_MESSAGE =~ /^bump:/
190+
when: never
191+
- when: on_success
192+
193+
bump:
194+
stage: bump
195+
before_script:
196+
- pip install -U commitizen
197+
- git config --global user.email "${CI_EMAIL}"
198+
- git config --global user.name "${CI_USERNAME}"
199+
# Replace the default fetch URL with one that includes the token so we can push.
200+
- git remote set-url origin "${REPO_URL}"
201+
script:
202+
# Re-attach HEAD to the branch (GitLab checks out a detached commit by default).
203+
- git checkout -B "${CI_COMMIT_REF_NAME}"
204+
- cz bump --yes
205+
- git push origin "${CI_COMMIT_REF_NAME}"
206+
- git push origin --tags
207+
rules:
208+
# Only run on the default branch, and never re-bump a bump commit.
209+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE !~ /^bump:/
210+
211+
release:
212+
stage: release
213+
script:
214+
- pip install -U commitizen build twine
215+
- cz changelog --dry-run "${CI_COMMIT_TAG}" > release_notes.md
216+
- python -m build
217+
# Upload the artifacts to your registry of choice; this is just an example.
218+
- twine upload --non-interactive dist/*
219+
rules:
220+
# This job only runs on tag pipelines created by the `bump` job above.
221+
- if: $CI_COMMIT_TAG
222+
artifacts:
223+
paths:
224+
- dist/
225+
- release_notes.md
226+
```
227+
228+
How the pipeline is wired:
229+
230+
- `test` runs on every branch and merge request, but is skipped on the bump commit so we do not waste runners re-testing what was just released.
231+
- `bump` only runs on the default branch and asks `commitizen` to compute the next version, update the version files, write the changelog, commit and tag. The push uses the token via `oauth2:${GITLAB_TOKEN}@…`.
232+
- `release` only runs on tag pipelines (i.e. when the tag pushed by `bump` arrives in GitLab). This is where you would publish artifacts, build and upload a Python package, deploy a Docker image, create a GitLab release, etc.
233+
234+
!!! tip "Avoiding pipeline loops"
235+
The default `cz bump` commit message starts with `bump:`. The `rules:` blocks above use that prefix to skip both the `test` and `bump` jobs on the bump commit. If you customize `bump_message`, update the regex accordingly. You can also add `[skip ci]` to the bump message — see `bump_message` in the [bump command documentation](../commands/bump.md).
236+
237+
!!! tip "Token rotation"
238+
Project Access Tokens expire. Set a calendar reminder before the expiration date to rotate the token and update the `GITLAB_TOKEN` CI/CD variable; otherwise the `bump` job will start failing with `403`/`401` errors.

0 commit comments

Comments
 (0)