@catladder/cli 3.13.1 → 3.14.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.
- package/dist/bundles/catenv/index.js +3 -3
- package/dist/bundles/cli/index.js +9 -9
- package/dist/cli/src/apps/cli/commands/project/setup/index.js +2 -0
- package/dist/cli/src/apps/cli/commands/project/setup/index.js.map +1 -1
- package/dist/cli/src/apps/cli/commands/project/setup/setupAgents.d.ts +2 -0
- package/dist/cli/src/apps/cli/commands/project/setup/setupAgents.js +155 -0
- package/dist/cli/src/apps/cli/commands/project/setup/setupAgents.js.map +1 -0
- package/dist/cli/src/config/getProjectConfig.js +4 -1
- package/dist/cli/src/config/getProjectConfig.js.map +1 -1
- package/dist/pipeline/src/bash/bashEscape.d.ts +2 -0
- package/dist/pipeline/src/bash/bashEscape.js +5 -1
- package/dist/pipeline/src/bash/bashEscape.js.map +1 -1
- package/dist/pipeline/src/pipeline/agent/createAgentContext.d.ts +6 -0
- package/dist/pipeline/src/pipeline/agent/createAgentContext.js +19 -0
- package/dist/pipeline/src/pipeline/agent/createAgentContext.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/createAgentEventJob.d.ts +2 -0
- package/dist/pipeline/src/pipeline/agent/createAgentEventJob.js +37 -0
- package/dist/pipeline/src/pipeline/agent/createAgentEventJob.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/createAgentReviewJob.d.ts +2 -0
- package/dist/pipeline/src/pipeline/agent/createAgentReviewJob.js +34 -0
- package/dist/pipeline/src/pipeline/agent/createAgentReviewJob.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/createJobsForAgentContext.d.ts +2 -0
- package/dist/pipeline/src/pipeline/agent/createJobsForAgentContext.js +10 -0
- package/dist/pipeline/src/pipeline/agent/createJobsForAgentContext.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/prompts.d.ts +6 -0
- package/dist/pipeline/src/pipeline/agent/prompts.js +221 -0
- package/dist/pipeline/src/pipeline/agent/prompts.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/shared.d.ts +6 -0
- package/dist/pipeline/src/pipeline/agent/shared.js +30 -0
- package/dist/pipeline/src/pipeline/agent/shared.js.map +1 -0
- package/dist/pipeline/src/pipeline/agent/utils.d.ts +3 -0
- package/dist/pipeline/src/pipeline/agent/utils.js +14 -0
- package/dist/pipeline/src/pipeline/agent/utils.js.map +1 -0
- package/dist/pipeline/src/pipeline/createAllJobs.d.ts +5 -1
- package/dist/pipeline/src/pipeline/createAllJobs.js +10 -1
- package/dist/pipeline/src/pipeline/createAllJobs.js.map +1 -1
- package/dist/pipeline/src/pipeline/createJobsForComponent.js.map +1 -1
- package/dist/pipeline/src/pipeline/createMainPipeline.js +13 -4
- package/dist/pipeline/src/pipeline/createMainPipeline.js.map +1 -1
- package/dist/pipeline/src/pipeline/gitlab/createGitlabJobs.d.ts +3 -3
- package/dist/pipeline/src/pipeline/gitlab/createGitlabJobs.js +18 -9
- package/dist/pipeline/src/pipeline/gitlab/createGitlabJobs.js.map +1 -1
- package/dist/pipeline/src/pipeline/gitlab/createGitlabPipeline.js +7 -0
- package/dist/pipeline/src/pipeline/gitlab/createGitlabPipeline.js.map +1 -1
- package/dist/pipeline/src/rules/index.d.ts +1 -0
- package/dist/pipeline/src/rules/index.js +8 -1
- package/dist/pipeline/src/rules/index.js.map +1 -1
- package/dist/pipeline/src/types/agent.d.ts +7 -0
- package/dist/pipeline/src/types/agent.js +3 -0
- package/dist/pipeline/src/types/agent.js.map +1 -0
- package/dist/pipeline/src/types/config.d.ts +2 -0
- package/dist/pipeline/src/types/config.js.map +1 -1
- package/dist/pipeline/src/types/context.d.ts +7 -0
- package/dist/pipeline/src/types/context.js.map +1 -1
- package/dist/pipeline/src/types/index.d.ts +1 -0
- package/dist/pipeline/src/types/index.js +1 -0
- package/dist/pipeline/src/types/index.js.map +1 -1
- package/dist/pipeline/src/types/jobs.d.ts +5 -1
- package/dist/pipeline/src/types/jobs.js +1 -0
- package/dist/pipeline/src/types/jobs.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/cli/commands/project/setup/index.ts +2 -0
- package/src/apps/cli/commands/project/setup/setupAgents.ts +319 -0
- package/src/config/getProjectConfig.ts +8 -3
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import { getAllPipelineContexts } from "../../../../../config/getProjectConfig";
|
|
|
3
3
|
import { setupAccessTokens } from "./setupAccessTokens";
|
|
4
4
|
import { setupContext } from "./setupContext";
|
|
5
5
|
import { setupTopic } from "./setupTopic";
|
|
6
|
+
import { setupAgents } from "./setupAgents";
|
|
6
7
|
|
|
7
8
|
export const setupProject = async (
|
|
8
9
|
instance: CommandInstance,
|
|
@@ -18,6 +19,7 @@ export const setupProject = async (
|
|
|
18
19
|
}
|
|
19
20
|
await setupAccessTokens(instance);
|
|
20
21
|
await setupTopic(instance);
|
|
22
|
+
await setupAgents(instance);
|
|
21
23
|
instance.log("");
|
|
22
24
|
instance.log("gitlab is ready! 🥂");
|
|
23
25
|
instance.log("\n");
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import type { CommandInstance } from "vorpal";
|
|
2
|
+
import { doGitlabRequest, getProjectInfo } from "../../../../../utils/gitlab";
|
|
3
|
+
import { getProjectConfig } from "../../../../../config/getProjectConfig";
|
|
4
|
+
import { getGitRemoteHostAndPath } from "../../../../../git/gitProjectInformation";
|
|
5
|
+
import type { AgentConfig } from "@catladder/pipeline";
|
|
6
|
+
|
|
7
|
+
type TriggerToken = {
|
|
8
|
+
id: number;
|
|
9
|
+
description: string;
|
|
10
|
+
created_at: string;
|
|
11
|
+
last_used: string | null;
|
|
12
|
+
token: string;
|
|
13
|
+
updated_at: string;
|
|
14
|
+
owner: any;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type Webhook = {
|
|
18
|
+
id: number;
|
|
19
|
+
url: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
project_id: number;
|
|
23
|
+
push_events: boolean;
|
|
24
|
+
push_events_branch_filter: string;
|
|
25
|
+
issues_events: boolean;
|
|
26
|
+
confidential_issues_events: boolean;
|
|
27
|
+
merge_requests_events: boolean;
|
|
28
|
+
tag_push_events: boolean;
|
|
29
|
+
note_events: boolean;
|
|
30
|
+
confidential_note_events: boolean;
|
|
31
|
+
job_events: boolean;
|
|
32
|
+
pipeline_events: boolean;
|
|
33
|
+
wiki_page_events: boolean;
|
|
34
|
+
deployment_events: boolean;
|
|
35
|
+
releases_events: boolean;
|
|
36
|
+
milestone_events: boolean;
|
|
37
|
+
feature_flag_events: boolean;
|
|
38
|
+
enable_ssl_verification: boolean;
|
|
39
|
+
repository_update_events: boolean;
|
|
40
|
+
alert_status: string;
|
|
41
|
+
disabled_until: string | null;
|
|
42
|
+
url_variables: any[];
|
|
43
|
+
created_at: string;
|
|
44
|
+
resource_access_token_events: boolean;
|
|
45
|
+
custom_webhook_template: string;
|
|
46
|
+
custom_headers: Array<{ key: string; value?: string }>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type WebhookData = {
|
|
50
|
+
url: string;
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
issues_events: boolean;
|
|
54
|
+
confidential_issues_events: boolean;
|
|
55
|
+
merge_requests_events: boolean;
|
|
56
|
+
note_events: boolean;
|
|
57
|
+
confidential_note_events: boolean;
|
|
58
|
+
custom_webhook_template: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const listTriggerTokens = async (
|
|
62
|
+
instance: CommandInstance,
|
|
63
|
+
projectId: string,
|
|
64
|
+
): Promise<TriggerToken[]> => {
|
|
65
|
+
return await doGitlabRequest(instance, `projects/${projectId}/triggers`);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const deleteTriggerToken = async (
|
|
69
|
+
instance: CommandInstance,
|
|
70
|
+
projectId: string,
|
|
71
|
+
triggerId: number,
|
|
72
|
+
): Promise<void> => {
|
|
73
|
+
await doGitlabRequest(
|
|
74
|
+
instance,
|
|
75
|
+
`projects/${projectId}/triggers/${triggerId}`,
|
|
76
|
+
undefined,
|
|
77
|
+
"DELETE",
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const createTriggerToken = async (
|
|
82
|
+
instance: CommandInstance,
|
|
83
|
+
projectId: string,
|
|
84
|
+
description: string,
|
|
85
|
+
): Promise<TriggerToken> => {
|
|
86
|
+
return await doGitlabRequest(
|
|
87
|
+
instance,
|
|
88
|
+
`projects/${projectId}/triggers`,
|
|
89
|
+
{ description },
|
|
90
|
+
"POST",
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const listWebhooks = async (
|
|
95
|
+
instance: CommandInstance,
|
|
96
|
+
projectId: string,
|
|
97
|
+
): Promise<Webhook[]> => {
|
|
98
|
+
return await doGitlabRequest(instance, `projects/${projectId}/hooks`);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createWebhook = async (
|
|
102
|
+
instance: CommandInstance,
|
|
103
|
+
projectId: string,
|
|
104
|
+
webhookData: WebhookData,
|
|
105
|
+
): Promise<Webhook> => {
|
|
106
|
+
return await doGitlabRequest(
|
|
107
|
+
instance,
|
|
108
|
+
`projects/${projectId}/hooks`,
|
|
109
|
+
webhookData,
|
|
110
|
+
"POST",
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const updateWebhook = async (
|
|
115
|
+
instance: CommandInstance,
|
|
116
|
+
projectId: string,
|
|
117
|
+
hookId: number,
|
|
118
|
+
webhookData: WebhookData,
|
|
119
|
+
): Promise<Webhook> => {
|
|
120
|
+
return await doGitlabRequest(
|
|
121
|
+
instance,
|
|
122
|
+
`projects/${projectId}/hooks/${hookId}`,
|
|
123
|
+
webhookData,
|
|
124
|
+
"PUT",
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const setupAgentWebhook = async (
|
|
129
|
+
instance: CommandInstance,
|
|
130
|
+
projectId: string,
|
|
131
|
+
agentName: string,
|
|
132
|
+
triggerToken: string,
|
|
133
|
+
): Promise<Webhook> => {
|
|
134
|
+
const webhookName = `cl_agent_${agentName}_webhook`;
|
|
135
|
+
const { gitRemoteHost } = await getGitRemoteHostAndPath();
|
|
136
|
+
const webhookUrl = `https://${gitRemoteHost}/api/v4/projects/${projectId}/ref/main/trigger/pipeline?token=${triggerToken}`;
|
|
137
|
+
|
|
138
|
+
instance.log(`Setting up webhook for agent: ${agentName}`);
|
|
139
|
+
|
|
140
|
+
// List existing webhooks
|
|
141
|
+
const existingWebhooks = await listWebhooks(instance, projectId);
|
|
142
|
+
|
|
143
|
+
// Find existing webhook by name
|
|
144
|
+
const existingWebhook = existingWebhooks.find(
|
|
145
|
+
(webhook) => webhook.name === webhookName,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Custom webhook template from the user's specification
|
|
149
|
+
const customWebhookTemplate = JSON.stringify({
|
|
150
|
+
variables: {
|
|
151
|
+
ASSIGNEE_USER_ID: "{{object_attributes.assignee_id}}",
|
|
152
|
+
OBJECT_DESCRIPTION: "{{object_attributes.description}}",
|
|
153
|
+
},
|
|
154
|
+
payload: {
|
|
155
|
+
object_kind: "{{object_kind}}",
|
|
156
|
+
event_type: "{{event_type}}",
|
|
157
|
+
user_id: "{{user.id}}",
|
|
158
|
+
user_name: "{{user.name}}",
|
|
159
|
+
user_username: "{{user.username}}",
|
|
160
|
+
user_avatar_url: "{{user.avatar_url}}",
|
|
161
|
+
user_email: "{{user.email}}",
|
|
162
|
+
project_id: "{{project.id}}",
|
|
163
|
+
project_name: "{{project.name}}",
|
|
164
|
+
project_web_url: "{{project.web_url}}",
|
|
165
|
+
project_namespace: "{{project.namespace}}",
|
|
166
|
+
project_path_with_namespace: "{{project.path_with_namespace}}",
|
|
167
|
+
object_attributes_id: "{{object_attributes.id}}",
|
|
168
|
+
object_attributes_iid: "{{object_attributes.iid}}",
|
|
169
|
+
object_attributes_title: "{{object_attributes.title}}",
|
|
170
|
+
object_attributes_description: "{{object_attributes.description}}",
|
|
171
|
+
object_attributes_state: "{{object_attributes.state}}",
|
|
172
|
+
object_attributes_created_at: "{{object_attributes.created_at}}",
|
|
173
|
+
object_attributes_updated_at: "{{object_attributes.updated_at}}",
|
|
174
|
+
object_attributes_url: "{{object_attributes.url}}",
|
|
175
|
+
object_attributes_action: "{{object_attributes.action}}",
|
|
176
|
+
object_attributes_noteable_type: "{{object_attributes.noteable_type}}",
|
|
177
|
+
merge_request_id: "{{merge_request.id}}",
|
|
178
|
+
merge_request_iid: "{{merge_request.iid}}",
|
|
179
|
+
merge_request_title: "{{merge_request.title}}",
|
|
180
|
+
merge_request_source_branch: "{{merge_request.source_branch}}",
|
|
181
|
+
merge_request_time_estimate: "{{merge_request.time_estimate}}",
|
|
182
|
+
//merge_request_description: "{{merge_request.description}}",
|
|
183
|
+
merge_request_merge_status: "{{merge_request.merge_status}}",
|
|
184
|
+
merge_request_last_commit_title: "{{merge_request.last_commit.title}}",
|
|
185
|
+
merge_request_last_commit_message:
|
|
186
|
+
"{{merge_request.last_commit.message}}",
|
|
187
|
+
merge_request_last_commit_author_name:
|
|
188
|
+
"{{merge_request.last_commit.author.name}}",
|
|
189
|
+
merge_request_last_commit_author_email:
|
|
190
|
+
"{{merge_request.last_commit.author.email}}",
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const webhookData: WebhookData = {
|
|
195
|
+
url: webhookUrl,
|
|
196
|
+
name: webhookName,
|
|
197
|
+
description: `Agent webhook for ${agentName} - triggers pipeline with custom payload`,
|
|
198
|
+
issues_events: true,
|
|
199
|
+
confidential_issues_events: false,
|
|
200
|
+
merge_requests_events: false,
|
|
201
|
+
note_events: true,
|
|
202
|
+
confidential_note_events: false,
|
|
203
|
+
custom_webhook_template: customWebhookTemplate,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
let webhook: Webhook;
|
|
207
|
+
|
|
208
|
+
if (existingWebhook) {
|
|
209
|
+
instance.log(
|
|
210
|
+
`Updating existing webhook: ${webhookName} (ID: ${existingWebhook.id})`,
|
|
211
|
+
);
|
|
212
|
+
webhook = await updateWebhook(
|
|
213
|
+
instance,
|
|
214
|
+
projectId,
|
|
215
|
+
existingWebhook.id,
|
|
216
|
+
webhookData,
|
|
217
|
+
);
|
|
218
|
+
instance.log(`✅ Updated webhook: ${webhook.name}`);
|
|
219
|
+
} else {
|
|
220
|
+
instance.log(`Creating new webhook: ${webhookName}`);
|
|
221
|
+
webhook = await createWebhook(instance, projectId, webhookData);
|
|
222
|
+
instance.log(`✅ Created webhook: ${webhook.name}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
instance.log(` Webhook ID: ${webhook.id}`);
|
|
226
|
+
instance.log(` Webhook URL: ${webhook.url}`);
|
|
227
|
+
|
|
228
|
+
return webhook;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const setupAgentTriggerToken = async (
|
|
232
|
+
instance: CommandInstance,
|
|
233
|
+
projectId: string,
|
|
234
|
+
agentName: string,
|
|
235
|
+
): Promise<TriggerToken> => {
|
|
236
|
+
const tokenName = `cl_agent_${agentName}`;
|
|
237
|
+
|
|
238
|
+
instance.log(`Setting up trigger token for agent: ${agentName}`);
|
|
239
|
+
|
|
240
|
+
// List existing trigger tokens
|
|
241
|
+
const existingTokens = await listTriggerTokens(instance, projectId);
|
|
242
|
+
|
|
243
|
+
// Find and delete existing cl_agent_<agentName> tokens
|
|
244
|
+
const existingAgentTokens = existingTokens.filter((token) =>
|
|
245
|
+
token.description.startsWith(`cl_agent_${agentName}`),
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
for (const token of existingAgentTokens) {
|
|
249
|
+
instance.log(
|
|
250
|
+
`Deleting existing trigger token: ${token.description} (ID: ${token.id})`,
|
|
251
|
+
);
|
|
252
|
+
await deleteTriggerToken(instance, projectId, token.id);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Create new trigger token
|
|
256
|
+
const description = `${tokenName} - Agent trigger token for ${agentName}`;
|
|
257
|
+
const newToken = await createTriggerToken(instance, projectId, description);
|
|
258
|
+
|
|
259
|
+
instance.log(`✅ Created new trigger token: ${newToken.description}`);
|
|
260
|
+
instance.log(` Token ID: ${newToken.id}`);
|
|
261
|
+
instance.log(` Token: ${newToken.token}`);
|
|
262
|
+
|
|
263
|
+
return newToken;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const setupAgent = async (
|
|
267
|
+
instance: CommandInstance,
|
|
268
|
+
agentName: string,
|
|
269
|
+
agentConfig: AgentConfig,
|
|
270
|
+
) => {
|
|
271
|
+
const { id: projectId } = await getProjectInfo(instance);
|
|
272
|
+
|
|
273
|
+
instance.log(`🤖 Setting up agent: ${agentName}`);
|
|
274
|
+
|
|
275
|
+
// Setup trigger token for this agent
|
|
276
|
+
const triggerToken = await setupAgentTriggerToken(
|
|
277
|
+
instance,
|
|
278
|
+
projectId,
|
|
279
|
+
agentName,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// Setup webhook for this agent
|
|
283
|
+
await setupAgentWebhook(instance, projectId, agentName, triggerToken.token);
|
|
284
|
+
|
|
285
|
+
instance.log(`✅ Agent ${agentName} setup complete!`);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export const setupAgents = async (instance: CommandInstance) => {
|
|
289
|
+
const config = await getProjectConfig();
|
|
290
|
+
|
|
291
|
+
instance.log("");
|
|
292
|
+
instance.log("🤖 Setting up agents...");
|
|
293
|
+
|
|
294
|
+
const agents = config.agents ?? {};
|
|
295
|
+
const agentEntries = Object.entries(agents);
|
|
296
|
+
|
|
297
|
+
if (agentEntries.length === 0) {
|
|
298
|
+
instance.log("No agents configured in project config");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
for (const [agentName, agentConfig] of agentEntries) {
|
|
303
|
+
if (agentConfig.type === "claude" && agentConfig) {
|
|
304
|
+
await setupAgent(instance, agentName, agentConfig);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
instance.log("✅ All agents setup complete!");
|
|
309
|
+
instance.log("");
|
|
310
|
+
instance.log("🔧 Manual setup required:");
|
|
311
|
+
instance.log("Please configure these environment variables in GitLab CI/CD:");
|
|
312
|
+
instance.log(" Go to Settings > CI/CD > Variables and add:");
|
|
313
|
+
instance.log(
|
|
314
|
+
" - ANTHROPIC_API_KEY (your Anthropic API key, mark as Protected & Masked)",
|
|
315
|
+
);
|
|
316
|
+
instance.log(
|
|
317
|
+
" - AGENT_GITLAB_PERSONAL_ACCESS_TOKEN (token from agent.claude user, mark as Protected & Masked)",
|
|
318
|
+
);
|
|
319
|
+
};
|
|
@@ -73,9 +73,7 @@ export const getAllComponentsWithAllEnvsFlat = async (): Promise<
|
|
|
73
73
|
if (!config) {
|
|
74
74
|
return [];
|
|
75
75
|
}
|
|
76
|
-
return
|
|
77
|
-
getAllEnvs(config, componentName).map((env) => ({ env, componentName })),
|
|
78
|
-
);
|
|
76
|
+
return getAllComponentsWithAllEnvsFlatFromConfig(config);
|
|
79
77
|
};
|
|
80
78
|
|
|
81
79
|
export const getAllComponentsWithAllEnvsHierarchical = async (): Promise<{
|
|
@@ -194,3 +192,10 @@ export const getJobOnlyEnvVarsResolved = async (
|
|
|
194
192
|
envionment.jobOnlyVars.deploy,
|
|
195
193
|
]);
|
|
196
194
|
};
|
|
195
|
+
function getAllComponentsWithAllEnvsFlatFromConfig(
|
|
196
|
+
config: Config,
|
|
197
|
+
): { env: string; componentName: string }[] {
|
|
198
|
+
return Object.keys(config.components).flatMap((componentName) =>
|
|
199
|
+
getAllEnvs(config, componentName).map((env) => ({ env, componentName })),
|
|
200
|
+
);
|
|
201
|
+
}
|