@intentius/chant-lexicon-gitlab 0.124.30 → 0.124.32

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.
Files changed (133) hide show
  1. package/dist/codegen/docs-cli.js +2 -0
  2. package/dist/codegen/docs.js +157 -0
  3. package/dist/codegen/fetch.js +93 -0
  4. package/dist/codegen/generate-cli.js +30 -0
  5. package/dist/codegen/generate-lexicon.js +36 -0
  6. package/dist/codegen/generate-typescript.js +105 -0
  7. package/dist/codegen/generate.js +125 -0
  8. package/dist/codegen/naming.js +48 -0
  9. package/dist/codegen/package.js +41 -0
  10. package/dist/codegen/parse.js +539 -0
  11. package/dist/codegen/patches.js +84 -0
  12. package/dist/codegen/typecheck.js +4 -0
  13. package/dist/components/ci-jobs.js +16 -0
  14. package/dist/components/generate-op-pipeline.js +288 -0
  15. package/dist/components/generate-op-waves-pipeline.js +125 -0
  16. package/dist/components/generate-pipeline.d.ts.map +1 -1
  17. package/dist/components/generate-pipeline.js +369 -0
  18. package/dist/composites/catalog.js +418 -0
  19. package/dist/composites/docker-build.js +38 -0
  20. package/dist/composites/index.js +5 -0
  21. package/dist/composites/mr-plan-report.js +42 -0
  22. package/dist/composites/node-pipeline.js +67 -0
  23. package/dist/composites/python-pipeline.js +45 -0
  24. package/dist/composites/review-app.js +32 -0
  25. package/dist/coverage.js +35 -0
  26. package/dist/generated/index.js +25 -0
  27. package/dist/generated/runtime.js +4 -0
  28. package/dist/graph-edges.js +202 -0
  29. package/dist/import/generator.js +196 -0
  30. package/dist/import/parser.js +111 -0
  31. package/dist/index.js +26 -0
  32. package/dist/integrity.json +3 -3
  33. package/dist/intrinsics.js +33 -0
  34. package/dist/lint/audit-catalog.js +53 -0
  35. package/dist/lint/audit-lineage.js +64 -0
  36. package/dist/lint/post-synth/index.js +84 -0
  37. package/dist/lint/post-synth/wgl010.js +34 -0
  38. package/dist/lint/post-synth/wgl011.js +48 -0
  39. package/dist/lint/post-synth/wgl012.d.ts.map +1 -1
  40. package/dist/lint/post-synth/wgl012.js +68 -0
  41. package/dist/lint/post-synth/wgl013.js +57 -0
  42. package/dist/lint/post-synth/wgl014.js +43 -0
  43. package/dist/lint/post-synth/wgl015.js +70 -0
  44. package/dist/lint/post-synth/wgl016.js +70 -0
  45. package/dist/lint/post-synth/wgl017.js +38 -0
  46. package/dist/lint/post-synth/wgl018.js +34 -0
  47. package/dist/lint/post-synth/wgl019.js +43 -0
  48. package/dist/lint/post-synth/wgl020.js +47 -0
  49. package/dist/lint/post-synth/wgl021.js +52 -0
  50. package/dist/lint/post-synth/wgl022.js +38 -0
  51. package/dist/lint/post-synth/wgl023.js +44 -0
  52. package/dist/lint/post-synth/wgl024.js +41 -0
  53. package/dist/lint/post-synth/wgl025.js +42 -0
  54. package/dist/lint/post-synth/wgl026.js +58 -0
  55. package/dist/lint/post-synth/wgl027.js +49 -0
  56. package/dist/lint/post-synth/wgl028.js +58 -0
  57. package/dist/lint/post-synth/wgl029.js +55 -0
  58. package/dist/lint/post-synth/wgl030.js +43 -0
  59. package/dist/lint/post-synth/wgl031.js +33 -0
  60. package/dist/lint/post-synth/wgl032.js +64 -0
  61. package/dist/lint/post-synth/wgl033.js +32 -0
  62. package/dist/lint/post-synth/wgl034.js +37 -0
  63. package/dist/lint/post-synth/wgl035.js +40 -0
  64. package/dist/lint/post-synth/wgl036.js +38 -0
  65. package/dist/lint/post-synth/wgl037.js +41 -0
  66. package/dist/lint/post-synth/wgl038.js +43 -0
  67. package/dist/lint/post-synth/wgl039.js +34 -0
  68. package/dist/lint/post-synth/wgl040.js +35 -0
  69. package/dist/lint/post-synth/wgl041.js +53 -0
  70. package/dist/lint/post-synth/wgl042.js +41 -0
  71. package/dist/lint/post-synth/wgl043.js +39 -0
  72. package/dist/lint/post-synth/wgl044.js +35 -0
  73. package/dist/lint/post-synth/wgl045.js +54 -0
  74. package/dist/lint/post-synth/wgl046.js +40 -0
  75. package/dist/lint/post-synth/wgl047.js +34 -0
  76. package/dist/lint/post-synth/wgl048.js +32 -0
  77. package/dist/lint/post-synth/wgl049.js +53 -0
  78. package/dist/lint/post-synth/wgl050.js +42 -0
  79. package/dist/lint/post-synth/yaml-helpers.js +432 -0
  80. package/dist/lint/rules/artifact-no-expiry.js +53 -0
  81. package/dist/lint/rules/data/known-include-sources.js +44 -0
  82. package/dist/lint/rules/data/untrusted-variables.js +30 -0
  83. package/dist/lint/rules/deprecated-only-except.js +46 -0
  84. package/dist/lint/rules/import-source.js +61 -0
  85. package/dist/lint/rules/index.js +7 -0
  86. package/dist/lint/rules/missing-script.js +67 -0
  87. package/dist/lint/rules/missing-stage.js +60 -0
  88. package/dist/lsp/completions.d.ts.map +1 -1
  89. package/dist/lsp/completions.js +18 -0
  90. package/dist/lsp/hover.d.ts.map +1 -1
  91. package/dist/lsp/hover.js +33 -0
  92. package/dist/manifest.json +1 -1
  93. package/dist/mcp/context-tools.js +262 -0
  94. package/dist/migrate/from-github/actions/index.js +17 -0
  95. package/dist/migrate/from-github/actions/registry.js +61 -0
  96. package/dist/migrate/from-github/actions/tier-1.js +298 -0
  97. package/dist/migrate/from-github/actions/tier-2.js +241 -0
  98. package/dist/migrate/from-github/actions/tier-3.js +96 -0
  99. package/dist/migrate/from-github/composites/patterns.js +151 -0
  100. package/dist/migrate/from-github/composites/rewriter.js +22 -0
  101. package/dist/migrate/from-github/diagnostics.js +53 -0
  102. package/dist/migrate/from-github/emit-yaml.js +121 -0
  103. package/dist/migrate/from-github/expressions.js +275 -0
  104. package/dist/migrate/from-github/index.js +96 -0
  105. package/dist/migrate/from-github/provenance.js +35 -0
  106. package/dist/migrate/from-github/rules.js +74 -0
  107. package/dist/migrate/from-github/security.js +189 -0
  108. package/dist/migrate/from-github/stages.js +156 -0
  109. package/dist/migrate/from-github/transformer.js +633 -0
  110. package/dist/op/activities/gitlab.js +38 -0
  111. package/dist/op/activities/index.js +10 -0
  112. package/dist/op/builders.js +26 -0
  113. package/dist/package-cli.js +21 -0
  114. package/dist/plugin.js +536 -0
  115. package/dist/rules/wgl012.ts +2 -2
  116. package/dist/serializer.js +197 -0
  117. package/dist/testdata/create-fixture.js +40 -0
  118. package/dist/testdata/load-fixtures.js +20 -0
  119. package/dist/validate-cli.js +16 -0
  120. package/dist/validate.js +29 -0
  121. package/dist/variables.js +30 -0
  122. package/package.json +8 -8
  123. package/src/codegen/fetch.ts +2 -2
  124. package/src/codegen/package.ts +2 -2
  125. package/src/components/__fixtures__/pr-loop-build.gitlab.golden.yml +3 -3
  126. package/src/components/__fixtures__/pr-loop-waves.gitlab.golden.yml +101 -0
  127. package/src/components/__fixtures__/pr-loop.gitlab.golden.yml +3 -3
  128. package/src/components/generate-pipeline.ts +60 -27
  129. package/src/components/generate-pr-pipeline.test.ts +14 -9
  130. package/src/components/generate-pr-waves-pipeline.test.ts +88 -0
  131. package/src/lint/post-synth/wgl012.ts +2 -2
  132. package/src/lsp/completions.ts +2 -1
  133. package/src/lsp/hover.ts +2 -1
@@ -0,0 +1,2 @@
1
+ import { generateDocs } from "./docs.js";
2
+ await generateDocs({ verbose: true });
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Documentation generation for GitLab CI lexicon.
3
+ *
4
+ * Generates Starlight MDX pages for CI entities using the core docs pipeline.
5
+ */
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
9
+ /**
10
+ * Extract service name from GitLab CI type: "GitLab::CI::Job" → "CI"
11
+ */
12
+ function serviceFromType(resourceType) {
13
+ const parts = resourceType.split("::");
14
+ return parts.length >= 2 ? parts[1] : "CI";
15
+ }
16
+ const overview = `The **GitLab CI/CD** lexicon provides typed constructors for GitLab CI pipeline
17
+ configuration. It covers jobs, workflow settings, artifacts, caching, images,
18
+ rules, environments, triggers, and more.
19
+
20
+ Install it with:
21
+
22
+ \`\`\`bash
23
+ npm install --save-dev @intentius/chant-lexicon-gitlab
24
+ \`\`\`
25
+
26
+ ## Quick Start
27
+
28
+ {{file:docs-snippets/src/quickstart.ts}}
29
+
30
+ The lexicon provides **4 resources** (Job, Workflow, Default, Pipeline), **16 property types** (Image, Cache, Artifacts, Rule, Environment, Trigger, Need, Service, and more), the \`CI\` pseudo-parameter object for predefined variables, and the \`reference()\` intrinsic for YAML \`!reference\` tags. It also ships **4 lint rules** + **39 post-synth checks** (including a CI/CD supply-chain security pass, WGL029–048) and a [\`chant migrate\`](./migration) source for translating GitHub Actions workflows.
31
+ `;
32
+ const outputFormat = `The GitLab lexicon serializes resources into **\`.gitlab-ci.yml\` YAML**. Keys are
33
+ converted to \`snake_case\` and jobs use kebab-case names. Stages are collected
34
+ from the job definitions, after any a \`Pipeline\` lists.
35
+
36
+ ## Building
37
+
38
+ Run \`chant build\` to produce a \`.gitlab-ci.yml\` from your declarations:
39
+
40
+ \`\`\`bash
41
+ chant build
42
+ # Writes dist/.gitlab-ci.yml
43
+ \`\`\`
44
+
45
+ The generated file includes:
46
+
47
+ - a header comment, \`include:\` and \`variables:\`, if a \`Pipeline\` resource is exported
48
+ - \`stages:\` list: a \`Pipeline\`'s \`stages\` in its order, then every other stage a job's \`stage\` names, in first use
49
+ - \`default:\` section — if a \`Default\` resource is exported
50
+ - \`workflow:\` section — if a \`Workflow\` resource is exported
51
+ - Job definitions with \`snake_case\` keys and \`kebab-case\` job names
52
+
53
+ ## Key conversions
54
+
55
+ | Chant (TypeScript) | YAML output | Rule |
56
+ |--------------------|-------------|------|
57
+ | \`export const buildApp = new Job({...})\` | \`build-app:\` | Export name → kebab-case job key |
58
+ | \`expire_in: "1 week"\` | \`expire_in: 1 week\` | Property names use spec-native snake_case |
59
+ | \`new Image({ name: "node:20" })\` | \`image: node:20\` | Single-property objects are collapsed |
60
+
61
+ ## Top-level keys and comments
62
+
63
+ A \`Pipeline\` resource holds the top-level keys that are not a job, \`default\` or \`workflow\`: \`include\`, \`stages\` and \`variables\`. Its \`stages\` can name a stage no declared job uses, such as one an included file's jobs run in, and set the order; a stage a job names that the list leaves out is still added after it, so the file stays valid. \`variables\` holds pipeline-wide variables, which \`Default\` cannot, since GitLab's \`default:\` has none.
64
+
65
+ \`Pipeline\` and \`Job\` also take a \`comment\`, written as \`#\` lines: the pipeline's at the top of the file, a job's above its key.
66
+
67
+ \`\`\`typescript
68
+ export const pipeline = new Pipeline({
69
+ comment: "Protect the writer's variables and scope them to their environment.",
70
+ include: [{ local: ".gitlab/watch.yml" }],
71
+ stages: ["review", "apply", "ops"],
72
+ variables: { GIT_DEPTH: "0" },
73
+ });
74
+
75
+ export const lint = new Job({ comment: "Blocks the merge.", stage: "review", image: "node:22", script: ["npm run lint"] });
76
+ \`\`\`
77
+
78
+ \`\`\`yaml
79
+ # Protect the writer's variables and scope them to their environment.
80
+
81
+ include:
82
+ - local: .gitlab/watch.yml
83
+
84
+ stages:
85
+ - review
86
+ - apply
87
+ - ops
88
+
89
+ variables:
90
+ GIT_DEPTH: '0'
91
+
92
+ # Blocks the merge.
93
+ lint:
94
+ stage: review
95
+ image: node:22
96
+ script:
97
+ - npm run lint
98
+ \`\`\`
99
+
100
+ ## Validating locally
101
+
102
+ The output is standard GitLab CI YAML. Validate with the GitLab CI Lint API or locally:
103
+
104
+ \`\`\`bash
105
+ # Using the GitLab API
106
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
107
+ --data-urlencode "content=$(cat dist/.gitlab-ci.yml)" \\
108
+ "https://gitlab.com/api/v4/ci/lint"
109
+
110
+ # Using the glab CLI
111
+ glab ci lint dist/.gitlab-ci.yml
112
+ \`\`\`
113
+
114
+ ## Compatibility
115
+
116
+ The output is compatible with:
117
+ - GitLab CI/CD (any recent GitLab version)
118
+ - GitLab CI Lint API
119
+ - \`glab\` CLI tool
120
+ - Any tool that processes \`.gitlab-ci.yml\` files`;
121
+ /**
122
+ * Generate documentation for the GitLab CI lexicon.
123
+ */
124
+ export async function generateDocs(opts) {
125
+ const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
126
+ const config = {
127
+ name: "gitlab",
128
+ displayName: "GitLab CI/CD",
129
+ description: "Typed constructors for GitLab CI/CD pipeline configuration",
130
+ distDir: join(pkgDir, "dist"),
131
+ outDir: join(pkgDir, "docs"),
132
+ overview,
133
+ outputFormat,
134
+ serviceFromType,
135
+ // "intrinsics" is no longer suppressed (chant #1067) — the reference
136
+ // table at that slug is now generated from the plugin's own
137
+ // `intrinsics()` registration (docsPipeline's generateIntrinsics, same
138
+ // mechanism azure/helm already use), so its "Folds?" column can never
139
+ // drift from the registration the way #1062's foldability matrix
140
+ // depends on. The hand-written `reference()` usage guide lives in a
141
+ // separate "intrinsics-guide" page under docs/pages/ — content unchanged,
142
+ // just no longer sharing a slug with generated data.
143
+ examplesDir: join(pkgDir, "examples"),
144
+ extraSections: [
145
+ {
146
+ title: "Migrating from GitHub Actions",
147
+ content: `\`chant migrate\` translates GitHub Actions workflows into GitLab CI/CD pipelines or typed chant source. See [Migration](./migration) for the full CLI surface, supported translations, and limitations.`,
148
+ },
149
+ ],
150
+ basePath: "/chant/lexicons/gitlab/",
151
+ };
152
+ const result = await docsPipeline(config);
153
+ writeDocsSite(config, result);
154
+ if (opts?.verbose) {
155
+ console.error(`Generated docs: ${result.stats.resources} resources, ${result.stats.properties} properties, ${result.stats.services} services, ${result.stats.rules} rules`);
156
+ }
157
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * GitLab CI schema fetching — downloads the CI JSON Schema
3
+ * and caches it locally.
4
+ */
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ import { readFileSync, writeFileSync } from "fs";
8
+ import { moduleSourceDir } from "@intentius/chant/module-source";
9
+ import { fetchWithCache, clearCacheFile } from "@intentius/chant/codegen/fetch";
10
+ /**
11
+ * Pinned schema version — a GitLab release tag that produces a known-good
12
+ * schema. Override at runtime with `--schema-version <tag>` in generate-cli
13
+ * or by setting `schemaVersion` in the generate options.
14
+ *
15
+ * Using a tag (e.g. "v17.8.1-ee") rather than `master` ensures reproducible
16
+ * codegen across environments and CI runs.
17
+ */
18
+ export const GITLAB_SCHEMA_VERSION = "v17.8.1-ee";
19
+ /**
20
+ * Build the schema URL for a given version ref.
21
+ */
22
+ function schemaUrl(version) {
23
+ return `https://gitlab.com/gitlab-org/gitlab/-/raw/${version}/app/assets/javascripts/editor/schema/ci.json`;
24
+ }
25
+ /**
26
+ * Get the cache file path for the CI schema.
27
+ */
28
+ export function getCachePath() {
29
+ return join(homedir(), ".chant", "gitlab-ci-schema.json");
30
+ }
31
+ /**
32
+ * Path to the vendored CI schema committed in this lexicon. Reading it offline
33
+ * is the default so `generate` never depends on gitlab.com — which aggressively
34
+ * 429-rate-limits the raw endpoint and chronically flaked CI (#574). The
35
+ * `GITLAB_SCHEMA_VERSION` pin already makes the fetch deterministic, so a
36
+ * committed copy is equivalent and network-free.
37
+ */
38
+ export function getVendoredSchemaPath() {
39
+ return join(moduleSourceDir(import.meta.url), "vendored", "gitlab-ci-schema.json");
40
+ }
41
+ /**
42
+ * Fetch the GitLab CI JSON Schema, returning the raw JSON buffer.
43
+ *
44
+ * Default (pinned version, no force): read the vendored, committed schema —
45
+ * offline, deterministic, no gitlab.com 429s (#574). Pass `force` or a custom
46
+ * `version` to fetch live — used to verify against upstream or to refresh the
47
+ * vendored copy when bumping the pin (see {@link refreshVendoredSchema}).
48
+ *
49
+ * @param force Bypass the vendored copy and download fresh.
50
+ * @param version GitLab ref (tag, branch, or SHA) to fetch from.
51
+ * Defaults to {@link GITLAB_SCHEMA_VERSION}.
52
+ */
53
+ export async function fetchCISchema(force, version) {
54
+ const ref = version ?? GITLAB_SCHEMA_VERSION;
55
+ if (!force && ref === GITLAB_SCHEMA_VERSION) {
56
+ return readFileSync(getVendoredSchemaPath());
57
+ }
58
+ return fetchWithCache({
59
+ url: schemaUrl(ref),
60
+ cacheFile: getCachePath(),
61
+ }, force);
62
+ }
63
+ /**
64
+ * Refresh the vendored schema from upstream. Call this when bumping
65
+ * `GITLAB_SCHEMA_VERSION` so the committed copy tracks the new pin — the
66
+ * lexicon-upgrade pipeline (#523) runs it as part of a gitlab pin bump.
67
+ * Fetches the given version live and overwrites the vendored file.
68
+ */
69
+ export async function refreshVendoredSchema(version) {
70
+ const ref = version ?? GITLAB_SCHEMA_VERSION;
71
+ const data = await fetchWithCache({ url: schemaUrl(ref), cacheFile: getCachePath() }, true);
72
+ writeFileSync(getVendoredSchemaPath(), data);
73
+ }
74
+ /**
75
+ * Fetch the CI schema and return it as a Map<typeName, Buffer>
76
+ * compatible with the generatePipeline fetchSchemas callback.
77
+ *
78
+ * The CI schema is a single document, so we return a single entry
79
+ * keyed by "GitLab::CI::Pipeline" — the parse step will split it
80
+ * into multiple entities.
81
+ */
82
+ export async function fetchSchemas(force, version) {
83
+ const data = await fetchCISchema(force, version);
84
+ const schemas = new Map();
85
+ schemas.set("GitLab::CI::Pipeline", data);
86
+ return schemas;
87
+ }
88
+ /**
89
+ * Clear the cached schema file.
90
+ */
91
+ export function clearCache() {
92
+ clearCacheFile(getCachePath());
93
+ }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * CLI entry point for GitLab CI lexicon generation.
4
+ */
5
+ import { dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { generate, writeGeneratedFiles } from "./generate.js";
8
+ import { GITLAB_SCHEMA_VERSION } from "./fetch.js";
9
+ const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
10
+ async function main() {
11
+ const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
12
+ const force = process.argv.includes("--force") || process.argv.includes("-f");
13
+ // Parse --schema-version <ref>
14
+ const versionIdx = process.argv.indexOf("--schema-version");
15
+ const schemaVersion = versionIdx !== -1 ? process.argv[versionIdx + 1] : undefined;
16
+ console.error(`Generating GitLab CI lexicon (schema: ${schemaVersion ?? GITLAB_SCHEMA_VERSION})...`);
17
+ const result = await generate({ verbose, force, schemaVersion });
18
+ writeGeneratedFiles(result, pkgDir);
19
+ console.error(`Generated ${result.resources} entities, ${result.properties} property types, ${result.enums} enums`);
20
+ if (result.warnings.length > 0) {
21
+ console.error(`${result.warnings.length} warnings:`);
22
+ for (const w of result.warnings) {
23
+ console.error(` ${w.file}: ${w.error}`);
24
+ }
25
+ }
26
+ }
27
+ main().catch((err) => {
28
+ console.error("Generation failed:", err);
29
+ process.exit(1);
30
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Lexicon JSON generator — produces lexicon-gitlab.json with metadata
3
+ * for all GitLab CI entities.
4
+ */
5
+ import { gitlabShortName } from "./parse.js";
6
+ import { buildRegistry, serializeRegistry, } from "@intentius/chant/codegen/generate-registry";
7
+ /**
8
+ * Generate the lexicon-gitlab.json content.
9
+ */
10
+ export function generateLexiconJSON(results, naming) {
11
+ const registryResources = results.map((r) => ({
12
+ typeName: r.resource.typeName,
13
+ attributes: r.resource.attributes,
14
+ properties: r.resource.properties,
15
+ propertyTypes: r.propertyTypes.map((pt) => ({ name: pt.name, specType: pt.defType })),
16
+ }));
17
+ const entries = buildRegistry(registryResources, naming, {
18
+ shortName: gitlabShortName,
19
+ buildEntry: (resource, _tsName, _attrs, propConstraints) => {
20
+ const r = results.find((res) => res.resource.typeName === resource.typeName);
21
+ return {
22
+ resourceType: resource.typeName,
23
+ kind: (r?.isProperty ? "property" : "resource"),
24
+ lexicon: "gitlab",
25
+ ...(r?.resource.deprecatedProperties?.length && { deprecatedProperties: r.resource.deprecatedProperties }),
26
+ ...(propConstraints && Object.keys(propConstraints).length > 0 && { constraints: propConstraints }),
27
+ };
28
+ },
29
+ buildPropertyEntry: (resourceType, propertyType) => ({
30
+ resourceType: `${resourceType}.${propertyType}`,
31
+ kind: "property",
32
+ lexicon: "gitlab",
33
+ }),
34
+ });
35
+ return serializeRegistry(entries);
36
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * TypeScript declaration file generator — produces index.d.ts with all
3
+ * GitLab CI entity classes.
4
+ */
5
+ import { gitlabShortName } from "./parse.js";
6
+ import { propertyTypeName, extractDefName } from "./naming.js";
7
+ import { writeResourceClass, writeEnumType, } from "@intentius/chant/codegen/generate-typescript";
8
+ /**
9
+ * Generate the complete .d.ts file content.
10
+ */
11
+ export function generateTypeScriptDeclarations(results, naming) {
12
+ const lines = [];
13
+ lines.push("// Code generated by chant gitlab generate. DO NOT EDIT.");
14
+ const resources = [];
15
+ const enums = [];
16
+ for (const r of results) {
17
+ const typeName = r.resource.typeName;
18
+ const tsName = naming.resolve(typeName);
19
+ if (!tsName)
20
+ continue;
21
+ const shortName = gitlabShortName(typeName);
22
+ const remap = new Map();
23
+ for (const pt of r.propertyTypes) {
24
+ const defName = extractDefName(pt.name, shortName);
25
+ remap.set(pt.name, propertyTypeName(tsName, defName));
26
+ }
27
+ for (const e of r.enums) {
28
+ const defName = extractDefName(e.name, shortName);
29
+ remap.set(e.name, propertyTypeName(tsName, defName));
30
+ }
31
+ const dtsProps = r.resource.properties.map((p) => ({
32
+ name: p.name,
33
+ type: p.tsType,
34
+ required: p.required,
35
+ description: p.description,
36
+ }));
37
+ const dtsAttrs = r.resource.attributes.map((a) => ({
38
+ name: a.name,
39
+ type: a.tsType,
40
+ }));
41
+ resources.push({ tsName, properties: dtsProps, attributes: dtsAttrs, remap });
42
+ for (const alias of naming.aliases(typeName)) {
43
+ resources.push({ tsName: alias, properties: dtsProps, attributes: dtsAttrs, remap });
44
+ }
45
+ for (const e of r.enums) {
46
+ const defName = extractDefName(e.name, shortName);
47
+ const eName = propertyTypeName(tsName, defName);
48
+ enums.push({ tsName: eName, values: e.values });
49
+ }
50
+ }
51
+ // Sort alphabetically
52
+ resources.sort((a, b) => a.tsName.localeCompare(b.tsName));
53
+ enums.sort((a, b) => a.tsName.localeCompare(b.tsName));
54
+ // Resource classes (all CI entities use writeResourceClass since they're
55
+ // all top-level — the "property" distinction is only in the registry)
56
+ lines.push("");
57
+ lines.push("// --- CI Entity classes ---");
58
+ for (const re of resources) {
59
+ writeResourceClass(lines, re.tsName, re.properties, re.attributes, re.remap);
60
+ }
61
+ // Enum types
62
+ if (enums.length > 0) {
63
+ lines.push("");
64
+ lines.push("// --- Enum types ---");
65
+ for (const ee of enums) {
66
+ writeEnumType(lines, ee.tsName, ee.values);
67
+ }
68
+ }
69
+ // Static content
70
+ lines.push("");
71
+ lines.push(staticTypeScript());
72
+ lines.push("");
73
+ return lines.join("\n");
74
+ }
75
+ function staticTypeScript() {
76
+ const lines = [];
77
+ lines.push("// --- CI/CD Variables ---");
78
+ lines.push("");
79
+ lines.push("export declare const CI: {");
80
+ const ciVars = [
81
+ "CommitBranch",
82
+ "CommitRef",
83
+ "CommitSha",
84
+ "CommitTag",
85
+ "DefaultBranch",
86
+ "Environment",
87
+ "JobId",
88
+ "JobName",
89
+ "JobStage",
90
+ "MergeRequestIid",
91
+ "PipelineId",
92
+ "PipelineSource",
93
+ "ProjectDir",
94
+ "ProjectId",
95
+ "ProjectName",
96
+ "ProjectPath",
97
+ "Registry",
98
+ "RegistryImage",
99
+ ];
100
+ for (const v of ciVars) {
101
+ lines.push(` readonly ${v}: string;`);
102
+ }
103
+ lines.push("};");
104
+ return lines.join("\n");
105
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * GitLab CI generation pipeline — uses core generatePipeline
3
+ * with GitLab-specific fetch, parse, naming, and generation callbacks.
4
+ */
5
+ import { generatePipeline, writeGeneratedArtifacts, } from "@intentius/chant/codegen/generate";
6
+ import { fetchSchemas } from "./fetch.js";
7
+ import { parseCISchema } from "./parse.js";
8
+ import { NamingStrategy, propertyTypeName, extractDefName } from "./naming.js";
9
+ import { gitlabShortName } from "./parse.js";
10
+ import { generateLexiconJSON } from "./generate-lexicon.js";
11
+ import { generateTypeScriptDeclarations } from "./generate-typescript.js";
12
+ import { generateRuntimeIndex as coreGenerateRuntimeIndex, } from "@intentius/chant/codegen/generate-runtime-index";
13
+ /**
14
+ * Run the full GitLab generation pipeline.
15
+ */
16
+ export async function generate(opts = {}) {
17
+ // Pipeline state captured in closure — no module-level mutation
18
+ let pendingResults = [];
19
+ const config = {
20
+ fetchSchemas: async (fetchOpts) => {
21
+ return fetchSchemas(fetchOpts.force, opts.schemaVersion);
22
+ },
23
+ parseSchema: (_typeName, data) => {
24
+ // The CI schema is a single document — parseCISchema returns multiple results.
25
+ // The pipeline calls this once per schema entry. We return the first result
26
+ // and use augmentResults to inject the rest.
27
+ const results = parseCISchema(data);
28
+ if (results.length === 0)
29
+ return null;
30
+ // Return the first result; stash the rest for augmentResults
31
+ pendingResults = results.slice(1);
32
+ return results[0];
33
+ },
34
+ createNaming: (results) => new NamingStrategy(results),
35
+ augmentResults: (results, _opts, log) => {
36
+ // Add the remaining results from the single-schema parse
37
+ if (pendingResults.length > 0) {
38
+ results.push(...pendingResults);
39
+ log(`Added ${pendingResults.length} additional CI entities from single schema`);
40
+ pendingResults = [];
41
+ }
42
+ log(`Total: ${results.length} CI entity schemas`);
43
+ return { results };
44
+ },
45
+ generateRegistry: (results, naming) => {
46
+ return generateLexiconJSON(results, naming);
47
+ },
48
+ generateTypes: (results, naming) => {
49
+ return generateTypeScriptDeclarations(results, naming);
50
+ },
51
+ generateRuntimeIndex: (results, naming) => {
52
+ return generateRuntimeIndex(results, naming);
53
+ },
54
+ };
55
+ return generatePipeline(config, opts);
56
+ }
57
+ /**
58
+ * Write generated artifacts to disk.
59
+ */
60
+ export function writeGeneratedFiles(result, baseDir) {
61
+ writeGeneratedArtifacts({
62
+ baseDir,
63
+ files: {
64
+ "lexicon-gitlab.json": result.lexiconJSON,
65
+ "index.d.ts": result.typesDTS,
66
+ "index.ts": result.indexTS,
67
+ "runtime.ts": `/**\n * Runtime factory constructors — re-exported from core.\n */\nexport { createResource, createProperty } from "@intentius/chant/runtime";\n`,
68
+ },
69
+ });
70
+ }
71
+ /**
72
+ * Generate the runtime index.ts with factory constructor exports.
73
+ */
74
+ function generateRuntimeIndex(results, naming) {
75
+ const resourceEntries = [];
76
+ const propertyEntries = [];
77
+ for (const r of results) {
78
+ const typeName = r.resource.typeName;
79
+ const tsName = naming.resolve(typeName);
80
+ if (!tsName)
81
+ continue;
82
+ // CI entities have no read-only attributes
83
+ const attrs = {};
84
+ if (r.isProperty) {
85
+ // Property-kind entities (Artifacts, Cache, Image, etc.)
86
+ propertyEntries.push({ tsName, resourceType: typeName });
87
+ for (const alias of naming.aliases(typeName)) {
88
+ propertyEntries.push({ tsName: alias, resourceType: typeName });
89
+ }
90
+ }
91
+ else {
92
+ // Resource-kind entities (Job, Default, Workflow)
93
+ resourceEntries.push({ tsName, resourceType: typeName, attrs });
94
+ for (const alias of naming.aliases(typeName)) {
95
+ resourceEntries.push({ tsName: alias, resourceType: typeName, attrs });
96
+ }
97
+ }
98
+ // Property types
99
+ const shortName = gitlabShortName(typeName);
100
+ const ptAliases = naming.propertyTypeAliases(typeName);
101
+ for (const pt of r.propertyTypes) {
102
+ const defName = extractDefName(pt.name, shortName);
103
+ const ptName = propertyTypeName(tsName, defName);
104
+ const ptType = `${typeName}.${pt.defType}`;
105
+ propertyEntries.push({ tsName: ptName, resourceType: ptType });
106
+ if (ptAliases) {
107
+ const aliasName = ptAliases.get(defName);
108
+ if (aliasName) {
109
+ propertyEntries.push({ tsName: aliasName, resourceType: ptType });
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return coreGenerateRuntimeIndex(resourceEntries, propertyEntries, {
115
+ lexiconName: "gitlab",
116
+ intrinsicReExports: {
117
+ names: ["reference"],
118
+ from: "../intrinsics",
119
+ },
120
+ pseudoReExports: {
121
+ names: ["CI"],
122
+ from: "../variables",
123
+ },
124
+ });
125
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * GitLab CI naming strategy — simpler than AWS since CI entities
3
+ * use a flat GitLab::CI:: namespace with few collisions.
4
+ */
5
+ import { NamingStrategy as CoreNamingStrategy, } from "@intentius/chant/codegen/naming";
6
+ import { gitlabShortName, gitlabServiceName } from "./parse.js";
7
+ export { propertyTypeName, extractDefName } from "@intentius/chant/codegen/naming";
8
+ const gitlabNamingConfig = {
9
+ priorityNames: {
10
+ "GitLab::CI::Job": "Job",
11
+ "GitLab::CI::Default": "Default",
12
+ "GitLab::CI::Workflow": "Workflow",
13
+ "GitLab::CI::Pipeline": "Pipeline",
14
+ "GitLab::CI::Artifacts": "Artifacts",
15
+ "GitLab::CI::Cache": "Cache",
16
+ "GitLab::CI::Image": "Image",
17
+ "GitLab::CI::Rule": "Rule",
18
+ "GitLab::CI::Retry": "Retry",
19
+ "GitLab::CI::AllowFailure": "AllowFailure",
20
+ "GitLab::CI::Parallel": "Parallel",
21
+ "GitLab::CI::Include": "Include",
22
+ "GitLab::CI::Release": "Release",
23
+ "GitLab::CI::Environment": "Environment",
24
+ "GitLab::CI::Trigger": "Trigger",
25
+ "GitLab::CI::AutoCancel": "AutoCancel",
26
+ "GitLab::CI::WorkflowRule": "WorkflowRule",
27
+ "GitLab::CI::Need": "Need",
28
+ "GitLab::CI::Inherit": "Inherit",
29
+ },
30
+ priorityAliases: {},
31
+ priorityPropertyAliases: {},
32
+ serviceAbbreviations: {},
33
+ shortName: gitlabShortName,
34
+ serviceName: gitlabServiceName,
35
+ };
36
+ /**
37
+ * GitLab-specific naming strategy.
38
+ * Extends core NamingStrategy with GitLab naming config.
39
+ */
40
+ export class NamingStrategy extends CoreNamingStrategy {
41
+ constructor(results) {
42
+ const inputs = results.map((r) => ({
43
+ typeName: r.resource.typeName,
44
+ propertyTypes: r.propertyTypes,
45
+ }));
46
+ super(inputs, gitlabNamingConfig);
47
+ }
48
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * GitLab lexicon packaging — delegates to core packagePipeline
3
+ * with GitLab-specific manifest building and skill collection.
4
+ */
5
+ import { readFileSync } from "fs";
6
+ import { gitlabPlugin } from "../plugin.js";
7
+ import { join, dirname } from "path";
8
+ import { moduleSourceDir } from "@intentius/chant/module-source";
9
+ import { packagePipeline, collectSkills, } from "@intentius/chant/codegen/package";
10
+ import { generate } from "./generate.js";
11
+ const pkgDir = dirname(moduleSourceDir(import.meta.url));
12
+ /**
13
+ * Package the GitLab lexicon into a distributable BundleSpec.
14
+ */
15
+ export async function packageLexicon(opts = {}) {
16
+ const pkgJson = JSON.parse(readFileSync(join(pkgDir, "..", "package.json"), "utf-8"));
17
+ return packagePipeline({
18
+ generate: (genOpts) => generate({ verbose: genOpts.verbose, force: genOpts.force }),
19
+ buildManifest: (_genResult) => {
20
+ // Derived from the plugin's own registration (chant #1067) — this
21
+ // used to be a hand-maintained array independent of ../plugin.ts
22
+ // (despite the comment claiming otherwise), free to drift from the
23
+ // real registration without anything noticing.
24
+ const intrinsics = gitlabPlugin.intrinsics?.() ?? [];
25
+ return {
26
+ name: "gitlab",
27
+ version: pkgJson.version ?? "0.0.0",
28
+ chantVersion: ">=0.1.0",
29
+ namespace: "GitLab",
30
+ intrinsics,
31
+ pseudoParameters: {},
32
+ };
33
+ },
34
+ srcDir: pkgDir,
35
+ collectSkills: () => {
36
+ const skillDefs = gitlabPlugin.skills?.() ?? [];
37
+ return collectSkills(skillDefs);
38
+ },
39
+ version: pkgJson.version ?? "0.0.0",
40
+ }, opts);
41
+ }