Forge (GitHub/GitLab)¶
Forge ABC¶
forge
¶
Git forge abstraction for GitLab and GitHub.
Provides a polymorphic interface for merge/pull request operations,
pipeline status checking, and review comment handling. Follows the
same ABC pattern as agentic_ci.backend and agentic_ci.harness.
Usage::
from agentic_ci.forge import Forge
forge = Forge.detect("https://gitlab.com/org/repo/-/merge_requests/42")
status = forge.mr_status("https://gitlab.com/org/repo/-/merge_requests/42")
Label helpers (callers choose names and create-vs-attach policy)::
forge = Forge.detect(repo_url)
if not forge.label_exists(repo_url, "autofix"):
forge.create_label(repo_url, "autofix")
forge.add_mr_labels(mr_url, ["autofix"])
ForgeError
¶
Bases: Exception
Raised when a forge API operation fails.
Forge
¶
Bases: ABC
Abstract base for git forge (GitLab/GitHub) API operations.
Concrete implementations handle authentication and API differences.
Use Forge.detect(url) to get the right implementation for a URL.
detect(url, *, github_token=None)
classmethod
¶
Return the correct Forge implementation for a URL.
Inspects the hostname to choose between GitLab and GitHub.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Any URL on the forge (repo URL, MR/PR URL, etc.). |
required |
github_token
|
str | None
|
Token for GitHub API authentication. |
None
|
Raises:
| Type | Description |
|---|---|
ForgeError
|
If the URL hostname is not recognized. |
Source code in src/agentic_ci/forge/__init__.py
create_merge_request(repo_url, source_branch, target_branch, title, description, draft=False)
abstractmethod
¶
Create an MR/PR.
Returns (web_url, None) on success or (None, error_msg)
on failure.
Source code in src/agentic_ci/forge/__init__.py
mr_status(mr_url)
abstractmethod
¶
Get MR/PR state, source branch, and pipeline status.
Returns {"state": str, "source_branch": str, "pipeline_status": str}.
State is normalized to "open", "merged", or "closed".
Source code in src/agentic_ci/forge/__init__.py
review_comments(mr_url)
abstractmethod
¶
Get unresolved review comment threads with diff positions.
Returns a list of dicts with keys:
thread_id, file, line, body, author.
general_comments(mr_url, since=None, skip_patterns=None)
abstractmethod
¶
Get general (non-diff-positioned) MR/PR comments.
Returns a list of dicts with keys: author, body, created_at.
Comments created before since (ISO 8601) are excluded.
Comments containing any string in skip_patterns are excluded.
If skip_patterns is None, a default list is used.
Source code in src/agentic_ci/forge/__init__.py
reply(mr_url, thread_id, message)
abstractmethod
¶
resolve(mr_url, thread_id)
abstractmethod
¶
update_description(mr_url, *, title=None, description=None)
abstractmethod
¶
Update an existing MR/PR title and/or description.
Only the provided keyword arguments are updated; omitted fields are left unchanged.
Raises ForgeError on API failure.
Source code in src/agentic_ci/forge/__init__.py
label_exists(repo_url, label)
abstractmethod
¶
Return whether label exists on the repository/project.
Does not create the label. Returns False when the label is
missing (including HTTP 404). Raises ForgeError on other
API failures.
Source code in src/agentic_ci/forge/__init__.py
create_label(repo_url, label, *, color=None, description=None)
abstractmethod
¶
Create a repository/project label.
Callers must opt in explicitly; attaching labels does not
implicitly create them via this API. When color is omitted,
DEFAULT_LABEL_COLOR is used.
Raises ForgeError on API failure (including if the label
already exists).
Source code in src/agentic_ci/forge/__init__.py
add_mr_labels(mr_url, labels)
abstractmethod
¶
Attach labels to an existing MR/PR, preserving current labels.
No-op when labels is empty. Does not check whether labels
exist on the repository first — callers that need that policy
should use :meth:label_exists / :meth:create_label.
Raises ForgeError on API failure.
Source code in src/agentic_ci/forge/__init__.py
pipeline_failures(mr_url)
abstractmethod
¶
Get failed CI job names and log tails.
Returns {"pipeline_status": str, "failed_jobs": [{"name", "id", "log"}]}.
parse_gitlab_mr_url(url)
¶
Parse a GitLab MR URL into (project_path, mr_iid).
Raises ForgeError if the URL does not match the expected pattern.
Source code in src/agentic_ci/forge/__init__.py
parse_github_pr_url(url)
¶
Parse a GitHub PR URL into (owner/repo, pr_number).
Raises ForgeError if the URL does not match the expected pattern.
Source code in src/agentic_ci/forge/__init__.py
repo_path_from_url(url)
¶
Extract the repository path from a GitLab or GitHub URL.
Strips trailing slashes and .git suffixes.
Source code in src/agentic_ci/forge/__init__.py
detect_forge(url, *, github_token=None)
¶
GitHub¶
github
¶
GitHub forge implementation.
Provides GitHubForge for interacting with the GitHub REST and
GraphQL APIs (pull requests, check runs, review threads).
GitHubForge(token=None)
¶
Bases: Forge
GitHub REST + GraphQL API implementation of the Forge interface.
Source code in src/agentic_ci/forge/github.py
graphql(query, variables=None)
¶
Execute a GitHub GraphQL query or mutation.
Returns the data dict from the response.
Raises ForgeError on HTTP or GraphQL errors.
Source code in src/agentic_ci/forge/github.py
check_runs(repo_path, sha)
¶
Fetch all check runs for a commit SHA.
Returns (check_runs, accessible) where accessible is
False when the token lacks checks:read permission (403).
Source code in src/agentic_ci/forge/github.py
commit_statuses(repo_path, sha)
¶
Fetch commit statuses for a SHA.
GitHub has two parallel CI reporting mechanisms: Check Runs
(used by GitHub Actions) and Commit Statuses (the older API
used by external CI like Prow, Jenkins, and other integrations).
check_runs() only covers the first; this method covers the
second so _derive_pipeline_status() sees the full picture.
Merge-management contexts (tide, Mergify) are excluded
since they reflect merge policy, not CI results.
See https://docs.github.com/en/rest/commits/statuses
Source code in src/agentic_ci/forge/github.py
generate_github_jwt(app_id, private_key_pem)
¶
Generate a GitHub App JWT signed with RS256.
The JWT is valid for 10 minutes (GitHub maximum).
Requires the PyJWT and cryptography packages.
Install with: pip install agentic-ci[forge]
Source code in src/agentic_ci/forge/github.py
get_installation_token(jwt_token, installation_id)
¶
Exchange a GitHub App JWT for an installation access token.
Returns the token string (valid for 1 hour).
Raises RuntimeError on failure.
Source code in src/agentic_ci/forge/github.py
resolve_app_token(repo_url, github_config)
¶
Resolve a GitHub App installation token for a repo URL.
Extracts the GitHub org from repo_url, looks up the matching
App configuration in github_config, and returns a short-lived
installation token.
Returns None (with an error log) on any failure.
Source code in src/agentic_ci/forge/github.py
GitLab¶
gitlab
¶
GitLab forge implementation.
Provides GitLabForge for interacting with the GitLab REST API
(merge requests, pipelines, discussions).
GitLabForge(*, adapter=None)
¶
Bases: Forge
GitLab REST API implementation of the Forge interface.
Source code in src/agentic_ci/forge/gitlab.py
session
property
¶
The authenticated HTTP session for direct API calls.
project_id(project_path)
¶
Look up the numeric GitLab project ID from a project path.
Raises ForgeError if the project cannot be found.
Source code in src/agentic_ci/forge/gitlab.py
pipeline_metadata(project_path, pipeline_id)
¶
Get pipeline metadata.
Returns the raw pipeline dict from the GitLab API.
Source code in src/agentic_ci/forge/gitlab.py
pipeline_jobs(project_path, pipeline_id, *, scope=None)
¶
Get all jobs for a pipeline (paginated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
GitLab project path (e.g. |
required |
pipeline_id
|
int
|
Numeric pipeline ID. |
required |
scope
|
str | None
|
Optional job scope filter (e.g. |
None
|
Returns a list of raw job dicts from the GitLab API.
Source code in src/agentic_ci/forge/gitlab.py
job_trace(project_path, job_id)
¶
Get the full trace log for a job.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
GitLab project path (e.g. |
required |
job_id
|
int
|
Numeric job ID. |
required |
Returns the trace text as a string.
Source code in src/agentic_ci/forge/gitlab.py
retry_job(project_path, job_id)
¶
Retry a failed or canceled GitLab CI job.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
GitLab project path (e.g. |
required |
job_id
|
int
|
Numeric job ID to retry. |
required |
Returns the new job dict from the API response (includes the
retried job's id, web_url, and status).
Raises:
| Type | Description |
|---|---|
ForgeError
|
On API failure. A 401/403 response raises an
actionable error indicating the token needs the |
Source code in src/agentic_ci/forge/gitlab.py
pipeline_schedules(project_path)
¶
List all pipeline schedules for a project (paginated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
GitLab project path (e.g. |
required |
Returns a list of raw schedule dicts from the GitLab API.
Source code in src/agentic_ci/forge/gitlab.py
pipeline_schedule(project_path, schedule_id)
¶
Get details of a specific pipeline schedule.
The returned dict includes last_pipeline with the most recent
pipeline triggered by this schedule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
GitLab project path (e.g. |
required |
schedule_id
|
int
|
Numeric pipeline schedule ID. |
required |
Returns the raw schedule dict from the GitLab API.
Source code in src/agentic_ci/forge/gitlab.py
add_mr_block(blocked_mr_url, blocking_mr_url)
¶
Set MR dependency: blocked_mr cannot merge until blocking_mr merges.
Requires GitLab Premium.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_mr_url
|
str
|
URL of the MR that should be blocked. |
required |
blocking_mr_url
|
str
|
URL of the MR that must merge first. |
required |
Raises ForgeError on failure.
Source code in src/agentic_ci/forge/gitlab.py
mr_diff_position(mr_url)
¶
Get the first changed line position and diff refs from a GitLab MR.
This is a GitLab-specific operation not available on other forges.
Returns {"file", "line", "base_sha", "head_sha", "start_sha"}.
Source code in src/agentic_ci/forge/gitlab.py
paginate(url, params=None)
¶
Fetch all pages of a paginated GitLab API endpoint.
Source code in src/agentic_ci/forge/gitlab.py
CLI¶
cli
¶
CLI subcommands for forge operations.
Registered as the agentic-ci forge subcommand group.
Usage::
agentic-ci forge mr-status <URL>
agentic-ci forge mr-comments <URL>
agentic-ci forge mr-general-comments <URL> [--since ISO]
agentic-ci forge mr-reply <URL> <thread_id> <message>
agentic-ci forge mr-resolve <URL> <thread_id>
agentic-ci forge pipeline-failures <URL>
agentic-ci forge mr-diff-position <URL>
agentic-ci forge label-exists <REPO_URL> <label>
agentic-ci forge label-create <REPO_URL> <label> [--color HEX] [--description TEXT]
agentic-ci forge mr-add-labels <URL> --labels LABEL [LABEL ...]
agentic-ci forge github-token --app-id ID --installation-id ID --private-key PEM
cmd_mr_status(args)
¶
Get MR/PR state, source branch, and pipeline status.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_comments(args)
¶
Get unresolved review comment threads.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_general_comments(args)
¶
Get general (non-diff-positioned) MR/PR comments.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_reply(args)
¶
Reply to a review thread.
cmd_mr_resolve(args)
¶
Resolve a review thread.
cmd_pipeline_failures(args)
¶
Get failed CI job names and logs.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_update(args)
¶
Update MR/PR title and/or description.
Source code in src/agentic_ci/forge/cli.py
cmd_label_exists(args)
¶
Check whether a repository/project label exists.
Source code in src/agentic_ci/forge/cli.py
cmd_label_create(args)
¶
Create a repository/project label.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_add_labels(args)
¶
Attach labels to an existing MR/PR.
Source code in src/agentic_ci/forge/cli.py
cmd_mr_diff_position(args)
¶
Get the first changed line position and diff refs (GitLab only).
Source code in src/agentic_ci/forge/cli.py
cmd_github_token(args)
¶
Generate a short-lived GitHub App installation token.
Source code in src/agentic_ci/forge/cli.py
register_subcommands(forge_parser)
¶
Register forge subcommands on the given parser.
Called from the main agentic-ci CLI to wire up the forge
subcommand group.
Source code in src/agentic_ci/forge/cli.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
Session¶
session
¶
HTTP session and adapter configuration for forge API calls.
Provides auth-injecting adapters for GitLab (PRIVATE-TOKEN) and GitHub (Bearer token), plus a pre-configured session with retry logic.
ForgeAuthError
¶
Bases: RuntimeError
Raised when forge API authentication credentials are missing.
GitLabHTTPAdapter
¶
Bases: HTTPAdapter
Requests adapter that injects PRIVATE-TOKEN for GitLab REST API.
GitHubHTTPAdapter(token, **kwargs)
¶
build_session(*, gitlab_adapter=None, github_token=None)
¶
Build a requests session with forge-specific auth adapters.
The session automatically injects the correct auth headers based
on the request URL prefix (gitlab.com or api.github.com).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gitlab_adapter
|
GitLabHTTPAdapter | None
|
Custom GitLab HTTP adapter. Defaults to
|
None
|
github_token
|
str | None
|
Token for GitHub API authentication. |
None
|
Source code in src/agentic_ci/forge/session.py
extract_api_error(resp)
¶
Extract a human-readable error message from a forge API error response.
Tries message, then errors[0].message, falling back to
"Unknown error".