mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 00:02:13 +00:00
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1xHmkxuxYb1GYvth1BS75
100 lines
5.0 KiB
Markdown
100 lines
5.0 KiB
Markdown
# Git sync with GitLab
|
|
|
|
GitLab has no equivalent of a GitHub App, so there is nothing to install and no
|
|
consent screen. What Windmill needs instead is one credential you create in
|
|
GitLab and paste once. With it, a GitLab repository gets the same managed
|
|
features an app-backed GitHub repository has: instant pull over a webhook, merge
|
|
requests opened on deploy, and a diff preview posted onto the merge request.
|
|
|
|
## The credential
|
|
|
|
Create a **group access token** on the group that owns the project (Settings →
|
|
Access tokens), or a **group service account** and a personal access token for
|
|
it. Either one is a bot identity that outlives the person who created it, which
|
|
is what you want for a credential the instance uses unattended.
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| Scope | `api` |
|
|
| Role | Developer to push deploy branches; **Maintainer** to also manage the webhook and open merge requests |
|
|
| Expiry | Required for a group access token; a group service account PAT can be non-expiring on self-managed (see below) |
|
|
|
|
The `api` scope is what makes the token rotatable, so Windmill can renew it
|
|
before it expires. A `write_repository`-only token can still push, but Windmill
|
|
cannot inspect or renew it and reports that in the workspace's git sync settings.
|
|
|
|
## Connecting a repository
|
|
|
|
In the resource form for a `git_repository` resource, use the **GitLab** button:
|
|
paste the instance URL and the token, pick a project from the list, and Windmill
|
|
stores the whole remote URL, credential included, in a **secret variable** and
|
|
points the resource at it (`"url": "$var:u/you/gitlab_group_project_url"`).
|
|
|
|
The variable indirection is what makes renewal possible: when Windmill rotates
|
|
the token it rewrites that one variable, and everything referencing it keeps
|
|
working. A URL pasted directly into the resource also syncs, but nothing can
|
|
renew it.
|
|
|
|
## Expiry and renewal
|
|
|
|
Windmill reads `expires_at` from the token itself and shows it on the repository
|
|
in the workspace's git sync settings. Within three weeks of expiry it rotates the
|
|
token through GitLab's own `POST /personal_access_tokens/self/rotate`, writes the
|
|
replacement back to the variable, and verifies it. Only the token can rotate
|
|
itself, so a token without `api` (or `self_rotate`) is a permanent warning rather
|
|
than something Windmill can fix.
|
|
|
|
Rotation is deliberately never retried. GitLab revokes the old token the instant
|
|
it issues the replacement, and presenting an already-rotated token to `/rotate`
|
|
again is treated as reuse: it revokes **the whole token family, including the
|
|
live replacement**. So a rotation that succeeded at GitLab but failed to persist
|
|
is surfaced as an error to act on, not retried.
|
|
|
|
Non-expiring tokens are possible only for a **group service account PAT** on
|
|
self-managed, with `require_personal_access_token_expiry` turned off in the
|
|
instance's application settings. A group access token is always rejected without
|
|
an `expires_at`.
|
|
|
|
## What each managed feature needs
|
|
|
|
| Feature | Needs |
|
|
| --- | --- |
|
|
| Instant pull | A project hook Windmill creates, so Maintainer; and a Windmill base URL GitLab can reach |
|
|
| Merge requests on deploy | Developer, plus the `api` scope |
|
|
| Diff preview on a merge request | The project hook, plus permission to post commit statuses and merge request notes |
|
|
|
|
Instant pull falls back to checking the tracked branch about every minute when
|
|
the hook cannot be created or delivered, so nothing silently stops syncing.
|
|
|
|
## Self-managed differences
|
|
|
|
**Webhooks to a private network are blocked by default.** GitLab refuses to
|
|
create a hook pointing at a private or local address until an administrator
|
|
enables *Allow requests to the local network from webhooks and integrations*
|
|
(Admin → Settings → Network → Outbound requests,
|
|
`allow_local_requests_from_web_hooks_and_services`). A Windmill instance on the
|
|
same private network as GitLab needs this; without it, hook creation fails with a
|
|
"blocked" error and the repository keeps polling.
|
|
|
|
Everything else is identical: Windmill talks to `<your-gitlab>/api/v4` and needs
|
|
no inbound access of its own beyond the hook deliveries.
|
|
|
|
## Commit statuses create a pipeline
|
|
|
|
GitLab has no separate check-run concept. Windmill's `Windmill diff` and
|
|
`Windmill` statuses are **commit statuses**, and posting one creates an `external`
|
|
pipeline on the project. Two consequences:
|
|
|
|
- A project with *Pipelines must succeed* set will not let a merge request merge
|
|
while a Windmill status is still running, and will block it if the status
|
|
failed. Windmill therefore always drives a status it created to a terminal
|
|
state, and reports an informational result (for example "3 changes to deploy")
|
|
as success rather than leaving it pending.
|
|
- `allow_failure` is ignored on the commit-status endpoint, so a Windmill status
|
|
cannot be made advisory. If you do not want it gating merges, turn the diff
|
|
preview off rather than expecting it to be non-blocking.
|
|
|
|
A commit status carries only a name, a 255-character description and a link, so
|
|
the diff itself goes in a merge request note that Windmill keeps up to date, and
|
|
the status links to the job.
|