@simplysf/simply-cicd-core 0.2.0

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,267 @@
1
+ /*
2
+ * Copyright (c) 2026, Clay Chipps.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ function normalizeProject(repo) {
17
+ return {
18
+ id: repo.id,
19
+ key: repo.full_name,
20
+ name: repo.name,
21
+ pathWithNamespace: repo.full_name,
22
+ defaultBranch: repo.default_branch,
23
+ archived: Boolean(repo.archived),
24
+ // GitHub has no `empty_repo` flag; a repo with no commits reports a zero size.
25
+ empty: repo.size === 0,
26
+ isFork: repo.fork === true,
27
+ raw: repo,
28
+ };
29
+ }
30
+ function normalizePullRequest(pullRequest) {
31
+ return {
32
+ id: pullRequest.id,
33
+ // GitHub's per-pull-request endpoints take the repo-scoped `number`.
34
+ iid: pullRequest.number,
35
+ title: pullRequest.title,
36
+ description: pullRequest.body,
37
+ sourceBranch: pullRequest.head.ref,
38
+ targetBranch: pullRequest.base.ref,
39
+ webUrl: pullRequest.html_url,
40
+ raw: pullRequest,
41
+ };
42
+ }
43
+ /** GitHub addresses repos as `owner/repo`, so each segment is encoded separately. */
44
+ function splitProject(project) {
45
+ const key = typeof project === 'object' ? project.key : String(project);
46
+ const [owner, repo] = key.split('/');
47
+ if (!owner || !repo) {
48
+ throw new Error(`Invalid GitHub repository "${key}". Expected "owner/repo".`);
49
+ }
50
+ return { owner: encodeURIComponent(owner), repo: encodeURIComponent(repo) };
51
+ }
52
+ function repoPath(project) {
53
+ const { owner, repo } = splitProject(project);
54
+ return `/repos/${owner}/${repo}`;
55
+ }
56
+ /** Encodes a file path for the contents API, preserving its directory separators. */
57
+ function encodeFilePath(filePath) {
58
+ return filePath.split('/').map(encodeURIComponent).join('/');
59
+ }
60
+ /** Extracts the `rel="next"` URL from a Link header, if present. */
61
+ function nextLink(linkHeader) {
62
+ if (!linkHeader) {
63
+ return undefined;
64
+ }
65
+ for (const part of linkHeader.split(',')) {
66
+ const match = /<(?<url>[^>]+)>\s*;\s*rel="next"/.exec(part);
67
+ if (match?.groups?.url) {
68
+ return match.groups.url;
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ /** A `VcsProvider` implementation for GitHub, backed by the GitHub REST API. */
74
+ export class GitHubProvider {
75
+ /** Used when no `--vcs-host` is supplied, so selecting a provider is enough to reach its SaaS instance. */
76
+ static defaultHost = 'github.com';
77
+ kind = 'github';
78
+ terminology = {
79
+ changeRequest: 'pull request',
80
+ changeRequestShort: 'PR',
81
+ projectVariable: 'GitHub Actions variable',
82
+ };
83
+ host;
84
+ apiUrl;
85
+ token;
86
+ constructor(options) {
87
+ const { host, token, apiUrl } = options;
88
+ if (!token) {
89
+ throw new Error('GitHub access token is required.');
90
+ }
91
+ const resolvedHost = host ?? GitHubProvider.defaultHost;
92
+ // github.com serves its API from a dedicated hostname; GitHub Enterprise Server serves it
93
+ // from /api/v3 on the instance itself.
94
+ const derived = resolvedHost === GitHubProvider.defaultHost ? 'https://api.github.com' : `https://${resolvedHost}/api/v3`;
95
+ this.apiUrl = (apiUrl ?? derived).replace(/\/+$/, '');
96
+ this.host = resolvedHost;
97
+ this.token = token;
98
+ }
99
+ // eslint-disable-next-line class-methods-use-this -- part of the VcsProvider instance contract; GitHub Actions variables are process-global
100
+ getCiContext() {
101
+ return {
102
+ projectPath: process.env.GITHUB_REPOSITORY,
103
+ projectId: process.env.GITHUB_REPOSITORY_ID,
104
+ };
105
+ }
106
+ async listProjects(orgId) {
107
+ const repos = [];
108
+ // GitHub orgs are flat, so there is no subgroup recursion to do here.
109
+ let endpoint = `/orgs/${encodeURIComponent(orgId)}/repos?per_page=100`;
110
+ while (endpoint) {
111
+ // eslint-disable-next-line no-await-in-loop -- pagination is inherently sequential (the next URL comes from this page's Link header)
112
+ const response = await this.request(endpoint);
113
+ // eslint-disable-next-line no-await-in-loop
114
+ const data = (await response.json());
115
+ if (!Array.isArray(data) || data.length === 0) {
116
+ break;
117
+ }
118
+ repos.push(...data);
119
+ endpoint = nextLink(response.headers.get('Link'));
120
+ }
121
+ return repos.map(normalizeProject);
122
+ }
123
+ async getFileContent(project, filePath, ref) {
124
+ const endpoint = `${repoPath(project)}/contents/${encodeFilePath(filePath)}?ref=${encodeURIComponent(ref)}`;
125
+ const response = await this.request(endpoint, { headers: { Accept: 'application/vnd.github.raw' } });
126
+ return response.text();
127
+ }
128
+ async compareRefs(project, from, to) {
129
+ const endpoint = `${repoPath(project)}/compare/${encodeURIComponent(from)}...${encodeURIComponent(to)}`;
130
+ const response = (await this.getJson(endpoint));
131
+ return (response.commits ?? []).map((commit) => ({
132
+ sha: commit.sha,
133
+ message: commit.commit.message,
134
+ raw: commit,
135
+ }));
136
+ }
137
+ async branchExists(project, branchName) {
138
+ const endpoint = `${repoPath(project)}/branches/${encodeURIComponent(branchName)}`;
139
+ try {
140
+ await this.request(endpoint, { method: 'GET' });
141
+ return true;
142
+ }
143
+ catch (error) {
144
+ const err = error;
145
+ if (err.message.includes('404')) {
146
+ return false;
147
+ }
148
+ throw err;
149
+ }
150
+ }
151
+ async createBranch(project, branchName, ref) {
152
+ // GitHub creates refs by SHA, so the source branch has to be resolved first.
153
+ const sourceRef = (await this.getJson(`${repoPath(project)}/git/ref/heads/${encodeURIComponent(ref)}`));
154
+ const response = await this.request(`${repoPath(project)}/git/refs`, {
155
+ method: 'POST',
156
+ body: { ref: `refs/heads/${branchName}`, sha: sourceRef.object.sha },
157
+ });
158
+ const raw = await response.json();
159
+ return { name: branchName, raw };
160
+ }
161
+ async commitFile(project, branchName, commitMessage, filePath, content) {
162
+ const encodedPath = encodeFilePath(filePath);
163
+ // Updating an existing file through the contents API requires its current blob SHA.
164
+ const existing = (await this.getJson(`${repoPath(project)}/contents/${encodedPath}?ref=${encodeURIComponent(branchName)}`));
165
+ const response = await this.request(`${repoPath(project)}/contents/${encodedPath}`, {
166
+ method: 'PUT',
167
+ body: {
168
+ message: commitMessage,
169
+ content: Buffer.from(content, 'utf8').toString('base64'),
170
+ branch: branchName,
171
+ sha: existing.sha,
172
+ },
173
+ });
174
+ const raw = (await response.json());
175
+ return { id: raw.commit?.sha, raw };
176
+ }
177
+ async findOpenMergeRequest(project, sourceBranch, targetBranch) {
178
+ const { owner } = splitProject(project);
179
+ const head = `${owner}:${sourceBranch}`;
180
+ const endpoint = `${repoPath(project)}/pulls?state=open&head=${encodeURIComponent(head)}&base=${encodeURIComponent(targetBranch)}`;
181
+ const pullRequests = (await this.getJson(endpoint));
182
+ return pullRequests.length > 0 ? normalizePullRequest(pullRequests[0]) : undefined;
183
+ }
184
+ async createMergeRequest(project, sourceBranch, targetBranch, title, description, labels) {
185
+ // GitLab's `remove_source_branch` has no per-pull-request equivalent here; branch cleanup is
186
+ // the repo-level `delete_branch_on_merge` setting.
187
+ const response = await this.request(`${repoPath(project)}/pulls`, {
188
+ method: 'POST',
189
+ body: { title, body: description, head: sourceBranch, base: targetBranch },
190
+ });
191
+ const pullRequest = normalizePullRequest((await response.json()));
192
+ if (labels?.length) {
193
+ // Labels live on the issue that backs the pull request, not on the pull request itself.
194
+ await this.request(`${repoPath(project)}/issues/${pullRequest.iid}/labels`, {
195
+ method: 'POST',
196
+ body: { labels },
197
+ });
198
+ }
199
+ return pullRequest;
200
+ }
201
+ async updateMergeRequest(project, mergeRequest, title, description, labels) {
202
+ const response = await this.request(`${repoPath(project)}/pulls/${mergeRequest.iid}`, {
203
+ method: 'PUT',
204
+ body: { title, body: description },
205
+ });
206
+ const updated = normalizePullRequest((await response.json()));
207
+ if (labels?.length) {
208
+ // PUT replaces the label set, matching GitLab's update-with-labels semantics.
209
+ await this.request(`${repoPath(project)}/issues/${updated.iid}/labels`, {
210
+ method: 'PUT',
211
+ body: { labels },
212
+ });
213
+ }
214
+ return updated;
215
+ }
216
+ async getProjectVariables(project) {
217
+ try {
218
+ const result = (await this.getJson(`${repoPath(project)}/actions/variables`));
219
+ return (result.variables ?? []).map((variable) => ({
220
+ key: variable.name,
221
+ value: variable.value,
222
+ raw: variable,
223
+ }));
224
+ }
225
+ catch {
226
+ // If we don't have permission to read variables, treat it as "no variables".
227
+ return [];
228
+ }
229
+ }
230
+ // eslint-disable-next-line class-methods-use-this -- part of the VcsProvider instance contract; the path scheme needs no instance state
231
+ buildChangeRequestUrl(projectUrl, changeRequestIid) {
232
+ return `${projectUrl.replace(/\/+$/, '')}/pull/${changeRequestIid}`;
233
+ }
234
+ buildAuthenticatedRemoteUrl(token, projectPath) {
235
+ return `https://x-access-token:${token}@${this.host}/${projectPath}.git`;
236
+ }
237
+ buildCiCloneUrl(ciJobToken, projectPath) {
238
+ // GitHub has no separate job token; Actions hands the workflow a GITHUB_TOKEN that
239
+ // authenticates the same way.
240
+ return `https://x-access-token:${ciJobToken}@${this.host}/${projectPath}.git`;
241
+ }
242
+ async request(endpoint, options = {}) {
243
+ const url = endpoint.startsWith('http') ? endpoint : `${this.apiUrl}${endpoint}`;
244
+ const headers = {
245
+ Authorization: `Bearer ${this.token}`,
246
+ Accept: 'application/vnd.github+json',
247
+ 'X-GitHub-Api-Version': '2022-11-28',
248
+ 'Content-Type': 'application/json',
249
+ ...options.headers,
250
+ };
251
+ const response = await fetch(url, {
252
+ method: options.method,
253
+ headers,
254
+ body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
255
+ });
256
+ if (!response.ok) {
257
+ const errorText = await response.text().catch(() => 'Unknown error');
258
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText} - ${errorText}`);
259
+ }
260
+ return response;
261
+ }
262
+ async getJson(endpoint) {
263
+ const response = await this.request(endpoint);
264
+ return response.json();
265
+ }
266
+ }
267
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/vcs/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { VcsBranch, VcsCiContext, VcsCommit, VcsCommitLogEntry, VcsMergeRequest, VcsProject, VcsProjectRef, VcsProjectVariable, VcsProvider, VcsProviderOptions, VcsTerminology } from './types.js';
2
+ /** A `VcsProvider` implementation for GitLab, backed by the GitLab REST API v4. */
3
+ export declare class GitLabProvider implements VcsProvider {
4
+ /** Used when no `--vcs-host` is supplied, so selecting a provider is enough to reach its SaaS instance. */
5
+ static readonly defaultHost = "gitlab.com";
6
+ readonly kind: "gitlab";
7
+ readonly terminology: VcsTerminology;
8
+ readonly host: string;
9
+ readonly apiUrl: string;
10
+ private readonly token;
11
+ private readonly tokenKind;
12
+ constructor(options: VcsProviderOptions);
13
+ getCiContext(): VcsCiContext;
14
+ listProjects(groupId: string | number): Promise<VcsProject[]>;
15
+ getFileContent(project: VcsProjectRef, filePath: string, ref: string): Promise<string>;
16
+ compareRefs(project: VcsProjectRef, from: string, to: string): Promise<VcsCommitLogEntry[]>;
17
+ branchExists(project: VcsProjectRef, branchName: string): Promise<boolean>;
18
+ createBranch(project: VcsProjectRef, branchName: string, ref: string): Promise<VcsBranch>;
19
+ commitFile(project: VcsProjectRef, branchName: string, commitMessage: string, filePath: string, content: string): Promise<VcsCommit>;
20
+ findOpenMergeRequest(project: VcsProjectRef, sourceBranch: string, targetBranch: string): Promise<VcsMergeRequest | undefined>;
21
+ createMergeRequest(project: VcsProjectRef, sourceBranch: string, targetBranch: string, title: string, description: string, labels?: string[]): Promise<VcsMergeRequest>;
22
+ updateMergeRequest(project: VcsProjectRef, mergeRequest: VcsMergeRequest, title: string, description: string, labels?: string[]): Promise<VcsMergeRequest>;
23
+ getProjectVariables(project: VcsProjectRef): Promise<VcsProjectVariable[]>;
24
+ buildChangeRequestUrl(projectUrl: string, changeRequestIid: string | number): string;
25
+ buildAuthenticatedRemoteUrl(token: string, projectPath: string): string;
26
+ buildCiCloneUrl(ciJobToken: string, projectPath: string): string;
27
+ private request;
28
+ private getJson;
29
+ private getRawText;
30
+ }
@@ -0,0 +1,242 @@
1
+ /*
2
+ * Copyright (c) 2026, Clay Chipps.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ function normalizeProject(project) {
17
+ return {
18
+ id: project.id,
19
+ key: String(project.id),
20
+ name: project.name,
21
+ pathWithNamespace: project.path_with_namespace,
22
+ defaultBranch: project.default_branch,
23
+ archived: Boolean(project.archived),
24
+ empty: Boolean(project.empty_repo),
25
+ isFork: Boolean(project.forked_from_project) || project.fork === true,
26
+ raw: project,
27
+ };
28
+ }
29
+ function normalizeMergeRequest(mergeRequest) {
30
+ return {
31
+ id: mergeRequest.id,
32
+ iid: mergeRequest.iid,
33
+ title: mergeRequest.title,
34
+ description: mergeRequest.description,
35
+ sourceBranch: mergeRequest.source_branch,
36
+ targetBranch: mergeRequest.target_branch,
37
+ webUrl: mergeRequest.web_url,
38
+ raw: mergeRequest,
39
+ };
40
+ }
41
+ /** GitLab addresses projects by numeric ID or URL-encoded path; both arrive here already usable. */
42
+ function projectSegment(project) {
43
+ const key = typeof project === 'object' ? project.key : String(project);
44
+ return encodeURIComponent(key);
45
+ }
46
+ /** A `VcsProvider` implementation for GitLab, backed by the GitLab REST API v4. */
47
+ export class GitLabProvider {
48
+ /** Used when no `--vcs-host` is supplied, so selecting a provider is enough to reach its SaaS instance. */
49
+ static defaultHost = 'gitlab.com';
50
+ kind = 'gitlab';
51
+ terminology = {
52
+ changeRequest: 'merge request',
53
+ changeRequestShort: 'MR',
54
+ projectVariable: 'GitLab CI/CD variable',
55
+ };
56
+ host;
57
+ apiUrl;
58
+ token;
59
+ tokenKind;
60
+ constructor(options) {
61
+ const { host, token, apiUrl, tokenKind } = options;
62
+ if (!token) {
63
+ throw new Error('GitLab access token is required.');
64
+ }
65
+ this.apiUrl = (apiUrl ?? `https://${host ?? GitLabProvider.defaultHost}/api/v4`).replace(/\/+$/, '');
66
+ // When only an API URL is supplied (the dependabot path), recover the host from it so the
67
+ // remote-URL builders stay usable.
68
+ this.host = host ?? new URL(this.apiUrl).host;
69
+ this.token = token;
70
+ this.tokenKind = tokenKind ?? 'personal';
71
+ }
72
+ // eslint-disable-next-line class-methods-use-this -- part of the VcsProvider instance contract; GitLab's CI variables are process-global
73
+ getCiContext() {
74
+ return {
75
+ projectPath: process.env.CI_PROJECT_PATH,
76
+ projectId: process.env.CI_PROJECT_ID,
77
+ };
78
+ }
79
+ async listProjects(groupId) {
80
+ let projects = [];
81
+ let page = 1;
82
+ let hasMore = true;
83
+ const encodedGroupId = encodeURIComponent(groupId);
84
+ while (hasMore) {
85
+ const endpoint = `/groups/${encodedGroupId}/projects?include_subgroups=true&per_page=100&page=${page}`;
86
+ // eslint-disable-next-line no-await-in-loop -- pagination is inherently sequential (next page depends on this one's headers)
87
+ const response = await this.request(endpoint);
88
+ // eslint-disable-next-line no-await-in-loop
89
+ const data = (await response.json());
90
+ if (!Array.isArray(data) || data.length === 0) {
91
+ break;
92
+ }
93
+ projects = projects.concat(data);
94
+ const nextPageHeader = response.headers.get('X-Next-Page');
95
+ if (nextPageHeader) {
96
+ const nextPageNum = Number(nextPageHeader);
97
+ if (nextPageNum && nextPageNum > page) {
98
+ page = nextPageNum;
99
+ }
100
+ else {
101
+ hasMore = false;
102
+ }
103
+ }
104
+ else if (data.length < 100) {
105
+ hasMore = false;
106
+ }
107
+ else {
108
+ page++;
109
+ }
110
+ }
111
+ return projects.map(normalizeProject);
112
+ }
113
+ async getFileContent(project, filePath, ref) {
114
+ const encodedFilePath = encodeURIComponent(filePath);
115
+ const endpoint = `/projects/${projectSegment(project)}/repository/files/${encodedFilePath}/raw?ref=${encodeURIComponent(ref)}`;
116
+ return this.getRawText(endpoint);
117
+ }
118
+ async compareRefs(project, from, to) {
119
+ const endpoint = `/projects/${projectSegment(project)}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`;
120
+ const response = (await this.getJson(endpoint));
121
+ return (response.commits ?? []).map((commit) => ({ sha: commit.id, message: commit.message, raw: commit }));
122
+ }
123
+ async branchExists(project, branchName) {
124
+ const encodedBranch = encodeURIComponent(branchName);
125
+ const endpoint = `/projects/${projectSegment(project)}/repository/branches/${encodedBranch}`;
126
+ try {
127
+ await this.request(endpoint, { method: 'GET' });
128
+ return true;
129
+ }
130
+ catch (error) {
131
+ const err = error;
132
+ if (err.message.includes('404')) {
133
+ return false;
134
+ }
135
+ throw err;
136
+ }
137
+ }
138
+ async createBranch(project, branchName, ref) {
139
+ const endpoint = `/projects/${projectSegment(project)}/repository/branches`;
140
+ const response = await this.request(endpoint, {
141
+ method: 'POST',
142
+ body: { branch: branchName, ref },
143
+ });
144
+ const raw = await response.json();
145
+ return { name: branchName, raw };
146
+ }
147
+ async commitFile(project, branchName, commitMessage, filePath, content) {
148
+ const endpoint = `/projects/${projectSegment(project)}/repository/commits`;
149
+ /* eslint-disable camelcase -- GitLab API field names */
150
+ const response = await this.request(endpoint, {
151
+ method: 'POST',
152
+ body: {
153
+ branch: branchName,
154
+ commit_message: commitMessage,
155
+ actions: [{ action: 'update', file_path: filePath, content }],
156
+ },
157
+ });
158
+ /* eslint-enable camelcase */
159
+ const raw = (await response.json());
160
+ return { id: raw.id, raw };
161
+ }
162
+ async findOpenMergeRequest(project, sourceBranch, targetBranch) {
163
+ const endpoint = `/projects/${projectSegment(project)}/merge_requests?state=opened&source_branch=${encodeURIComponent(sourceBranch)}&target_branch=${encodeURIComponent(targetBranch)}`;
164
+ const mergeRequests = (await this.getJson(endpoint));
165
+ return mergeRequests.length > 0 ? normalizeMergeRequest(mergeRequests[0]) : undefined;
166
+ }
167
+ async createMergeRequest(project, sourceBranch, targetBranch, title, description, labels) {
168
+ const endpoint = `/projects/${projectSegment(project)}/merge_requests`;
169
+ /* eslint-disable camelcase -- GitLab API field names */
170
+ const body = {
171
+ source_branch: sourceBranch,
172
+ target_branch: targetBranch,
173
+ title,
174
+ description,
175
+ remove_source_branch: true,
176
+ };
177
+ /* eslint-enable camelcase */
178
+ if (labels?.length) {
179
+ body.labels = labels.join(',');
180
+ }
181
+ const response = await this.request(endpoint, { method: 'POST', body });
182
+ return normalizeMergeRequest((await response.json()));
183
+ }
184
+ async updateMergeRequest(project, mergeRequest, title, description, labels) {
185
+ // GitLab's per-merge-request endpoints take the project-scoped `iid`, not the global `id`.
186
+ const endpoint = `/projects/${projectSegment(project)}/merge_requests/${mergeRequest.iid}`;
187
+ const body = { title, description };
188
+ if (labels?.length) {
189
+ body.labels = labels.join(',');
190
+ }
191
+ const response = await this.request(endpoint, { method: 'PUT', body });
192
+ return normalizeMergeRequest((await response.json()));
193
+ }
194
+ async getProjectVariables(project) {
195
+ try {
196
+ const variables = (await this.getJson(`/projects/${projectSegment(project)}/variables`));
197
+ return variables.map((variable) => ({ key: variable.key, value: variable.value, raw: variable }));
198
+ }
199
+ catch {
200
+ // If we don't have permission to read variables, treat it as "no variables".
201
+ return [];
202
+ }
203
+ }
204
+ // eslint-disable-next-line class-methods-use-this -- part of the VcsProvider instance contract; the path scheme needs no instance state
205
+ buildChangeRequestUrl(projectUrl, changeRequestIid) {
206
+ return `${projectUrl.replace(/\/+$/, '')}/-/merge_requests/${changeRequestIid}`;
207
+ }
208
+ buildAuthenticatedRemoteUrl(token, projectPath) {
209
+ return `https://oauth2:${token}@${this.host}/${projectPath}.git`;
210
+ }
211
+ buildCiCloneUrl(ciJobToken, projectPath) {
212
+ return `https://gitlab-ci-token:${ciJobToken}@${this.host}/${projectPath}.git`;
213
+ }
214
+ async request(endpoint, options = {}) {
215
+ const url = endpoint.startsWith('http') ? endpoint : `${this.apiUrl}${endpoint}`;
216
+ const tokenHeader = this.tokenKind === 'job' ? 'JOB-TOKEN' : 'PRIVATE-TOKEN';
217
+ const headers = {
218
+ [tokenHeader]: this.token,
219
+ 'Content-Type': 'application/json',
220
+ ...options.headers,
221
+ };
222
+ const response = await fetch(url, {
223
+ method: options.method,
224
+ headers,
225
+ body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
226
+ });
227
+ if (!response.ok) {
228
+ const errorText = await response.text().catch(() => 'Unknown error');
229
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText} - ${errorText}`);
230
+ }
231
+ return response;
232
+ }
233
+ async getJson(endpoint) {
234
+ const response = await this.request(endpoint);
235
+ return response.json();
236
+ }
237
+ async getRawText(endpoint) {
238
+ const response = await this.request(endpoint);
239
+ return response.text();
240
+ }
241
+ }
242
+ //# sourceMappingURL=gitlab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab.js","sourceRoot":"","sources":["../../src/vcs/gitlab.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { createVcsProvider, listVcsProviderKinds, registerVcsProvider } from './registry.js';
2
+ export { GitHubProvider } from './github.js';
3
+ export { GitLabProvider } from './gitlab.js';
4
+ export type { VcsBranch, VcsCiContext, VcsCommit, VcsCommitLogEntry, VcsMergeRequest, VcsProject, VcsProjectRef, VcsProjectVariable, VcsProvider, VcsProviderFactory, VcsProviderKind, VcsProviderOptions, VcsTerminology, } from './types.js';
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright (c) 2026, Clay Chipps.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { GitHubProvider } from './github.js';
17
+ import { GitLabProvider } from './gitlab.js';
18
+ import { registerVcsProvider } from './registry.js';
19
+ registerVcsProvider('gitlab', (options) => new GitLabProvider(options));
20
+ registerVcsProvider('github', (options) => new GitHubProvider(options));
21
+ export { createVcsProvider, listVcsProviderKinds, registerVcsProvider } from './registry.js';
22
+ export { GitHubProvider } from './github.js';
23
+ export { GitLabProvider } from './gitlab.js';
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vcs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,mBAAmB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AACxE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { VcsProvider, VcsProviderFactory, VcsProviderKind, VcsProviderOptions } from './types.js';
2
+ /**
3
+ * Registers a platform implementation. Built-in providers register themselves when `./index.js` is
4
+ * imported; call this directly to add a platform without modifying this package.
5
+ */
6
+ export declare function registerVcsProvider(kind: VcsProviderKind, factory: VcsProviderFactory): void;
7
+ /** The platforms currently registered, for flag options and error messages. */
8
+ export declare function listVcsProviderKinds(): VcsProviderKind[];
9
+ /** Builds a provider for the given platform and instance. */
10
+ export declare function createVcsProvider(kind: VcsProviderKind, options: VcsProviderOptions): VcsProvider;
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright (c) 2026, Clay Chipps.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ const factories = new Map();
17
+ /**
18
+ * Registers a platform implementation. Built-in providers register themselves when `./index.js` is
19
+ * imported; call this directly to add a platform without modifying this package.
20
+ */
21
+ export function registerVcsProvider(kind, factory) {
22
+ factories.set(kind, factory);
23
+ }
24
+ /** The platforms currently registered, for flag options and error messages. */
25
+ export function listVcsProviderKinds() {
26
+ return [...factories.keys()].sort();
27
+ }
28
+ /** Builds a provider for the given platform and instance. */
29
+ export function createVcsProvider(kind, options) {
30
+ const factory = factories.get(kind);
31
+ if (!factory) {
32
+ const known = listVcsProviderKinds().join(', ');
33
+ throw new Error(`VCS provider "${kind}" is not supported. Registered providers: ${known}.`);
34
+ }
35
+ return factory(options);
36
+ }
37
+ //# sourceMappingURL=registry.js.map