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.1.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.1-py3-none-any.whl (16.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qodev_gitlab_api-0.2.1.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.1.tar.gz
Algorithm Hash digest
SHA256 97b25309dbcd8eeeb819999994cd09c764c5b29cd855412689cbc500ced4a438
MD5 7d90f111f77fa4854fd81633700e5707
BLAKE2b-256 60f361abbbc9094ef3217e4cf279ac4c849a8f0c46980df7e502562c12ddcd32

See more details on using hashes here.

Provenance

The following attestation bundles were made for qodev_gitlab_api-0.2.1.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.1-py3-none-any.whl.

File metadata

File hashes

Hashes for qodev_gitlab_api-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8ce472468e0d6b32927a4625ea8e4370ce0ceb6c21562ba72653f8c3ddc80c11
MD5 6d0324ffe4f9043a44e1f9a631ba6088
BLAKE2b-256 e2e48967300e81a8665a4149fbf4b3109afa3940465aa3979be11b8222ce3ec6

See more details on using hashes here.

Provenance

The following attestation bundles were made for qodev_gitlab_api-0.2.1-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