app.sources.gitlab

GitLab source adapter: ADR files on the published branch of projects.

Settings (catalog.yaml):

  • base_url – instance URL (https:// only; no credentials, query or fragment; /api/v4 is appended internally).

  • token_env – env var name of the access token. A missing or empty variable disables the registration; the value is sent only in the PRIVATE-TOKEN header and never logged or echoed.

  • scope – {groups, include_subgroups, projects}; at least one group or project is required (the adapter never lists every visible project). include_subgroups defaults to false.

  • published_ref – a branch name, or "@project-default" for each project’s default branch. Other values starting with @ are reserved and rejected.

A project is in scope when its full path is listed in scope.projects, or its parent namespace is a scoped group (any descendant namespace with include_subgroups). Comparison ignores case.

Poll steps:

  1. GitLabAdapter.list_projects() – paginated, non-archived projects of every scoped group (with_shared=false) plus each explicit project; archived and out-of-scope projects are dropped (so a complete listing removes their records), as are empty repositories without a default branch. A missing group/project or a persistently failing request makes the listing incomplete (nothing is deleted); 401/403 fails the poll.

  2. GitLabAdapter.list_documents() – reads the branch head. When the stored tokens prove nothing changed (see below) all documents are reported unchanged without listing the tree. Otherwise the recursive tree under path is listed at the head commit (paginated) and a document is changed only if its blob_id differs from the stored one. A missing ADR path (tree 404) is an empty, complete listing; a missing branch raises SourceNotFoundError, so polling keeps the project’s records.

  3. GitLabAdapter.fetch() – raw blob by blob id; source_url is <web_url>/-/blob/<ref>/<path>. author is always None: the last commit of a file would cost one extra (slow, path-filtered commit log) request per changed file, which is not “cheaply available”.

Change token: <blob_id>:<head_sha>:<count>:<path_tag> where head_sha is the branch head the listing was made at, count the number of ADR files in that listing and path_tag the first 12 hex digits of SHA-256 of the configured path. The tree listing is skipped only if every stored token of the project carries the current head and path tag and the number of stored tokens equals count; a file that failed to fetch, was owned elsewhere or a changed path therefore forces a full listing on the next poll.

Settings parsing lives in app.sources.gitlab_settings; HTTP behaviour (retries, backoff, concurrency, timeouts, pagination) in app.sources.gitlab_api.

Public API: PROJECT_DEFAULT, validate_settings(), GitLabScope, GitLabAdapter.

class app.sources.gitlab.GitLabAdapter(*, api, config)

Bases: object

Reads ADR files from the published branch of GitLab projects.

Parameters:
  • api (GitLabApi) – API access for this poll.

  • config (GitLabConfig) – Validated registration settings.

async list_projects()

List the non-archived, in-scope projects.

Return type:

ProjectListing

Returns:

The listing; incomplete when a group or project could not be read.

Raises:

SourceAccessError – If GitLab refuses the token.

async list_documents(project, previous_tokens)

List the ADR files of one project at its branch head.

Parameters:
Return type:

DocumentListing

Returns:

The listing.

Raises:
async fetch(document)

Read one document’s raw blob.

Parameters:

document (SourceDocument) – A document from list_documents().

Return type:

FetchedDocument

Returns:

Its exact bytes and web URL; author is None.

Raises:

SourceError – If the blob cannot be read.

class app.sources.gitlab.GitLabScope(groups, include_subgroups, projects)

Bases: object

Which projects a registration owns.

Parameters:
  • groups (frozenset[str]) – Casefolded group paths.

  • include_subgroups (bool) – Whether descendant namespaces count.

  • projects (frozenset[str]) – Casefolded explicit project paths.

groups: frozenset[str]
include_subgroups: bool
projects: frozenset[str]
contains(project)

Tell whether a project path is in scope.

Parameters:

project (str) – Full project path (group/sub/project).

Return type:

bool

Returns:

True if listed explicitly or under a scoped group.

app.sources.gitlab.validate_settings(settings, env, *, transport=None, sleep=<function sleep>, ssl_context=None)

Validate a gitlab registration and bind the adapter factory.

No network I/O happens here (see app.sources.gitlab_settings.parse_settings()); the HTTP client is created (and closed) by open_adapter for each poll.

Parameters:
  • settings (SourceSettings) – Common settings plus options (base_url, token_env) and scope.

  • env (Mapping[str, str]) – Environment used to resolve token_env.

  • transport (AsyncBaseTransport | None) – HTTP transport override (tests).

  • sleep (Sleep) – Async sleep for retry waits (tests).

  • ssl_context (SSLContext | None) – TLS context of the HTTP client (see app.tls); None uses the default outbound context.

Return type:

SourceBinding

Returns:

The binding.

Raises:

app.sources.port.SourceConfigError – If the settings are invalid or the token env var is not set.