diff --git a/changelog.md b/changelog.md index 9b91a2c..a79a969 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2026-03-02 - 2.5.0 - feat(gitlab) +add pipelines and jobs API support, including list/get/trigger/delete/retry/cancel operations, job controls, and related types and list options + +- Add new pipeline methods: getPipelines (with filtering, ordering and pagination), getPipeline, triggerPipeline, deletePipeline, retryPipeline, cancelPipeline, getPipelineVariables, getPipelineTestReport +- Add new job methods: getPipelineJobs (with scope filtering and pagination), getJob, retryJob, cancelJob, playJob, eraseJob; preserve getJobLog +- Introduce IPipelineListOptions and IJobListOptions for richer listing filters +- Extend and add interfaces: IGitLabPipeline (additional metadata fields), IGitLabJob (expanded fields including pipeline, runner, artifacts), IGitLabPipelineVariable, IGitLabTestReport, IGitLabTestSuite, IGitLabTestCase and other minor interface reorganizations +- Export the new interfaces from the package entry (ts/index.ts) + ## 2026-03-02 - 2.4.0 - feat(gitlab) add repository branches and tags endpoints and corresponding types diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index baa6b2e..9a87a25 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@apiclient.xyz/gitlab', - version: '2.4.0', + version: '2.5.0', description: 'A TypeScript client for the GitLab API, providing easy access to projects, groups, CI/CD variables, and pipelines.' } diff --git a/ts/gitlab.classes.gitlabclient.ts b/ts/gitlab.classes.gitlabclient.ts index acbca50..cee21fb 100644 --- a/ts/gitlab.classes.gitlabclient.ts +++ b/ts/gitlab.classes.gitlabclient.ts @@ -10,9 +10,13 @@ import type { IGitLabBranch, IGitLabTag, IGitLabPipeline, + IGitLabPipelineVariable, + IGitLabTestReport, IGitLabJob, ITestConnectionResult, IListOptions, + IPipelineListOptions, + IJobListOptions, } from './gitlab.interfaces.js'; export class GitLabClient { @@ -335,22 +339,131 @@ export class GitLabClient { // Pipelines // --------------------------------------------------------------------------- - public async getPipelines(projectId: number | string, opts?: IListOptions): Promise { + /** + * List pipelines for a project with optional filters. + * Supports status, ref, source, scope, username, date range, ordering. + */ + public async getPipelines(projectId: number | string, opts?: IPipelineListOptions): Promise { const page = opts?.page || 1; const perPage = opts?.perPage || 30; - return this.request( + const orderBy = opts?.orderBy || 'updated_at'; + const sort = opts?.sort || 'desc'; + let url = `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines?page=${page}&per_page=${perPage}&order_by=${orderBy}&sort=${sort}`; + if (opts?.status) url += `&status=${encodeURIComponent(opts.status)}`; + if (opts?.ref) url += `&ref=${encodeURIComponent(opts.ref)}`; + if (opts?.source) url += `&source=${encodeURIComponent(opts.source)}`; + if (opts?.scope) url += `&scope=${encodeURIComponent(opts.scope)}`; + if (opts?.username) url += `&username=${encodeURIComponent(opts.username)}`; + if (opts?.updatedAfter) url += `&updated_after=${encodeURIComponent(opts.updatedAfter)}`; + if (opts?.updatedBefore) url += `&updated_before=${encodeURIComponent(opts.updatedBefore)}`; + return this.request('GET', url); + } + + /** + * Get a single pipeline's full details. + */ + public async getPipeline(projectId: number | string, pipelineId: number): Promise { + return this.request( 'GET', - `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines?page=${page}&per_page=${perPage}&order_by=updated_at&sort=desc`, + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}`, ); } - public async getPipelineJobs(projectId: number | string, pipelineId: number): Promise { - return this.request( - 'GET', - `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/jobs`, + /** + * Trigger a new pipeline on the given ref, optionally with variables. + */ + public async triggerPipeline( + projectId: number | string, + ref: string, + variables?: { key: string; value: string; variable_type?: string }[], + ): Promise { + const body: any = { ref }; + if (variables && variables.length > 0) { + body.variables = variables; + } + return this.request( + 'POST', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipeline`, + body, ); } + /** + * Delete a pipeline and all its jobs. + */ + public async deletePipeline(projectId: number | string, pipelineId: number): Promise { + await this.request( + 'DELETE', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}`, + ); + } + + /** + * Get variables used in a specific pipeline run. + */ + public async getPipelineVariables(projectId: number | string, pipelineId: number): Promise { + return this.request( + 'GET', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/variables`, + ); + } + + /** + * Get the test report for a pipeline. + */ + public async getPipelineTestReport(projectId: number | string, pipelineId: number): Promise { + return this.request( + 'GET', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/test_report`, + ); + } + + public async retryPipeline(projectId: number | string, pipelineId: number): Promise { + return this.request( + 'POST', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`, + ); + } + + public async cancelPipeline(projectId: number | string, pipelineId: number): Promise { + return this.request( + 'POST', + `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`, + ); + } + + // --------------------------------------------------------------------------- + // Jobs + // --------------------------------------------------------------------------- + + /** + * List jobs for a pipeline with optional scope filter and pagination. + */ + public async getPipelineJobs(projectId: number | string, pipelineId: number, opts?: IJobListOptions): Promise { + const page = opts?.page || 1; + const perPage = opts?.perPage || 100; + let url = `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/jobs?page=${page}&per_page=${perPage}`; + if (opts?.scope && opts.scope.length > 0) { + for (const s of opts.scope) { + url += `&scope[]=${encodeURIComponent(s)}`; + } + } + return this.request('GET', url); + } + + /** + * Get a single job's full details. + */ + public async getJob(projectId: number | string, jobId: number): Promise { + return this.request( + 'GET', + `/api/v4/projects/${encodeURIComponent(projectId)}/jobs/${jobId}`, + ); + } + + /** + * Get a job's raw log (trace) output. + */ public async getJobLog(projectId: number | string, jobId: number): Promise { return this.requestText( 'GET', @@ -358,17 +471,43 @@ export class GitLabClient { ); } - public async retryPipeline(projectId: number | string, pipelineId: number): Promise { - await this.request( + /** + * Retry a single job. + */ + public async retryJob(projectId: number | string, jobId: number): Promise { + return this.request( 'POST', - `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`, + `/api/v4/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/retry`, ); } - public async cancelPipeline(projectId: number | string, pipelineId: number): Promise { + /** + * Cancel a running job. + */ + public async cancelJob(projectId: number | string, jobId: number): Promise { + return this.request( + 'POST', + `/api/v4/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/cancel`, + ); + } + + /** + * Trigger a manual job (play action). + */ + public async playJob(projectId: number | string, jobId: number): Promise { + return this.request( + 'POST', + `/api/v4/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/play`, + ); + } + + /** + * Erase a job's trace and artifacts. + */ + public async eraseJob(projectId: number | string, jobId: number): Promise { await this.request( 'POST', - `/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`, + `/api/v4/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/erase`, ); } diff --git a/ts/gitlab.interfaces.ts b/ts/gitlab.interfaces.ts index fa4eb9e..f461327 100644 --- a/ts/gitlab.interfaces.ts +++ b/ts/gitlab.interfaces.ts @@ -1,3 +1,52 @@ +// --------------------------------------------------------------------------- +// Common +// --------------------------------------------------------------------------- + +export interface ITestConnectionResult { + ok: boolean; + error?: string; +} + +export interface IListOptions { + search?: string; + page?: number; + perPage?: number; +} + +// --------------------------------------------------------------------------- +// Pipeline / Job list options +// --------------------------------------------------------------------------- + +export interface IPipelineListOptions extends IListOptions { + /** Filter by pipeline status */ + status?: string; + /** Filter by branch or tag ref */ + ref?: string; + /** Filter by trigger source (push, web, trigger, schedule, api, external, pipeline, chat, merge_request_event, …) */ + source?: string; + /** Filter by scope (running, pending, finished, branches, tags) */ + scope?: string; + /** Filter by the user who triggered the pipeline */ + username?: string; + /** Return pipelines updated after this ISO 8601 date */ + updatedAfter?: string; + /** Return pipelines updated before this ISO 8601 date */ + updatedBefore?: string; + /** Order by field (id, status, ref, updated_at, user_id). Default: id */ + orderBy?: string; + /** Sort direction (asc, desc). Default: desc */ + sort?: string; +} + +export interface IJobListOptions extends IListOptions { + /** Filter by job scope(s) */ + scope?: string[]; +} + +// --------------------------------------------------------------------------- +// Users +// --------------------------------------------------------------------------- + export interface IGitLabUser { id: number; username: string; @@ -8,6 +57,10 @@ export interface IGitLabUser { state: string; } +// --------------------------------------------------------------------------- +// Projects +// --------------------------------------------------------------------------- + export interface IGitLabProject { id: number; name: string; @@ -20,6 +73,10 @@ export interface IGitLabProject { last_activity_at: string; } +// --------------------------------------------------------------------------- +// Groups +// --------------------------------------------------------------------------- + export interface IGitLabGroup { id: number; name: string; @@ -29,6 +86,10 @@ export interface IGitLabGroup { visibility: string; } +// --------------------------------------------------------------------------- +// Variables +// --------------------------------------------------------------------------- + export interface IGitLabVariable { key: string; value: string; @@ -44,32 +105,133 @@ export interface IVariableOptions { environment_scope?: string; } +// --------------------------------------------------------------------------- +// Protected Branches +// --------------------------------------------------------------------------- + export interface IGitLabProtectedBranch { id: number; name: string; allow_force_push: boolean; } +// --------------------------------------------------------------------------- +// Pipelines +// --------------------------------------------------------------------------- + export interface IGitLabPipeline { id: number; + iid: number; project_id: number; status: string; ref: string; sha: string; + before_sha: string; + tag: boolean; web_url: string; duration: number; + queued_duration: number; created_at: string; + updated_at: string; + started_at: string; + finished_at: string; source: string; + coverage: string; + user: IGitLabUser; + detailed_status: { + icon: string; + text: string; + label: string; + group: string; + tooltip: string; + has_details: boolean; + details_path: string; + favicon: string; + }; + yaml_errors: string; } +export interface IGitLabPipelineVariable { + key: string; + value: string; + variable_type: string; +} + +export interface IGitLabTestReport { + total_time: number; + total_count: number; + success_count: number; + failed_count: number; + skipped_count: number; + error_count: number; + test_suites: IGitLabTestSuite[]; +} + +export interface IGitLabTestSuite { + name: string; + total_time: number; + total_count: number; + success_count: number; + failed_count: number; + skipped_count: number; + error_count: number; + test_cases: IGitLabTestCase[]; +} + +export interface IGitLabTestCase { + status: string; + name: string; + classname: string; + execution_time: number; + system_output: string; + stack_trace: string; +} + +// --------------------------------------------------------------------------- +// Jobs +// --------------------------------------------------------------------------- + export interface IGitLabJob { id: number; name: string; stage: string; status: string; + ref: string; + tag: boolean; + web_url: string; + created_at: string; + started_at: string; + finished_at: string; duration: number; + queued_duration: number; + coverage: number; + allow_failure: boolean; + failure_reason: string; + pipeline: { + id: number; + project_id: number; + ref: string; + sha: string; + status: string; + }; + user: IGitLabUser; + runner: { + id: number; + description: string; + active: boolean; + is_shared: boolean; + }; + artifacts: { + filename: string; + size: number; + }[]; + artifacts_expire_at: string; } +// --------------------------------------------------------------------------- +// Branches & Tags +// --------------------------------------------------------------------------- + export interface IGitLabBranch { name: string; commit: { @@ -83,14 +245,3 @@ export interface IGitLabTag { id: string; }; } - -export interface ITestConnectionResult { - ok: boolean; - error?: string; -} - -export interface IListOptions { - search?: string; - page?: number; - perPage?: number; -} diff --git a/ts/index.ts b/ts/index.ts index 0e0f95d..fb2054a 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -9,8 +9,14 @@ export type { IGitLabTag, IVariableOptions, IGitLabPipeline, + IGitLabPipelineVariable, + IGitLabTestReport, + IGitLabTestSuite, + IGitLabTestCase, IGitLabJob, ITestConnectionResult, IListOptions, + IPipelineListOptions, + IJobListOptions, } from './gitlab.interfaces.js'; export { commitinfo } from './00_commitinfo_data.js';