Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 016eaf1b91 | |||
| cbb9f364ae | |||
| ef688115d6 | |||
| 3aaa0f7e68 | |||
| 1cd7c62431 | |||
| 8f85104136 | |||
| c8059fb1c0 | |||
| 413c4be172 | |||
| e7cfd5dec1 | |||
| e002e90bd0 | |||
| f460b92c3a |
28
changelog.md
28
changelog.md
@@ -1,5 +1,33 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-02-28 - 2.2.0 - feat(gitlabclient)
|
||||||
|
add deleteProject method to delete a project using GitLab API DELETE /api/v4/projects/:id
|
||||||
|
|
||||||
|
- Adds GitlabClient.deleteProject(projectId: number | string): Promise<void>.
|
||||||
|
- Implements DELETE /api/v4/projects/:id and URL-encodes the projectId.
|
||||||
|
- Non-breaking API addition — bump minor version.
|
||||||
|
|
||||||
|
## 2026-02-28 - 2.1.0 - feat(gitlab)
|
||||||
|
add group- and project-management methods to GitLab client
|
||||||
|
|
||||||
|
- Adds getGroupByPath(fullPath) to fetch a group by full path
|
||||||
|
- Adds getGroupProjects(groupId, opts?) to list projects in a group (include_subgroups=true, default perPage=50, optional search)
|
||||||
|
- Adds getDescendantGroups(groupId, opts?) to list descendant subgroups (paginated, default perPage=50, optional search)
|
||||||
|
- Adds createGroup(name, path, parentId?) to create groups (defaults to private visibility)
|
||||||
|
- Adds createProject(name, opts?) to create projects with optional path, namespaceId, visibility (default private) and description
|
||||||
|
|
||||||
|
## 2026-02-24 - 2.0.3 - fix()
|
||||||
|
no changes detected; nothing to commit
|
||||||
|
|
||||||
|
- No files changed in the provided diff
|
||||||
|
- No updates to source, dependencies, or package files
|
||||||
|
|
||||||
|
## 2026-02-24 - 2.0.2 - fix()
|
||||||
|
no changes detected in diff; no version bump required
|
||||||
|
|
||||||
|
- No files changed in this commit/diff.
|
||||||
|
- No code or dependency changes detected; no release necessary.
|
||||||
|
|
||||||
## 2026-02-24 - 2.0.1 - fix()
|
## 2026-02-24 - 2.0.1 - fix()
|
||||||
no changes detected; no version bump required
|
no changes detected; no version bump required
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"@git.zone/cli": {
|
"@git.zone/cli": {
|
||||||
"release": {
|
"release": {
|
||||||
"registries": [
|
"registries": [
|
||||||
|
"https://verdaccio.lossless.digital",
|
||||||
"https://registry.npmjs.org"
|
"https://registry.npmjs.org"
|
||||||
],
|
],
|
||||||
"accessLevel": "public"
|
"accessLevel": "public"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@apiclient.xyz/gitlab",
|
"name": "@apiclient.xyz/gitlab",
|
||||||
"version": "2.0.1",
|
"version": "2.2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A TypeScript client for the GitLab API, providing easy access to projects, groups, CI/CD variables, and pipelines.",
|
"description": "A TypeScript client for the GitLab API, providing easy access to projects, groups, CI/CD variables, and pipelines.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
|||||||
278
readme.md
278
readme.md
@@ -4,25 +4,291 @@ A TypeScript client for the GitLab API, providing easy access to projects, group
|
|||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
npm install @apiclient.xyz/gitlab
|
npm install @apiclient.xyz/gitlab
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
All examples below use ESM imports and async/await.
|
||||||
|
|
||||||
|
### Creating a Client
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { GitLabClient } from '@apiclient.xyz/gitlab';
|
import { GitLabClient } from '@apiclient.xyz/gitlab';
|
||||||
|
|
||||||
const client = new GitLabClient('https://gitlab.com', 'your-private-token');
|
const client = new GitLabClient('https://gitlab.example.com', 'your-private-token');
|
||||||
|
```
|
||||||
|
|
||||||
// Test connection
|
The constructor accepts the base URL of your GitLab instance and a personal access token (or project/group token) used for authentication via the `PRIVATE-TOKEN` header.
|
||||||
|
|
||||||
|
### Testing the Connection
|
||||||
|
|
||||||
|
```typescript
|
||||||
const result = await client.testConnection();
|
const result = await client.testConnection();
|
||||||
|
|
||||||
// List projects
|
if (result.ok) {
|
||||||
|
console.log('Connected successfully.');
|
||||||
|
} else {
|
||||||
|
console.error('Connection failed:', result.error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listing Projects
|
||||||
|
|
||||||
|
Retrieve projects you are a member of, ordered by most recently updated.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// First page, default 50 per page
|
||||||
const projects = await client.getProjects();
|
const projects = await client.getProjects();
|
||||||
|
|
||||||
// Manage CI/CD variables
|
// Search with pagination
|
||||||
await client.createProjectVariable(123, 'SECRET_KEY', 'secret-value');
|
const filtered = await client.getProjects({
|
||||||
|
search: 'my-service',
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const project of filtered) {
|
||||||
|
console.log(`${project.id} - ${project.path_with_namespace}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listing Groups
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const groups = await client.getGroups();
|
||||||
|
|
||||||
|
// Search groups by name
|
||||||
|
const matchingGroups = await client.getGroups({ search: 'platform' });
|
||||||
|
|
||||||
|
for (const group of matchingGroups) {
|
||||||
|
console.log(`${group.id} - ${group.full_path}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Project Variables
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const projectId = 42;
|
||||||
|
|
||||||
|
// List all variables
|
||||||
|
const vars = await client.getProjectVariables(projectId);
|
||||||
|
|
||||||
|
// Create a variable
|
||||||
|
await client.createProjectVariable(projectId, 'DATABASE_URL', 'postgres://...', {
|
||||||
|
protected: true,
|
||||||
|
masked: true,
|
||||||
|
environment_scope: 'production',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update a variable
|
||||||
|
await client.updateProjectVariable(projectId, 'DATABASE_URL', 'postgres://new-host/...', {
|
||||||
|
protected: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete a variable
|
||||||
|
await client.deleteProjectVariable(projectId, 'DATABASE_URL');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Group Variables
|
||||||
|
|
||||||
|
The group variable API mirrors the project variable API.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const groupId = 7;
|
||||||
|
|
||||||
|
// List all variables
|
||||||
|
const groupVars = await client.getGroupVariables(groupId);
|
||||||
|
|
||||||
|
// Create a variable
|
||||||
|
await client.createGroupVariable(groupId, 'NPM_TOKEN', 'tok-xxx', {
|
||||||
|
protected: false,
|
||||||
|
masked: true,
|
||||||
|
environment_scope: '*',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update a variable
|
||||||
|
await client.updateGroupVariable(groupId, 'NPM_TOKEN', 'tok-yyy', {
|
||||||
|
masked: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete a variable
|
||||||
|
await client.deleteGroupVariable(groupId, 'NPM_TOKEN');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Pipelines
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const projectId = 42;
|
||||||
|
|
||||||
|
// List recent pipelines
|
||||||
|
const pipelines = await client.getPipelines(projectId, { page: 1, perPage: 10 });
|
||||||
|
|
||||||
|
for (const pipeline of pipelines) {
|
||||||
|
console.log(`Pipeline #${pipeline.id} [${pipeline.status}] on ${pipeline.ref}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get jobs for a specific pipeline
|
||||||
|
const jobs = await client.getPipelineJobs(projectId, pipelines[0].id);
|
||||||
|
for (const job of jobs) {
|
||||||
|
console.log(` Job "${job.name}" (${job.stage}): ${job.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the raw log output of a job
|
||||||
|
const log = await client.getJobLog(projectId, jobs[0].id);
|
||||||
|
console.log(log);
|
||||||
|
|
||||||
|
// Retry a failed pipeline
|
||||||
|
await client.retryPipeline(projectId, pipelines[0].id);
|
||||||
|
|
||||||
|
// Cancel a running pipeline
|
||||||
|
await client.cancelPipeline(projectId, pipelines[0].id);
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `GitLabClient`
|
||||||
|
|
||||||
|
| Method | Signature | Returns | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `constructor` | `(baseUrl: string, token: string)` | `GitLabClient` | Create a new client instance. |
|
||||||
|
| `testConnection` | `()` | `Promise<ITestConnectionResult>` | Verify the token and connectivity. |
|
||||||
|
| `getProjects` | `(opts?: IListOptions)` | `Promise<IGitLabProject[]>` | List projects you are a member of. |
|
||||||
|
| `getGroups` | `(opts?: IListOptions)` | `Promise<IGitLabGroup[]>` | List accessible groups. |
|
||||||
|
| `getProjectVariables` | `(projectId: number \| string)` | `Promise<IGitLabVariable[]>` | List all CI/CD variables for a project. |
|
||||||
|
| `createProjectVariable` | `(projectId: number \| string, key: string, value: string, opts?: IVariableOptions)` | `Promise<IGitLabVariable>` | Create a CI/CD variable on a project. |
|
||||||
|
| `updateProjectVariable` | `(projectId: number \| string, key: string, value: string, opts?: IVariableOptions)` | `Promise<IGitLabVariable>` | Update an existing project variable. |
|
||||||
|
| `deleteProjectVariable` | `(projectId: number \| string, key: string)` | `Promise<void>` | Delete a project variable. |
|
||||||
|
| `getGroupVariables` | `(groupId: number \| string)` | `Promise<IGitLabVariable[]>` | List all CI/CD variables for a group. |
|
||||||
|
| `createGroupVariable` | `(groupId: number \| string, key: string, value: string, opts?: IVariableOptions)` | `Promise<IGitLabVariable>` | Create a CI/CD variable on a group. |
|
||||||
|
| `updateGroupVariable` | `(groupId: number \| string, key: string, value: string, opts?: IVariableOptions)` | `Promise<IGitLabVariable>` | Update an existing group variable. |
|
||||||
|
| `deleteGroupVariable` | `(groupId: number \| string, key: string)` | `Promise<void>` | Delete a group variable. |
|
||||||
|
| `getPipelines` | `(projectId: number \| string, opts?: IListOptions)` | `Promise<IGitLabPipeline[]>` | List pipelines for a project, newest first. |
|
||||||
|
| `getPipelineJobs` | `(projectId: number \| string, pipelineId: number)` | `Promise<IGitLabJob[]>` | Get all jobs for a pipeline. |
|
||||||
|
| `getJobLog` | `(projectId: number \| string, jobId: number)` | `Promise<string>` | Retrieve the raw trace/log of a job. |
|
||||||
|
| `retryPipeline` | `(projectId: number \| string, pipelineId: number)` | `Promise<void>` | Retry all failed jobs in a pipeline. |
|
||||||
|
| `cancelPipeline` | `(projectId: number \| string, pipelineId: number)` | `Promise<void>` | Cancel a running pipeline. |
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### `IListOptions`
|
||||||
|
|
||||||
|
Pagination and search options used by `getProjects`, `getGroups`, and `getPipelines`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IListOptions {
|
||||||
|
search?: string; // Filter results by keyword
|
||||||
|
page?: number; // Page number (default: 1)
|
||||||
|
perPage?: number; // Items per page (default: 50 for projects/groups, 30 for pipelines)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IVariableOptions`
|
||||||
|
|
||||||
|
Options when creating or updating CI/CD variables.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IVariableOptions {
|
||||||
|
protected?: boolean; // Only expose to protected branches/tags (default: false)
|
||||||
|
masked?: boolean; // Mask the value in job logs (default: false)
|
||||||
|
environment_scope?: string; // Environment scope (default: '*' for all environments)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `ITestConnectionResult`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ITestConnectionResult {
|
||||||
|
ok: boolean;
|
||||||
|
error?: string; // Present when ok is false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabProject`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabProject {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path_with_namespace: string;
|
||||||
|
description: string;
|
||||||
|
default_branch: string;
|
||||||
|
web_url: string;
|
||||||
|
visibility: string;
|
||||||
|
topics: string[];
|
||||||
|
last_activity_at: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabGroup`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabGroup {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
full_path: string;
|
||||||
|
description: string;
|
||||||
|
web_url: string;
|
||||||
|
visibility: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabVariable`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabVariable {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
variable_type: string;
|
||||||
|
protected: boolean;
|
||||||
|
masked: boolean;
|
||||||
|
environment_scope: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabPipeline`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabPipeline {
|
||||||
|
id: number;
|
||||||
|
project_id: number;
|
||||||
|
status: string;
|
||||||
|
ref: string;
|
||||||
|
sha: string;
|
||||||
|
web_url: string;
|
||||||
|
duration: number;
|
||||||
|
created_at: string;
|
||||||
|
source: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabJob`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabJob {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
stage: string;
|
||||||
|
status: string;
|
||||||
|
duration: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IGitLabUser`
|
||||||
|
|
||||||
|
Returned internally by `testConnection` to verify credentials.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface IGitLabUser {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
avatar_url: string;
|
||||||
|
web_url: string;
|
||||||
|
state: string;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ const testQenv = new qenv.Qenv('./', '.nogit/');
|
|||||||
let gitlabClient: GitLabClient;
|
let gitlabClient: GitLabClient;
|
||||||
|
|
||||||
tap.test('should create a GitLabClient instance', async () => {
|
tap.test('should create a GitLabClient instance', async () => {
|
||||||
const baseUrl = testQenv.getEnvVarOnDemand('GITLAB_BASE_URL') || 'https://gitlab.com';
|
const baseUrl = (await testQenv.getEnvVarOnDemand('GITLAB_BASE_URL')) || 'https://gitlab.com';
|
||||||
const token = testQenv.getEnvVarOnDemand('GITLAB_TOKEN') || '';
|
const token = (await testQenv.getEnvVarOnDemand('GITLAB_TOKEN')) || '';
|
||||||
gitlabClient = new GitLabClient(baseUrl, token);
|
gitlabClient = new GitLabClient(baseUrl, token);
|
||||||
expect(gitlabClient).toBeInstanceOf(GitLabClient);
|
expect(gitlabClient).toBeInstanceOf(GitLabClient);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@apiclient.xyz/gitlab',
|
name: '@apiclient.xyz/gitlab',
|
||||||
version: '2.0.1',
|
version: '2.2.0',
|
||||||
description: 'A TypeScript client for the GitLab API, providing easy access to projects, groups, CI/CD variables, and pipelines.'
|
description: 'A TypeScript client for the GitLab API, providing easy access to projects, groups, CI/CD variables, and pipelines.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,73 @@ export class GitLabClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Groups — scoped queries
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single group by its full path (e.g. "foss.global" or "foss.global/push.rocks")
|
||||||
|
*/
|
||||||
|
public async getGroupByPath(fullPath: string): Promise<IGitLabGroup> {
|
||||||
|
return this.request<IGitLabGroup>(
|
||||||
|
'GET',
|
||||||
|
`/api/v4/groups/${encodeURIComponent(fullPath)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List projects within a group (includes subgroups when include_subgroups=true)
|
||||||
|
*/
|
||||||
|
public async getGroupProjects(groupId: number | string, opts?: IListOptions): Promise<IGitLabProject[]> {
|
||||||
|
const page = opts?.page || 1;
|
||||||
|
const perPage = opts?.perPage || 50;
|
||||||
|
let url = `/api/v4/groups/${encodeURIComponent(groupId)}/projects?include_subgroups=true&order_by=updated_at&sort=desc&page=${page}&per_page=${perPage}`;
|
||||||
|
if (opts?.search) {
|
||||||
|
url += `&search=${encodeURIComponent(opts.search)}`;
|
||||||
|
}
|
||||||
|
return this.request<IGitLabProject[]>('GET', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all descendant groups (recursive subgroups) within a group
|
||||||
|
*/
|
||||||
|
public async getDescendantGroups(groupId: number | string, opts?: IListOptions): Promise<IGitLabGroup[]> {
|
||||||
|
const page = opts?.page || 1;
|
||||||
|
const perPage = opts?.perPage || 50;
|
||||||
|
let url = `/api/v4/groups/${encodeURIComponent(groupId)}/descendant_groups?order_by=name&sort=asc&page=${page}&per_page=${perPage}`;
|
||||||
|
if (opts?.search) {
|
||||||
|
url += `&search=${encodeURIComponent(opts.search)}`;
|
||||||
|
}
|
||||||
|
return this.request<IGitLabGroup[]>('GET', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new group. Optionally nested under a parent group.
|
||||||
|
*/
|
||||||
|
public async createGroup(name: string, path: string, parentId?: number): Promise<IGitLabGroup> {
|
||||||
|
const body: any = { name, path, visibility: 'private' };
|
||||||
|
if (parentId) body.parent_id = parentId;
|
||||||
|
return this.request<IGitLabGroup>('POST', '/api/v4/groups', body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new project (repository).
|
||||||
|
*/
|
||||||
|
public async createProject(name: string, opts?: {
|
||||||
|
path?: string;
|
||||||
|
namespaceId?: number;
|
||||||
|
visibility?: string;
|
||||||
|
description?: string;
|
||||||
|
}): Promise<IGitLabProject> {
|
||||||
|
return this.request<IGitLabProject>('POST', '/api/v4/projects', {
|
||||||
|
name,
|
||||||
|
path: opts?.path || name,
|
||||||
|
namespace_id: opts?.namespaceId,
|
||||||
|
visibility: opts?.visibility || 'private',
|
||||||
|
description: opts?.description || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Projects
|
// Projects
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -301,4 +368,15 @@ export class GitLabClient {
|
|||||||
`/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`,
|
`/api/v4/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Project Deletion
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public async deleteProject(projectId: number | string): Promise<void> {
|
||||||
|
await this.request(
|
||||||
|
'DELETE',
|
||||||
|
`/api/v4/projects/${encodeURIComponent(projectId)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user