Skip to main content

Python client for the GitLab API

Reason this release was yanked:

Broken release, use 0.2.2+

Project description

qodev-gitlab-api

A lightweight, typed Python client for the GitLab REST API. Built on httpx with automatic pagination, structured error handling, and .env support, it provides a clean interface for common GitLab operations without the weight of a full-featured SDK.

Installation

pip install qodev-gitlab-api

Quick Start

from qodev_gitlab_api import GitLabClient

# Reads GITLAB_TOKEN and GITLAB_URL from environment or .env file
client = GitLabClient()

# Or pass credentials explicitly
client = GitLabClient(token="glpat-xxxxxxxxxxxx", base_url="https://gitlab.example.com")

# Skip connection validation for faster startup
client = GitLabClient(validate=False)

Features

  • Merge requests -- create, update, merge, close, and review with inline diff comments
  • Pipelines and jobs -- list, inspect, wait for completion, retry, and download artifacts
  • Issues -- create, update, close, and comment
  • Releases -- create, update, delete, and list with asset links
  • CI/CD variables -- get, list, create, update, and upsert (set) project variables
  • File operations -- read repository files at any ref, upload files for markdown embedding
  • Automatic pagination -- all list endpoints handle multi-page results transparently
  • Typed exceptions -- AuthenticationError, NotFoundError, APIError, ConfigurationError

Configuration

The client reads configuration from environment variables, with .env file support via python-dotenv:

Variable Description Default
GITLAB_TOKEN GitLab personal access token (required) --
GITLAB_URL GitLab instance base URL https://gitlab.com

You can also use GITLAB_BASE_URL as an alias for GITLAB_URL.

Create a .env file in your project root:

GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
GITLAB_URL=https://gitlab.example.com

API Reference

Projects

projects = client.get_projects(owned=True)
project = client.get_project("my-group/my-project")

Merge Requests

# List and inspect
mrs = client.get_merge_requests("my-group/my-project", state="opened")
mr = client.get_merge_request("my-group/my-project", mr_iid=42)
changes = client.get_mr_changes("my-group/my-project", mr_iid=42)
commits = client.get_mr_commits("my-group/my-project", mr_iid=42)
approvals = client.get_mr_approvals("my-group/my-project", mr_iid=42)

# Create
mr = client.create_merge_request(
    "my-group/my-project",
    source_branch="feature/foo",
    target_branch="main",
    title="Add foo feature",
    description="Implements the foo feature.",
    assignee_ids=[123],
    reviewer_ids=[456],
    labels="enhancement",
)

# Update, merge, close
client.update_mr("my-group/my-project", mr_iid=42, title="Updated title")
client.merge_mr("my-group/my-project", mr_iid=42, squash=True)
client.close_mr("my-group/my-project", mr_iid=42)

# Discussions and comments
discussions = client.get_mr_discussions("my-group/my-project", mr_iid=42)
client.create_mr_note("my-group/my-project", mr_iid=42, body="Looks good!")
client.reply_to_discussion("my-group/my-project", mr_iid=42, discussion_id="abc123", body="Fixed.")
client.resolve_discussion("my-group/my-project", mr_iid=42, discussion_id="abc123", resolved=True)

# Inline diff comment
from qodev_gitlab_api import DiffPosition

client.create_mr_discussion(
    "my-group/my-project",
    mr_iid=42,
    body="Consider renaming this variable.",
    position=DiffPosition(file_path="src/main.py", new_line=15),
)

Pipelines and Jobs

pipelines = client.get_pipelines("my-group/my-project", ref="main")
pipeline = client.get_pipeline("my-group/my-project", pipeline_id=1001)
jobs = client.get_pipeline_jobs("my-group/my-project", pipeline_id=1001)

# Job details, logs, and artifacts
job = client.get_job("my-group/my-project", job_id=5001)
log = client.get_job_log("my-group/my-project", job_id=5001)
artifact = client.get_job_artifact("my-group/my-project", job_id=5001, artifact_path="report.xml")

# Retry a failed job
client.retry_job("my-group/my-project", job_id=5001)

# Wait for pipeline completion (blocks until done or timeout)
result = client.wait_for_pipeline("my-group/my-project", pipeline_id=1001, timeout_seconds=600)
print(result["final_status"])  # "success", "failed", "canceled", "skipped", or "timeout"

Issues

issues = client.get_issues("my-group/my-project", state="opened", labels="bug")
issue = client.get_issue("my-group/my-project", issue_iid=10)

issue = client.create_issue(
    "my-group/my-project",
    title="Fix login bug",
    description="Users cannot log in with SSO.",
    labels="bug,urgent",
    assignee_ids=[123],
)

client.update_issue("my-group/my-project", issue_iid=10, labels="bug,resolved")
client.close_issue("my-group/my-project", issue_iid=10)

# Comments
notes = client.get_issue_notes("my-group/my-project", issue_iid=10)
client.create_issue_note("my-group/my-project", issue_iid=10, body="Investigating this now.")

Releases

releases = client.get_releases("my-group/my-project")
release = client.get_release("my-group/my-project", tag_name="v1.0.0")

release = client.create_release(
    "my-group/my-project",
    tag_name="v1.1.0",
    name="Version 1.1.0",
    description="## What's new\n- Feature A\n- Bug fix B",
    ref="main",
)

client.update_release("my-group/my-project", tag_name="v1.1.0", description="Updated notes.")
client.delete_release("my-group/my-project", tag_name="v1.1.0")

CI/CD Variables

variables = client.list_project_variables("my-group/my-project")
var = client.get_project_variable("my-group/my-project", key="API_KEY")

# Create or update (upsert)
var, action = client.set_project_variable(
    "my-group/my-project",
    key="API_KEY",
    value="secret-value",
    masked=True,
    protected=True,
)
print(action)  # "created" or "updated"

File Operations

# Read a file from the repository
content = client.get_file_content("my-group/my-project", file_path="README.md", ref="main")

# Upload a file (for embedding in issues/MRs)
from qodev_gitlab_api import FileFromPath

result = client.upload_file("my-group/my-project", source=FileFromPath(path="/tmp/screenshot.png"))

Error Handling

All API errors raise typed exceptions that inherit from GitLabError:

from qodev_gitlab_api import GitLabClient, AuthenticationError, NotFoundError, APIError, ConfigurationError

try:
    client = GitLabClient()
    mr = client.get_merge_request("my-group/my-project", mr_iid=999)
except ConfigurationError:
    print("Missing or invalid GITLAB_TOKEN / GITLAB_URL")
except AuthenticationError:
    print("Token is invalid or expired")
except NotFoundError:
    print("Merge request not found")
except APIError as e:
    print(f"API error {e.status_code}: {e.response_body}")

Requirements

License

MIT -- see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qodev_gitlab_api-0.2.0.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qodev_gitlab_api-0.2.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file qodev_gitlab_api-0.2.0.tar.gz.

File metadata

  • Download URL: qodev_gitlab_api-0.2.0.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qodev_gitlab_api-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ceb0a663d85d052fd9bb7452d39d91dc221619c72d0fc53e4587059c46fec5c5
MD5 e935ff5f47d1daced6a19b8fc7d45cad
BLAKE2b-256 f4b095d1f0dc98e66c96582c9abd04b6e987ba4af0319f735fdcd69e5a7e4b94

See more details on using hashes here.

Provenance

The following attestation bundles were made for qodev_gitlab_api-0.2.0.tar.gz:

Publisher: publish.yml on qodevai/gitlab-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qodev_gitlab_api-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for qodev_gitlab_api-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ae5e7bd804ccfcbfaf0f6a1f4b56c7bf2adb42fd1b890855fa4a31167abc7f9f
MD5 3585cb6540dfeab2a3fda38f977ac6f6
BLAKE2b-256 c7148e9e939d56c7adbcc9b92547f723b73f836bf502a14fc389d7df57a7907a

See more details on using hashes here.

Provenance

The following attestation bundles were made for qodev_gitlab_api-0.2.0-py3-none-any.whl:

Publisher: publish.yml on qodevai/gitlab-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page