@ci-kit/gitlab 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ interface GitlabClientOptions {
2
+ baseUrl: string;
3
+ projectId: string | number;
4
+ token: string;
5
+ tokenType?: 'job' | 'private';
6
+ }
7
+ interface ReleaseAsset {
8
+ name: string;
9
+ url: string;
10
+ link_type?: 'other' | 'package' | 'image';
11
+ }
12
+ interface ReleaseInput {
13
+ tagName: string;
14
+ name: string;
15
+ description: string;
16
+ assets?: ReleaseAsset[];
17
+ ref?: string;
18
+ }
19
+ interface PackageUpload {
20
+ packageName: string;
21
+ version: string;
22
+ filePath: string;
23
+ fileName?: string;
24
+ }
25
+ interface ScheduleInput {
26
+ description: string;
27
+ ref: string;
28
+ cron: string;
29
+ cronTimezone?: string;
30
+ }
31
+ declare class GitlabClient {
32
+ private readonly options;
33
+ private readonly apiBase;
34
+ private readonly headers;
35
+ constructor(options: GitlabClientOptions);
36
+ get<T>(path: string, query?: Record<string, string | number>): Promise<T>;
37
+ paginate<T>(path: string, query?: Record<string, string | number>): Promise<T[]>;
38
+ post<T>(path: string, body: unknown): Promise<T>;
39
+ put<T>(path: string, body: unknown): Promise<T>;
40
+ delete(path: string): Promise<void>;
41
+ getRelease(tagName: string): Promise<Record<string, unknown> | null>;
42
+ createRelease(input: ReleaseInput): Promise<void>;
43
+ uploadPackage({ packageName, version, filePath, fileName }: PackageUpload): Promise<void>;
44
+ listPackages(packageName?: string): Promise<Array<{
45
+ id: number;
46
+ name: string;
47
+ version: string;
48
+ }>>;
49
+ deletePackage(id: number): Promise<void>;
50
+ upsertSchedule(input: ScheduleInput): Promise<{
51
+ id: number;
52
+ action: 'created' | 'updated' | 'unchanged';
53
+ }>;
54
+ private url;
55
+ }
56
+ declare function clientFromEnv(env?: NodeJS.ProcessEnv): GitlabClient;
57
+
58
+ export { GitlabClient, clientFromEnv };
59
+ export type { GitlabClientOptions, PackageUpload, ReleaseAsset, ReleaseInput, ScheduleInput };
@@ -0,0 +1,59 @@
1
+ interface GitlabClientOptions {
2
+ baseUrl: string;
3
+ projectId: string | number;
4
+ token: string;
5
+ tokenType?: 'job' | 'private';
6
+ }
7
+ interface ReleaseAsset {
8
+ name: string;
9
+ url: string;
10
+ link_type?: 'other' | 'package' | 'image';
11
+ }
12
+ interface ReleaseInput {
13
+ tagName: string;
14
+ name: string;
15
+ description: string;
16
+ assets?: ReleaseAsset[];
17
+ ref?: string;
18
+ }
19
+ interface PackageUpload {
20
+ packageName: string;
21
+ version: string;
22
+ filePath: string;
23
+ fileName?: string;
24
+ }
25
+ interface ScheduleInput {
26
+ description: string;
27
+ ref: string;
28
+ cron: string;
29
+ cronTimezone?: string;
30
+ }
31
+ declare class GitlabClient {
32
+ private readonly options;
33
+ private readonly apiBase;
34
+ private readonly headers;
35
+ constructor(options: GitlabClientOptions);
36
+ get<T>(path: string, query?: Record<string, string | number>): Promise<T>;
37
+ paginate<T>(path: string, query?: Record<string, string | number>): Promise<T[]>;
38
+ post<T>(path: string, body: unknown): Promise<T>;
39
+ put<T>(path: string, body: unknown): Promise<T>;
40
+ delete(path: string): Promise<void>;
41
+ getRelease(tagName: string): Promise<Record<string, unknown> | null>;
42
+ createRelease(input: ReleaseInput): Promise<void>;
43
+ uploadPackage({ packageName, version, filePath, fileName }: PackageUpload): Promise<void>;
44
+ listPackages(packageName?: string): Promise<Array<{
45
+ id: number;
46
+ name: string;
47
+ version: string;
48
+ }>>;
49
+ deletePackage(id: number): Promise<void>;
50
+ upsertSchedule(input: ScheduleInput): Promise<{
51
+ id: number;
52
+ action: 'created' | 'updated' | 'unchanged';
53
+ }>;
54
+ private url;
55
+ }
56
+ declare function clientFromEnv(env?: NodeJS.ProcessEnv): GitlabClient;
57
+
58
+ export { GitlabClient, clientFromEnv };
59
+ export type { GitlabClientOptions, PackageUpload, ReleaseAsset, ReleaseInput, ScheduleInput };
package/dist/index.mjs ADDED
@@ -0,0 +1,120 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import process from 'node:process';
3
+ import { requestJson, request } from '@ci-kit/http';
4
+
5
+ class GitlabClient {
6
+ constructor(options) {
7
+ this.options = options;
8
+ this.apiBase = `${options.baseUrl.replace(/\/$/, "")}/api/v4/projects/${encodeURIComponent(String(options.projectId))}`;
9
+ this.headers = options.tokenType === "private" ? { "PRIVATE-TOKEN": options.token } : { "JOB-TOKEN": options.token };
10
+ }
11
+ apiBase;
12
+ headers;
13
+ async get(path, query = {}) {
14
+ return requestJson(this.url(path, query), { headers: this.headers });
15
+ }
16
+ async paginate(path, query = {}) {
17
+ const rows = [];
18
+ let page = 1;
19
+ while (true) {
20
+ const url = this.url(path, { ...query, per_page: 100, page });
21
+ const response = await request(url, { headers: this.headers });
22
+ rows.push(...await response.json());
23
+ const next = response.headers.get("x-next-page");
24
+ if (!next) {
25
+ break;
26
+ }
27
+ page = Number(next);
28
+ }
29
+ return rows;
30
+ }
31
+ async post(path, body) {
32
+ return requestJson(this.url(path), {
33
+ method: "POST",
34
+ headers: { ...this.headers, "content-type": "application/json" },
35
+ body: JSON.stringify(body)
36
+ });
37
+ }
38
+ async put(path, body) {
39
+ return requestJson(this.url(path), {
40
+ method: "PUT",
41
+ headers: { ...this.headers, "content-type": "application/json" },
42
+ body: JSON.stringify(body)
43
+ });
44
+ }
45
+ async delete(path) {
46
+ await request(this.url(path), { method: "DELETE", headers: this.headers });
47
+ }
48
+ async getRelease(tagName) {
49
+ try {
50
+ return await this.get(`releases/${encodeURIComponent(tagName)}`);
51
+ } catch (error) {
52
+ if (error instanceof Error && error.name === "HttpError") {
53
+ return null;
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+ async createRelease(input) {
59
+ await this.post("releases", {
60
+ tag_name: input.tagName,
61
+ ref: input.ref,
62
+ name: input.name,
63
+ description: input.description,
64
+ assets: input.assets ? { links: input.assets } : void 0
65
+ });
66
+ }
67
+ async uploadPackage({ packageName, version, filePath, fileName }) {
68
+ const name = fileName ?? filePath.split(/[\\/]/).pop();
69
+ const body = readFileSync(filePath);
70
+ await request(this.url(`packages/generic/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}/${encodeURIComponent(name)}`), {
71
+ method: "PUT",
72
+ headers: { ...this.headers, "content-type": "application/octet-stream" },
73
+ body
74
+ });
75
+ }
76
+ async listPackages(packageName) {
77
+ const query = packageName ? { package_name: packageName } : {};
78
+ return this.paginate("packages", query);
79
+ }
80
+ async deletePackage(id) {
81
+ await this.delete(`packages/${id}`);
82
+ }
83
+ async upsertSchedule(input) {
84
+ const existing = await this.paginate("pipeline_schedules");
85
+ const match = existing.find((item) => item.description === input.description);
86
+ const body = {
87
+ description: input.description,
88
+ ref: input.ref,
89
+ cron: input.cron,
90
+ cron_timezone: input.cronTimezone ?? "Asia/Shanghai"
91
+ };
92
+ if (!match) {
93
+ const created = await this.post("pipeline_schedules", body);
94
+ return { id: created.id, action: "created" };
95
+ }
96
+ if (match.cron === input.cron && match.ref === input.ref) {
97
+ return { id: match.id, action: "unchanged" };
98
+ }
99
+ await this.put(`pipeline_schedules/${match.id}`, body);
100
+ return { id: match.id, action: "updated" };
101
+ }
102
+ url(path, query = {}) {
103
+ const url = new URL(`${this.apiBase}/${path.replace(/^\//, "")}`);
104
+ for (const [key, value] of Object.entries(query)) {
105
+ url.searchParams.set(key, String(value));
106
+ }
107
+ return url.toString();
108
+ }
109
+ }
110
+ function clientFromEnv(env = process.env) {
111
+ const baseUrl = env.CI_SERVER_URL;
112
+ const projectId = env.CI_PROJECT_ID;
113
+ const token = env.CI_JOB_TOKEN;
114
+ if (!baseUrl || !projectId || !token) {
115
+ throw new Error("CI_SERVER_URL, CI_PROJECT_ID and CI_JOB_TOKEN are required");
116
+ }
117
+ return new GitlabClient({ baseUrl, projectId, token });
118
+ }
119
+
120
+ export { GitlabClient, clientFromEnv };
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@ci-kit/gitlab",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "Minimal GitLab API client for releases, packages and schedules",
6
+ "author": "imba97",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/cikit-js/ci-kit#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/cikit-js/ci-kit.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/cikit-js/ci-kit/issues"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.mts",
19
+ "import": "./dist/index.mjs"
20
+ }
21
+ },
22
+ "main": "./dist/index.mjs",
23
+ "module": "./dist/index.mjs",
24
+ "types": "./dist/index.d.mts",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "dependencies": {
29
+ "@ci-kit/http": "0.0.1"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "build": "unbuild",
36
+ "typecheck": "tsc --noEmit",
37
+ "stub": "unbuild --stub"
38
+ }
6
39
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.