@intentius/chant-lexicon-gitlab 0.0.6 → 0.0.8
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/integrity.json +3 -3
- package/dist/manifest.json +1 -1
- package/dist/skills/chant-gitlab.md +59 -0
- package/package.json +1 -1
- package/src/codegen/docs.ts +57 -3
- package/src/codegen/package.ts +2 -0
- package/src/import/parser.test.ts +3 -3
- package/src/import/parser.ts +12 -26
- package/src/lsp/completions.ts +2 -0
- package/src/lsp/hover.ts +2 -0
- package/src/plugin.test.ts +5 -6
- package/src/plugin.ts +60 -61
- package/src/serializer.test.ts +6 -6
- package/src/serializer.ts +1 -9
- package/dist/skills/gitlab-ci.md +0 -37
- package/src/codegen/rollback.ts +0 -26
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "xxhash64",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "3a48cdeca93a7a4d",
|
|
5
5
|
"meta.json": "9ee0d2f2d1679f09",
|
|
6
6
|
"types/index.d.ts": "4e56a7de40d655c0",
|
|
7
7
|
"rules/missing-stage.ts": "6d5379e74209a735",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"rules/wgl011.ts": "b6b97e5104d91267",
|
|
12
12
|
"rules/yaml-helpers.ts": "a66cc193b4ef4f0a",
|
|
13
13
|
"rules/wgl010.ts": "1548cad287cdf286",
|
|
14
|
-
"skills/gitlab
|
|
14
|
+
"skills/chant-gitlab.md": "92ce73e97ee82ac9"
|
|
15
15
|
},
|
|
16
|
-
"composite": "
|
|
16
|
+
"composite": "c6ad87f69da787de"
|
|
17
17
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-gitlab
|
|
3
|
+
description: Build, validate, and deploy GitLab CI pipelines from a chant project
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Deploying GitLab CI Pipelines from Chant
|
|
8
|
+
|
|
9
|
+
This project defines GitLab CI jobs as TypeScript in `src/`. Use these steps to build, validate, and deploy.
|
|
10
|
+
|
|
11
|
+
## Build the pipeline
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
chant build src/ --output .gitlab-ci.yml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Validate before pushing
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
chant lint src/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For API-level validation against your GitLab instance:
|
|
24
|
+
```bash
|
|
25
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
|
26
|
+
"https://gitlab.com/api/v4/ci/lint" \
|
|
27
|
+
--data "{\"content\": \"$(cat .gitlab-ci.yml)\"}"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Deploy
|
|
31
|
+
|
|
32
|
+
Commit and push the generated `.gitlab-ci.yml` — GitLab runs the pipeline automatically:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
chant build src/ --output .gitlab-ci.yml
|
|
36
|
+
git add .gitlab-ci.yml
|
|
37
|
+
git commit -m "Update pipeline"
|
|
38
|
+
git push
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Check pipeline status
|
|
42
|
+
|
|
43
|
+
- GitLab UI: project → CI/CD → Pipelines
|
|
44
|
+
- API: `curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?per_page=5"`
|
|
45
|
+
|
|
46
|
+
## Retry a failed job
|
|
47
|
+
|
|
48
|
+
- GitLab UI: click Retry on the failed job
|
|
49
|
+
- API: `curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/retry"`
|
|
50
|
+
|
|
51
|
+
## Cancel a running pipeline
|
|
52
|
+
|
|
53
|
+
- API: `curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/cancel"`
|
|
54
|
+
|
|
55
|
+
## Troubleshooting
|
|
56
|
+
|
|
57
|
+
- Check job logs in GitLab UI: project → CI/CD → Jobs → click the job
|
|
58
|
+
- `chant lint src/` catches: missing scripts (WGL002), deprecated only/except (WGL001), missing stages (WGL003), artifacts without expiry (WGL004)
|
|
59
|
+
- Post-synth checks (WGL010, WGL011) run during build
|
package/package.json
CHANGED
package/src/codegen/docs.ts
CHANGED
|
@@ -58,8 +58,7 @@ The generated file includes:
|
|
|
58
58
|
| Chant (TypeScript) | YAML output | Rule |
|
|
59
59
|
|--------------------|-------------|------|
|
|
60
60
|
| \`export const buildApp = new Job({...})\` | \`build-app:\` | Export name → kebab-case job key |
|
|
61
|
-
| \`
|
|
62
|
-
| \`ifCondition: ...\` | \`if: ...\` | Reserved word properties use suffixed names |
|
|
61
|
+
| \`expire_in: "1 week"\` | \`expire_in: 1 week\` | Property names use spec-native snake_case |
|
|
63
62
|
| \`new Image({ name: "node:20" })\` | \`image: node:20\` | Single-property objects are collapsed |
|
|
64
63
|
|
|
65
64
|
## Validating locally
|
|
@@ -108,7 +107,7 @@ export async function generateDocs(opts?: { verbose?: boolean }): Promise<void>
|
|
|
108
107
|
description: "Jobs, stages, artifacts, caching, images, rules, environments, and triggers in the GitLab CI/CD lexicon",
|
|
109
108
|
content: `Every exported \`Job\` declaration becomes a job entry in the generated \`.gitlab-ci.yml\`. The serializer handles the translation automatically:
|
|
110
109
|
|
|
111
|
-
-
|
|
110
|
+
- Property names use spec-native snake_case (\`expire_in\`, \`allow_failure\`)
|
|
112
111
|
- Converts export names to kebab-case job keys (\`buildApp\` → \`build-app\`)
|
|
113
112
|
- Collects stages from all jobs into a \`stages:\` list
|
|
114
113
|
- Collapses single-property objects (\`new Image({ name: "node:20" })\` → \`image: node:20\`)
|
|
@@ -539,6 +538,61 @@ deploy:
|
|
|
539
538
|
5. **JUnit reports** — test artifacts include JUnit XML for GitLab MR display
|
|
540
539
|
`,
|
|
541
540
|
},
|
|
541
|
+
{
|
|
542
|
+
slug: "skills",
|
|
543
|
+
title: "AI Skills",
|
|
544
|
+
description: "AI agent skills bundled with the GitLab CI/CD lexicon",
|
|
545
|
+
content: `The GitLab lexicon ships an AI skill called **chant-gitlab** that teaches AI coding agents (like Claude Code) how to build, validate, and deploy GitLab CI pipelines from a chant project.
|
|
546
|
+
|
|
547
|
+
## What are skills?
|
|
548
|
+
|
|
549
|
+
Skills are structured markdown documents bundled with a lexicon. When an AI agent works in a chant project, it discovers and loads relevant skills automatically — giving it operational knowledge about the deployment workflow without requiring the user to explain each step.
|
|
550
|
+
|
|
551
|
+
## Installation
|
|
552
|
+
|
|
553
|
+
When you scaffold a new project with \`chant init --lexicon gitlab\`, the skill is installed to \`.claude/skills/chant-gitlab/SKILL.md\` for automatic discovery by Claude Code.
|
|
554
|
+
|
|
555
|
+
For existing projects, create the file manually:
|
|
556
|
+
|
|
557
|
+
\`\`\`
|
|
558
|
+
.claude/
|
|
559
|
+
skills/
|
|
560
|
+
chant-gitlab/
|
|
561
|
+
SKILL.md # skill content (see below)
|
|
562
|
+
\`\`\`
|
|
563
|
+
|
|
564
|
+
## Skill: chant-gitlab
|
|
565
|
+
|
|
566
|
+
The \`chant-gitlab\` skill covers the full deployment lifecycle:
|
|
567
|
+
|
|
568
|
+
- **Build** — \`chant build src/ --output .gitlab-ci.yml\`
|
|
569
|
+
- **Validate** — \`chant lint src/\` + GitLab CI Lint API
|
|
570
|
+
- **Deploy** — commit and push the generated YAML
|
|
571
|
+
- **Status** — GitLab UI or pipelines API
|
|
572
|
+
- **Retry** — retry failed jobs via UI or API
|
|
573
|
+
- **Cancel** — cancel running pipelines via API
|
|
574
|
+
- **Troubleshooting** — job logs, lint rule codes (WGL001–WGL004), post-synth checks (WGL010, WGL011)
|
|
575
|
+
|
|
576
|
+
The skill is invocable as a slash command: \`/chant-gitlab\`
|
|
577
|
+
|
|
578
|
+
## MCP integration
|
|
579
|
+
|
|
580
|
+
The lexicon also provides MCP (Model Context Protocol) tools and resources that AI agents can use programmatically:
|
|
581
|
+
|
|
582
|
+
| MCP tool | Description |
|
|
583
|
+
|----------|-------------|
|
|
584
|
+
| \`build\` | Build the chant project |
|
|
585
|
+
| \`lint\` | Run lint rules |
|
|
586
|
+
| \`explain\` | Summarize project resources |
|
|
587
|
+
| \`scaffold\` | Generate starter files |
|
|
588
|
+
| \`search\` | Search available resource types |
|
|
589
|
+
| \`gitlab:diff\` | Compare current build output against previous |
|
|
590
|
+
|
|
591
|
+
| MCP resource | Description |
|
|
592
|
+
|--------------|-------------|
|
|
593
|
+
| \`resource-catalog\` | JSON list of all supported GitLab CI entity types |
|
|
594
|
+
| \`examples/basic-pipeline\` | Example pipeline with build, test, and deploy jobs |`,
|
|
595
|
+
},
|
|
542
596
|
],
|
|
543
597
|
basePath: "/chant/lexicons/gitlab/",
|
|
544
598
|
};
|
package/src/codegen/package.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* with GitLab-specific manifest building and skill collection.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { createRequire } from "module";
|
|
6
7
|
import { readFileSync } from "fs";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
7
9
|
import { join, dirname } from "path";
|
|
8
10
|
import { fileURLToPath } from "url";
|
|
9
11
|
import type { IntrinsicDef } from "@intentius/chant/lexicon";
|
|
@@ -65,7 +65,7 @@ workflow:
|
|
|
65
65
|
expect(workflow!.properties.name).toBe("My Pipeline");
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
test("
|
|
68
|
+
test("preserves spec-native snake_case property keys", () => {
|
|
69
69
|
const yaml = `
|
|
70
70
|
test-job:
|
|
71
71
|
stage: test
|
|
@@ -77,8 +77,8 @@ test-job:
|
|
|
77
77
|
- npm test
|
|
78
78
|
`;
|
|
79
79
|
const ir = parser.parse(yaml);
|
|
80
|
-
expect(ir.resources[0].properties.
|
|
81
|
-
expect(ir.resources[0].properties.
|
|
80
|
+
expect(ir.resources[0].properties.before_script).toEqual(["echo setup"]);
|
|
81
|
+
expect(ir.resources[0].properties.after_script).toEqual(["echo done"]);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
test("converts kebab-case job names to camelCase", () => {
|
package/src/import/parser.ts
CHANGED
|
@@ -10,6 +10,14 @@ import type { TemplateParser, TemplateIR, ResourceIR } from "@intentius/chant/im
|
|
|
10
10
|
/**
|
|
11
11
|
* Reserved top-level keys in .gitlab-ci.yml that are NOT job definitions.
|
|
12
12
|
*/
|
|
13
|
+
/**
|
|
14
|
+
* Convert snake_case to camelCase — used only for TS variable names (logicalId),
|
|
15
|
+
* NOT for spec property names.
|
|
16
|
+
*/
|
|
17
|
+
function snakeToCamelCase(name: string): string {
|
|
18
|
+
return name.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
const RESERVED_KEYS = new Set([
|
|
14
22
|
"stages",
|
|
15
23
|
"variables",
|
|
@@ -24,28 +32,6 @@ const RESERVED_KEYS = new Set([
|
|
|
24
32
|
"pages",
|
|
25
33
|
]);
|
|
26
34
|
|
|
27
|
-
/**
|
|
28
|
-
* Map snake_case GitLab CI keys to camelCase for Chant properties.
|
|
29
|
-
*/
|
|
30
|
-
function toCamelCase(name: string): string {
|
|
31
|
-
return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Recursively convert snake_case keys in an object to camelCase.
|
|
36
|
-
*/
|
|
37
|
-
function camelCaseKeys(value: unknown): unknown {
|
|
38
|
-
if (value === null || value === undefined) return value;
|
|
39
|
-
if (Array.isArray(value)) return value.map(camelCaseKeys);
|
|
40
|
-
if (typeof value === "object") {
|
|
41
|
-
const result: Record<string, unknown> = {};
|
|
42
|
-
for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
|
|
43
|
-
result[toCamelCase(key)] = camelCaseKeys(val);
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
35
|
|
|
50
36
|
/**
|
|
51
37
|
* Parse a YAML document into a plain object.
|
|
@@ -227,7 +213,7 @@ export class GitLabParser implements TemplateParser {
|
|
|
227
213
|
resources.push({
|
|
228
214
|
logicalId: "defaults",
|
|
229
215
|
type: "GitLab::CI::Default",
|
|
230
|
-
properties:
|
|
216
|
+
properties: doc.default as Record<string, unknown>,
|
|
231
217
|
});
|
|
232
218
|
}
|
|
233
219
|
|
|
@@ -236,7 +222,7 @@ export class GitLabParser implements TemplateParser {
|
|
|
236
222
|
resources.push({
|
|
237
223
|
logicalId: "workflow",
|
|
238
224
|
type: "GitLab::CI::Workflow",
|
|
239
|
-
properties:
|
|
225
|
+
properties: doc.workflow as Record<string, unknown>,
|
|
240
226
|
});
|
|
241
227
|
}
|
|
242
228
|
|
|
@@ -256,9 +242,9 @@ export class GitLabParser implements TemplateParser {
|
|
|
256
242
|
obj.needs !== undefined
|
|
257
243
|
) {
|
|
258
244
|
resources.push({
|
|
259
|
-
logicalId:
|
|
245
|
+
logicalId: snakeToCamelCase(key.replace(/-/g, "_")),
|
|
260
246
|
type: "GitLab::CI::Job",
|
|
261
|
-
properties:
|
|
247
|
+
properties: obj as Record<string, unknown>,
|
|
262
248
|
metadata: {
|
|
263
249
|
originalName: key,
|
|
264
250
|
stage: typeof obj.stage === "string" ? obj.stage : undefined,
|
package/src/lsp/completions.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
|
|
2
3
|
import { LexiconIndex, lexiconCompletions, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
let cachedIndex: LexiconIndex | null = null;
|
|
5
7
|
|
package/src/lsp/hover.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
|
|
2
3
|
import { LexiconIndex, lexiconHover, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
let cachedIndex: LexiconIndex | null = null;
|
|
5
7
|
|
package/src/plugin.test.ts
CHANGED
|
@@ -159,9 +159,12 @@ describe("gitlabPlugin", () => {
|
|
|
159
159
|
test("returns skills", () => {
|
|
160
160
|
const skills = gitlabPlugin.skills!();
|
|
161
161
|
expect(skills).toHaveLength(1);
|
|
162
|
-
expect(skills[0].name).toBe("gitlab
|
|
162
|
+
expect(skills[0].name).toBe("chant-gitlab");
|
|
163
163
|
expect(skills[0].description).toBeDefined();
|
|
164
|
-
expect(skills[0].content).toContain("
|
|
164
|
+
expect(skills[0].content).toContain("skill: chant-gitlab");
|
|
165
|
+
expect(skills[0].content).toContain("user-invocable: true");
|
|
166
|
+
expect(skills[0].content).toContain("chant build");
|
|
167
|
+
expect(skills[0].content).toContain("chant lint");
|
|
165
168
|
expect(skills[0].triggers).toHaveLength(2);
|
|
166
169
|
expect(skills[0].examples).toHaveLength(1);
|
|
167
170
|
});
|
|
@@ -217,10 +220,6 @@ describe("gitlabPlugin", () => {
|
|
|
217
220
|
expect(typeof gitlabPlugin.coverage).toBe("function");
|
|
218
221
|
});
|
|
219
222
|
|
|
220
|
-
test("has rollback method", () => {
|
|
221
|
-
expect(typeof gitlabPlugin.rollback).toBe("function");
|
|
222
|
-
});
|
|
223
|
-
|
|
224
223
|
test("has docs method", () => {
|
|
225
224
|
expect(typeof gitlabPlugin.docs).toBe("function");
|
|
226
225
|
});
|
package/src/plugin.ts
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* for GitLab CI/CD pipelines.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { createRequire } from "module";
|
|
8
9
|
import type { LexiconPlugin, IntrinsicDef, SkillDefinition } from "@intentius/chant/lexicon";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
9
11
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
10
12
|
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
11
13
|
import { gitlabSerializer } from "./serializer";
|
|
@@ -169,31 +171,6 @@ export const test = new Job({
|
|
|
169
171
|
console.error(`Packaged ${stats.resources} entities, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
170
172
|
},
|
|
171
173
|
|
|
172
|
-
async rollback(options?: { restore?: string; verbose?: boolean }): Promise<void> {
|
|
173
|
-
const { listSnapshots, restoreSnapshot } = await import("./codegen/rollback");
|
|
174
|
-
const { join, dirname } = await import("path");
|
|
175
|
-
const { fileURLToPath } = await import("url");
|
|
176
|
-
|
|
177
|
-
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
178
|
-
const snapshotsDir = join(pkgDir, ".snapshots");
|
|
179
|
-
|
|
180
|
-
if (options?.restore) {
|
|
181
|
-
const generatedDir = join(pkgDir, "src", "generated");
|
|
182
|
-
restoreSnapshot(String(options.restore), generatedDir);
|
|
183
|
-
console.error(`Restored snapshot: ${options.restore}`);
|
|
184
|
-
} else {
|
|
185
|
-
const snapshots = listSnapshots(snapshotsDir);
|
|
186
|
-
if (snapshots.length === 0) {
|
|
187
|
-
console.error("No snapshots available.");
|
|
188
|
-
} else {
|
|
189
|
-
console.error(`Available snapshots (${snapshots.length}):`);
|
|
190
|
-
for (const s of snapshots) {
|
|
191
|
-
console.error(` ${s.timestamp} ${s.resourceCount} resources ${s.path}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
|
|
197
174
|
mcpTools() {
|
|
198
175
|
return [
|
|
199
176
|
{
|
|
@@ -283,45 +260,67 @@ export const deploy = new Job({
|
|
|
283
260
|
skills(): SkillDefinition[] {
|
|
284
261
|
return [
|
|
285
262
|
{
|
|
286
|
-
name: "gitlab
|
|
287
|
-
description: "GitLab CI/CD
|
|
263
|
+
name: "chant-gitlab",
|
|
264
|
+
description: "GitLab CI/CD pipeline management — workflows, patterns, and troubleshooting",
|
|
288
265
|
content: `---
|
|
289
|
-
|
|
290
|
-
description: GitLab CI
|
|
266
|
+
skill: chant-gitlab
|
|
267
|
+
description: Build, validate, and deploy GitLab CI pipelines from a chant project
|
|
268
|
+
user-invocable: true
|
|
291
269
|
---
|
|
292
270
|
|
|
293
|
-
# GitLab CI
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
271
|
+
# Deploying GitLab CI Pipelines from Chant
|
|
272
|
+
|
|
273
|
+
This project defines GitLab CI jobs as TypeScript in \`src/\`. Use these steps to build, validate, and deploy.
|
|
274
|
+
|
|
275
|
+
## Build the pipeline
|
|
276
|
+
|
|
277
|
+
\`\`\`bash
|
|
278
|
+
chant build src/ --output .gitlab-ci.yml
|
|
279
|
+
\`\`\`
|
|
280
|
+
|
|
281
|
+
## Validate before pushing
|
|
282
|
+
|
|
283
|
+
\`\`\`bash
|
|
284
|
+
chant lint src/
|
|
285
|
+
\`\`\`
|
|
286
|
+
|
|
287
|
+
For API-level validation against your GitLab instance:
|
|
288
|
+
\`\`\`bash
|
|
289
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
290
|
+
"https://gitlab.com/api/v4/ci/lint" \\
|
|
291
|
+
--data "{\\"content\\": \\"$(cat .gitlab-ci.yml)\\"}"
|
|
292
|
+
\`\`\`
|
|
293
|
+
|
|
294
|
+
## Deploy
|
|
295
|
+
|
|
296
|
+
Commit and push the generated \`.gitlab-ci.yml\` — GitLab runs the pipeline automatically:
|
|
297
|
+
|
|
298
|
+
\`\`\`bash
|
|
299
|
+
chant build src/ --output .gitlab-ci.yml
|
|
300
|
+
git add .gitlab-ci.yml
|
|
301
|
+
git commit -m "Update pipeline"
|
|
302
|
+
git push
|
|
303
|
+
\`\`\`
|
|
304
|
+
|
|
305
|
+
## Check pipeline status
|
|
306
|
+
|
|
307
|
+
- GitLab UI: project → CI/CD → Pipelines
|
|
308
|
+
- API: \`curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?per_page=5"\`
|
|
309
|
+
|
|
310
|
+
## Retry a failed job
|
|
311
|
+
|
|
312
|
+
- GitLab UI: click Retry on the failed job
|
|
313
|
+
- API: \`curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/retry"\`
|
|
314
|
+
|
|
315
|
+
## Cancel a running pipeline
|
|
316
|
+
|
|
317
|
+
- API: \`curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/cancel"\`
|
|
318
|
+
|
|
319
|
+
## Troubleshooting
|
|
320
|
+
|
|
321
|
+
- Check job logs in GitLab UI: project → CI/CD → Jobs → click the job
|
|
322
|
+
- \`chant lint src/\` catches: missing scripts (WGL002), deprecated only/except (WGL001), missing stages (WGL003), artifacts without expiry (WGL004)
|
|
323
|
+
- Post-synth checks (WGL010, WGL011) run during build
|
|
325
324
|
`,
|
|
326
325
|
triggers: [
|
|
327
326
|
{ type: "file-pattern", value: "**/*.gitlab.ts" },
|
package/src/serializer.test.ts
CHANGED
|
@@ -121,12 +121,12 @@ describe("gitlabSerializer.serialize", () => {
|
|
|
121
121
|
expect(output).toContain("my-test-job:");
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
test("
|
|
124
|
+
test("passes through spec-native snake_case property keys", () => {
|
|
125
125
|
const entities = new Map<string, Declarable>();
|
|
126
126
|
entities.set("job", new MockJob({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
before_script: ["echo hello"],
|
|
128
|
+
after_script: ["echo done"],
|
|
129
|
+
expire_in: "1 week",
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
132
|
const output = gitlabSerializer.serialize(entities);
|
|
@@ -270,7 +270,7 @@ describe("nested objects and arrays", () => {
|
|
|
270
270
|
|
|
271
271
|
const output = gitlabSerializer.serialize(entities);
|
|
272
272
|
expect(output).toContain("variables:");
|
|
273
|
-
expect(output).toContain("
|
|
273
|
+
expect(output).toContain("NODE_ENV: production");
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
test("serializes arrays of strings", () => {
|
|
@@ -289,7 +289,7 @@ describe("nested objects and arrays", () => {
|
|
|
289
289
|
const entities = new Map<string, Declarable>();
|
|
290
290
|
entities.set("job", new MockJob({
|
|
291
291
|
interruptible: true,
|
|
292
|
-
|
|
292
|
+
allow_failure: false,
|
|
293
293
|
}));
|
|
294
294
|
|
|
295
295
|
const output = gitlabSerializer.serialize(entities);
|
package/src/serializer.ts
CHANGED
|
@@ -14,13 +14,6 @@ import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
|
14
14
|
import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
|
|
15
15
|
import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* Convert camelCase or PascalCase to snake_case.
|
|
19
|
-
*/
|
|
20
|
-
function toSnakeCase(name: string): string {
|
|
21
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
17
|
/**
|
|
25
18
|
* GitLab CI visitor for the generic serializer walker.
|
|
26
19
|
*/
|
|
@@ -36,12 +29,11 @@ function gitlabVisitor(entityNames: Map<Declarable, string>): SerializerVisitor
|
|
|
36
29
|
const result: Record<string, unknown> = {};
|
|
37
30
|
for (const [key, value] of Object.entries(props)) {
|
|
38
31
|
if (value !== undefined) {
|
|
39
|
-
result[
|
|
32
|
+
result[key] = walk(value);
|
|
40
33
|
}
|
|
41
34
|
}
|
|
42
35
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
43
36
|
},
|
|
44
|
-
transformKey: toSnakeCase,
|
|
45
37
|
};
|
|
46
38
|
}
|
|
47
39
|
|
package/dist/skills/gitlab-ci.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: gitlab-ci
|
|
3
|
-
description: GitLab CI/CD best practices and common patterns
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# GitLab CI/CD with Chant
|
|
7
|
-
|
|
8
|
-
## Common Entity Types
|
|
9
|
-
|
|
10
|
-
- `Job` — Pipeline job definition
|
|
11
|
-
- `Default` — Default settings inherited by all jobs
|
|
12
|
-
- `Workflow` — Pipeline-level configuration
|
|
13
|
-
- `Artifacts` — Job artifact configuration
|
|
14
|
-
- `Cache` — Cache configuration
|
|
15
|
-
- `Image` — Docker image for a job
|
|
16
|
-
- `Rule` — Conditional execution rule
|
|
17
|
-
- `Environment` — Deployment environment
|
|
18
|
-
- `Trigger` — Trigger downstream pipeline
|
|
19
|
-
- `Include` — Include external CI configuration
|
|
20
|
-
|
|
21
|
-
## Predefined Variables
|
|
22
|
-
|
|
23
|
-
- `CI.CommitBranch` — Current branch name
|
|
24
|
-
- `CI.CommitSha` — Current commit SHA
|
|
25
|
-
- `CI.PipelineSource` — What triggered the pipeline
|
|
26
|
-
- `CI.ProjectPath` — Project path (group/project)
|
|
27
|
-
- `CI.Registry` — Container registry URL
|
|
28
|
-
- `CI.MergeRequestIid` — MR internal ID
|
|
29
|
-
|
|
30
|
-
## Best Practices
|
|
31
|
-
|
|
32
|
-
1. **Use stages** — Organize jobs into logical stages (build, test, deploy)
|
|
33
|
-
2. **Cache dependencies** — Cache node_modules, pip packages, etc.
|
|
34
|
-
3. **Use rules** — Prefer `rules:` over `only:/except:` for conditional execution
|
|
35
|
-
4. **Minimize artifacts** — Only preserve files needed by later stages
|
|
36
|
-
5. **Use includes** — Share common configuration across projects
|
|
37
|
-
6. **Set timeouts** — Prevent stuck jobs from blocking pipelines
|
package/src/codegen/rollback.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rollback and snapshot management for GitLab CI lexicon.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the core rollback module with GitLab-specific artifact names.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export type { ArtifactSnapshot, SnapshotInfo } from "@intentius/chant/codegen/rollback";
|
|
8
|
-
export {
|
|
9
|
-
snapshotArtifacts,
|
|
10
|
-
saveSnapshot,
|
|
11
|
-
restoreSnapshot,
|
|
12
|
-
listSnapshots,
|
|
13
|
-
} from "@intentius/chant/codegen/rollback";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* GitLab-specific artifact filenames to snapshot.
|
|
17
|
-
*/
|
|
18
|
-
export const GITLAB_ARTIFACT_NAMES = ["lexicon-gitlab.json", "index.d.ts", "index.ts"];
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Snapshot GitLab lexicon artifacts.
|
|
22
|
-
*/
|
|
23
|
-
export function snapshotGitLabArtifacts(generatedDir: string) {
|
|
24
|
-
const { snapshotArtifacts } = require("@intentius/chant/codegen/rollback");
|
|
25
|
-
return snapshotArtifacts(generatedDir, GITLAB_ARTIFACT_NAMES);
|
|
26
|
-
}
|