@intentius/chant-lexicon-gitlab 0.124.31 → 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.
- package/dist/codegen/docs-cli.js +2 -0
- package/dist/codegen/docs.js +157 -0
- package/dist/codegen/fetch.js +93 -0
- package/dist/codegen/generate-cli.js +30 -0
- package/dist/codegen/generate-lexicon.js +36 -0
- package/dist/codegen/generate-typescript.js +105 -0
- package/dist/codegen/generate.js +125 -0
- package/dist/codegen/naming.js +48 -0
- package/dist/codegen/package.js +41 -0
- package/dist/codegen/parse.js +539 -0
- package/dist/codegen/patches.js +84 -0
- package/dist/codegen/typecheck.js +4 -0
- package/dist/components/ci-jobs.js +16 -0
- package/dist/components/generate-op-pipeline.js +288 -0
- package/dist/components/generate-op-waves-pipeline.js +125 -0
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +369 -0
- package/dist/composites/catalog.js +418 -0
- package/dist/composites/docker-build.js +38 -0
- package/dist/composites/index.js +5 -0
- package/dist/composites/mr-plan-report.js +42 -0
- package/dist/composites/node-pipeline.js +67 -0
- package/dist/composites/python-pipeline.js +45 -0
- package/dist/composites/review-app.js +32 -0
- package/dist/coverage.js +35 -0
- package/dist/generated/index.js +25 -0
- package/dist/generated/runtime.js +4 -0
- package/dist/graph-edges.js +202 -0
- package/dist/import/generator.js +196 -0
- package/dist/import/parser.js +111 -0
- package/dist/index.js +26 -0
- package/dist/integrity.json +3 -3
- package/dist/intrinsics.js +33 -0
- package/dist/lint/audit-catalog.js +53 -0
- package/dist/lint/audit-lineage.js +64 -0
- package/dist/lint/post-synth/index.js +84 -0
- package/dist/lint/post-synth/wgl010.js +34 -0
- package/dist/lint/post-synth/wgl011.js +48 -0
- package/dist/lint/post-synth/wgl012.d.ts.map +1 -1
- package/dist/lint/post-synth/wgl012.js +68 -0
- package/dist/lint/post-synth/wgl013.js +57 -0
- package/dist/lint/post-synth/wgl014.js +43 -0
- package/dist/lint/post-synth/wgl015.js +70 -0
- package/dist/lint/post-synth/wgl016.js +70 -0
- package/dist/lint/post-synth/wgl017.js +38 -0
- package/dist/lint/post-synth/wgl018.js +34 -0
- package/dist/lint/post-synth/wgl019.js +43 -0
- package/dist/lint/post-synth/wgl020.js +47 -0
- package/dist/lint/post-synth/wgl021.js +52 -0
- package/dist/lint/post-synth/wgl022.js +38 -0
- package/dist/lint/post-synth/wgl023.js +44 -0
- package/dist/lint/post-synth/wgl024.js +41 -0
- package/dist/lint/post-synth/wgl025.js +42 -0
- package/dist/lint/post-synth/wgl026.js +58 -0
- package/dist/lint/post-synth/wgl027.js +49 -0
- package/dist/lint/post-synth/wgl028.js +58 -0
- package/dist/lint/post-synth/wgl029.js +55 -0
- package/dist/lint/post-synth/wgl030.js +43 -0
- package/dist/lint/post-synth/wgl031.js +33 -0
- package/dist/lint/post-synth/wgl032.js +64 -0
- package/dist/lint/post-synth/wgl033.js +32 -0
- package/dist/lint/post-synth/wgl034.js +37 -0
- package/dist/lint/post-synth/wgl035.js +40 -0
- package/dist/lint/post-synth/wgl036.js +38 -0
- package/dist/lint/post-synth/wgl037.js +41 -0
- package/dist/lint/post-synth/wgl038.js +43 -0
- package/dist/lint/post-synth/wgl039.js +34 -0
- package/dist/lint/post-synth/wgl040.js +35 -0
- package/dist/lint/post-synth/wgl041.js +53 -0
- package/dist/lint/post-synth/wgl042.js +41 -0
- package/dist/lint/post-synth/wgl043.js +39 -0
- package/dist/lint/post-synth/wgl044.js +35 -0
- package/dist/lint/post-synth/wgl045.js +54 -0
- package/dist/lint/post-synth/wgl046.js +40 -0
- package/dist/lint/post-synth/wgl047.js +34 -0
- package/dist/lint/post-synth/wgl048.js +32 -0
- package/dist/lint/post-synth/wgl049.js +53 -0
- package/dist/lint/post-synth/wgl050.js +42 -0
- package/dist/lint/post-synth/yaml-helpers.js +432 -0
- package/dist/lint/rules/artifact-no-expiry.js +53 -0
- package/dist/lint/rules/data/known-include-sources.js +44 -0
- package/dist/lint/rules/data/untrusted-variables.js +30 -0
- package/dist/lint/rules/deprecated-only-except.js +46 -0
- package/dist/lint/rules/import-source.js +61 -0
- package/dist/lint/rules/index.js +7 -0
- package/dist/lint/rules/missing-script.js +67 -0
- package/dist/lint/rules/missing-stage.js +60 -0
- package/dist/lsp/completions.d.ts.map +1 -1
- package/dist/lsp/completions.js +18 -0
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/lsp/hover.js +33 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/context-tools.js +262 -0
- package/dist/migrate/from-github/actions/index.js +17 -0
- package/dist/migrate/from-github/actions/registry.js +61 -0
- package/dist/migrate/from-github/actions/tier-1.js +298 -0
- package/dist/migrate/from-github/actions/tier-2.js +241 -0
- package/dist/migrate/from-github/actions/tier-3.js +96 -0
- package/dist/migrate/from-github/composites/patterns.js +151 -0
- package/dist/migrate/from-github/composites/rewriter.js +22 -0
- package/dist/migrate/from-github/diagnostics.js +53 -0
- package/dist/migrate/from-github/emit-yaml.js +121 -0
- package/dist/migrate/from-github/expressions.js +275 -0
- package/dist/migrate/from-github/index.js +96 -0
- package/dist/migrate/from-github/provenance.js +35 -0
- package/dist/migrate/from-github/rules.js +74 -0
- package/dist/migrate/from-github/security.js +189 -0
- package/dist/migrate/from-github/stages.js +156 -0
- package/dist/migrate/from-github/transformer.js +633 -0
- package/dist/op/activities/gitlab.js +38 -0
- package/dist/op/activities/index.js +10 -0
- package/dist/op/builders.js +26 -0
- package/dist/package-cli.js +21 -0
- package/dist/plugin.js +536 -0
- package/dist/rules/wgl012.ts +2 -2
- package/dist/serializer.js +197 -0
- package/dist/testdata/create-fixture.js +40 -0
- package/dist/testdata/load-fixtures.js +20 -0
- package/dist/validate-cli.js +16 -0
- package/dist/validate.js +29 -0
- package/dist/variables.js +30 -0
- package/package.json +8 -8
- package/src/codegen/fetch.ts +2 -2
- package/src/codegen/package.ts +2 -2
- package/src/components/__fixtures__/pr-loop-build.gitlab.golden.yml +3 -3
- package/src/components/__fixtures__/pr-loop-waves.gitlab.golden.yml +101 -0
- package/src/components/__fixtures__/pr-loop.gitlab.golden.yml +3 -3
- package/src/components/generate-pipeline.ts +60 -27
- package/src/components/generate-pr-pipeline.test.ts +14 -9
- package/src/components/generate-pr-waves-pipeline.test.ts +88 -0
- package/src/lint/post-synth/wgl012.ts +2 -2
- package/src/lsp/completions.ts +2 -1
- package/src/lsp/hover.ts +2 -1
|
@@ -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
|
+
}
|