@intentius/chant-lexicon-gitlab 0.0.6 → 0.0.9
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 +10 -6
- package/dist/manifest.json +1 -1
- package/dist/meta.json +186 -8
- package/dist/rules/wgl012.ts +86 -0
- package/dist/rules/wgl013.ts +62 -0
- package/dist/rules/wgl014.ts +51 -0
- package/dist/rules/wgl015.ts +85 -0
- package/dist/rules/yaml-helpers.ts +65 -3
- package/dist/skills/chant-gitlab.md +502 -0
- package/dist/types/index.d.ts +55 -16
- package/package.json +2 -2
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +58 -0
- package/src/codegen/docs.ts +88 -11
- package/src/codegen/generate-lexicon.ts +6 -1
- package/src/codegen/generate.ts +45 -50
- package/src/codegen/naming.ts +3 -0
- package/src/codegen/package.ts +2 -0
- package/src/codegen/parse.test.ts +154 -4
- package/src/codegen/parse.ts +161 -49
- package/src/codegen/snapshot.test.ts +7 -5
- package/src/composites/composites.test.ts +452 -0
- package/src/composites/docker-build.ts +81 -0
- package/src/composites/index.ts +8 -0
- package/src/composites/node-pipeline.ts +104 -0
- package/src/composites/python-pipeline.ts +75 -0
- package/src/composites/review-app.ts +63 -0
- package/src/generated/index.d.ts +55 -16
- package/src/generated/index.ts +3 -0
- package/src/generated/lexicon-gitlab.json +186 -8
- package/src/import/generator.ts +3 -2
- package/src/import/parser.test.ts +3 -3
- package/src/import/parser.ts +12 -26
- package/src/index.ts +4 -0
- package/src/lint/post-synth/wgl012.test.ts +131 -0
- package/src/lint/post-synth/wgl012.ts +86 -0
- package/src/lint/post-synth/wgl013.test.ts +164 -0
- package/src/lint/post-synth/wgl013.ts +62 -0
- package/src/lint/post-synth/wgl014.test.ts +97 -0
- package/src/lint/post-synth/wgl014.ts +51 -0
- package/src/lint/post-synth/wgl015.test.ts +139 -0
- package/src/lint/post-synth/wgl015.ts +85 -0
- package/src/lint/post-synth/yaml-helpers.ts +65 -3
- package/src/lsp/completions.ts +2 -0
- package/src/lsp/hover.ts +2 -0
- package/src/plugin.test.ts +44 -19
- package/src/plugin.ts +671 -76
- package/src/serializer.test.ts +146 -6
- package/src/serializer.ts +64 -14
- package/src/validate.ts +1 -0
- package/src/variables.ts +4 -0
- package/dist/skills/gitlab-ci.md +0 -37
- package/src/codegen/rollback.ts +0 -26
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Composite } from "@intentius/chant";
|
|
2
|
+
import { Job, Default, Image, Cache, Artifacts } from "../generated";
|
|
3
|
+
import { CI } from "../variables";
|
|
4
|
+
|
|
5
|
+
export interface PythonPipelineProps {
|
|
6
|
+
/** Python version. Default: "3.12" */
|
|
7
|
+
pythonVersion?: string;
|
|
8
|
+
/** Test command. Default: "pytest --junitxml=report.xml --cov" */
|
|
9
|
+
testCommand?: string;
|
|
10
|
+
/** Lint command. Set to null to omit lint job. Default: "ruff check ." */
|
|
11
|
+
lintCommand?: string | null;
|
|
12
|
+
/** Requirements file. Default: "requirements.txt" */
|
|
13
|
+
requirementsFile?: string;
|
|
14
|
+
/** Use poetry instead of pip. Default: false */
|
|
15
|
+
usePoetry?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const PythonPipeline = Composite<PythonPipelineProps>((props) => {
|
|
19
|
+
const {
|
|
20
|
+
pythonVersion = "3.12",
|
|
21
|
+
testCommand = "pytest --junitxml=report.xml --cov",
|
|
22
|
+
lintCommand = "ruff check .",
|
|
23
|
+
requirementsFile = "requirements.txt",
|
|
24
|
+
usePoetry = false,
|
|
25
|
+
} = props;
|
|
26
|
+
|
|
27
|
+
const pythonImage = new Image({ name: `python:${pythonVersion}-slim` });
|
|
28
|
+
|
|
29
|
+
const cache = new Cache({
|
|
30
|
+
key: { files: [usePoetry ? "poetry.lock" : requirementsFile] },
|
|
31
|
+
paths: [".pip-cache/", ".venv/"],
|
|
32
|
+
policy: "pull-push",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const installSteps = usePoetry
|
|
36
|
+
? ["pip install poetry", "poetry config virtualenvs.in-project true", "poetry install"]
|
|
37
|
+
: [
|
|
38
|
+
"python -m venv .venv",
|
|
39
|
+
"source .venv/bin/activate",
|
|
40
|
+
`pip install -r ${requirementsFile}`,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const activateStep = usePoetry ? "source .venv/bin/activate" : "source .venv/bin/activate";
|
|
44
|
+
|
|
45
|
+
const defaults = new Default({
|
|
46
|
+
image: pythonImage,
|
|
47
|
+
cache: [cache],
|
|
48
|
+
before_script: [...installSteps],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const test = new Job({
|
|
52
|
+
stage: "test",
|
|
53
|
+
variables: { PIP_CACHE_DIR: `${CI.ProjectDir}/.pip-cache` },
|
|
54
|
+
script: [activateStep, testCommand],
|
|
55
|
+
artifacts: new Artifacts({
|
|
56
|
+
reports: { junit: "report.xml" },
|
|
57
|
+
when: "always",
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const lint =
|
|
62
|
+
lintCommand !== null
|
|
63
|
+
? new Job({
|
|
64
|
+
stage: "test",
|
|
65
|
+
variables: { PIP_CACHE_DIR: `${CI.ProjectDir}/.pip-cache` },
|
|
66
|
+
script: [activateStep, lintCommand],
|
|
67
|
+
})
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
70
|
+
if (lint) {
|
|
71
|
+
return { defaults, test, lint };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { defaults, test } as any;
|
|
75
|
+
}, "PythonPipeline");
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Composite } from "@intentius/chant";
|
|
2
|
+
import { Job, Image, Environment, Rule } from "../generated";
|
|
3
|
+
import { CI } from "../variables";
|
|
4
|
+
|
|
5
|
+
export interface ReviewAppProps {
|
|
6
|
+
/** Base name for the jobs (e.g. "review" → review-deploy, review-stop in YAML). */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Deploy script command(s). */
|
|
9
|
+
deployScript: string | string[];
|
|
10
|
+
/** Stop script command(s). Default: echo "Stopping review app..." */
|
|
11
|
+
stopScript?: string | string[];
|
|
12
|
+
/** Environment URL pattern. Default: "https://$CI_ENVIRONMENT_SLUG.example.com" */
|
|
13
|
+
urlPattern?: string;
|
|
14
|
+
/** Auto-stop timer. Default: "1 week" */
|
|
15
|
+
autoStopIn?: string;
|
|
16
|
+
/** Override image for both jobs. */
|
|
17
|
+
image?: InstanceType<typeof Image>;
|
|
18
|
+
/** Job stage. Default: "deploy" */
|
|
19
|
+
stage?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const ReviewApp = Composite<ReviewAppProps>((props) => {
|
|
23
|
+
const {
|
|
24
|
+
name,
|
|
25
|
+
deployScript,
|
|
26
|
+
stopScript = 'echo "Stopping review app..."',
|
|
27
|
+
urlPattern = `https://${CI.EnvironmentSlug}.example.com`,
|
|
28
|
+
autoStopIn = "1 week",
|
|
29
|
+
image,
|
|
30
|
+
stage = "deploy",
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
const stopJobName = `${name}-stop`;
|
|
34
|
+
|
|
35
|
+
const deployScriptArr = Array.isArray(deployScript) ? deployScript : [deployScript];
|
|
36
|
+
const stopScriptArr = Array.isArray(stopScript) ? stopScript : [stopScript];
|
|
37
|
+
|
|
38
|
+
const deploy = new Job({
|
|
39
|
+
stage,
|
|
40
|
+
...(image ? { image } : {}),
|
|
41
|
+
environment: new Environment({
|
|
42
|
+
name: `review/${CI.CommitRefSlug}`,
|
|
43
|
+
url: urlPattern,
|
|
44
|
+
auto_stop_in: autoStopIn,
|
|
45
|
+
on_stop: stopJobName,
|
|
46
|
+
}),
|
|
47
|
+
rules: [new Rule({ if: CI.MergeRequestIid })],
|
|
48
|
+
script: deployScriptArr,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const stop = new Job({
|
|
52
|
+
stage,
|
|
53
|
+
...(image ? { image } : {}),
|
|
54
|
+
environment: new Environment({
|
|
55
|
+
name: `review/${CI.CommitRefSlug}`,
|
|
56
|
+
action: "stop",
|
|
57
|
+
}),
|
|
58
|
+
rules: [new Rule({ if: CI.MergeRequestIid, when: "manual" })],
|
|
59
|
+
script: stopScriptArr,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { deploy, stop };
|
|
63
|
+
}, "ReviewApp");
|
package/src/generated/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export declare class AllowFailure {
|
|
6
6
|
constructor(props: {
|
|
7
|
-
exit_codes: number;
|
|
7
|
+
exit_codes: number | number[];
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -48,7 +48,7 @@ export declare class Default {
|
|
|
48
48
|
before_script?: string | string[];
|
|
49
49
|
cache?: Cache | Cache[];
|
|
50
50
|
hooks?: Record<string, unknown>;
|
|
51
|
-
id_tokens?: Record<string,
|
|
51
|
+
id_tokens?: Record<string, { aud: string | string[] }>;
|
|
52
52
|
identity?: "google_cloud";
|
|
53
53
|
image?: Image;
|
|
54
54
|
interruptible?: boolean;
|
|
@@ -85,19 +85,34 @@ export declare class Image {
|
|
|
85
85
|
docker?: Record<string, unknown>;
|
|
86
86
|
/** Command or script that should be executed as the container's entrypoint. It will be translated to Docker's --entrypoint option while creating the container. The syntax is similar to Dockerfile's ENTRYPOINT directive, where each shell token is a separate string in the array. */
|
|
87
87
|
entrypoint?: any[];
|
|
88
|
-
pull_policy?: "always" | "never" | "if-not-present" | "always" | "never" | "if-not-present"[];
|
|
88
|
+
pull_policy?: "always" | "never" | "if-not-present" | ("always" | "never" | "if-not-present")[];
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export declare class Include {
|
|
93
93
|
constructor(props: {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
/** Local path to component directory or full path to external component directory. */
|
|
95
|
+
component?: string;
|
|
96
|
+
file?: string | string[];
|
|
97
97
|
inputs?: Record<string, unknown>;
|
|
98
|
+
/** Relative path from local repository root (`/`) to the `yaml`/`yml` file template. The file must be on the same branch, and does not work across git submodules. */
|
|
99
|
+
local?: string;
|
|
100
|
+
/** Path to the project, e.g. `group/project`, or `group/sub-group/project` [Learn more](https://docs.gitlab.com/ee/ci/yaml/index.html#includefile). */
|
|
101
|
+
project?: string;
|
|
98
102
|
/** Branch/Tag/Commit-hash for the target project. */
|
|
99
103
|
ref?: string;
|
|
104
|
+
/** URL to a `yaml`/`yml` template file using HTTP/HTTPS. */
|
|
105
|
+
remote?: string;
|
|
100
106
|
rules?: any[];
|
|
107
|
+
/** Use a `.gitlab-ci.yml` template as a base, e.g. `Nodejs.gitlab-ci.yml`. */
|
|
108
|
+
template?: string;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export declare class Inherit {
|
|
113
|
+
constructor(props: {
|
|
114
|
+
default?: boolean | ("after_script" | "artifacts" | "before_script" | "cache" | "image" | "interruptible" | "retry" | "services" | "tags" | "timeout")[];
|
|
115
|
+
variables?: boolean | string[];
|
|
101
116
|
});
|
|
102
117
|
}
|
|
103
118
|
|
|
@@ -113,20 +128,20 @@ export declare class Job {
|
|
|
113
128
|
/** Specify a list of job names from earlier stages from which artifacts should be loaded. By default, all previous artifacts are passed. Use an empty array to skip downloading artifacts. */
|
|
114
129
|
dependencies?: string[];
|
|
115
130
|
/** Used to associate environment metadata with a deploy. Environment can have a name and URL attached to it, and will be displayed under /environments under the project. */
|
|
116
|
-
environment?:
|
|
131
|
+
environment?: Environment | string;
|
|
117
132
|
/** Job will run *except* for when these filtering options match. */
|
|
118
133
|
except?: any;
|
|
119
134
|
/** The name of one or more jobs to inherit configuration from. */
|
|
120
135
|
extends?: string | string[];
|
|
121
136
|
hooks?: Record<string, unknown>;
|
|
122
|
-
id_tokens?: Record<string,
|
|
137
|
+
id_tokens?: Record<string, { aud: string | string[] }>;
|
|
123
138
|
identity?: "google_cloud";
|
|
124
139
|
image?: Image;
|
|
125
|
-
inherit?:
|
|
140
|
+
inherit?: Inherit;
|
|
126
141
|
interruptible?: boolean;
|
|
127
142
|
manual_confirmation?: string;
|
|
128
143
|
/** The list of jobs in previous stages whose sole completion is needed to start the current job. */
|
|
129
|
-
needs?:
|
|
144
|
+
needs?: Need[];
|
|
130
145
|
/** Job will run *only* when these filtering options match. */
|
|
131
146
|
only?: any;
|
|
132
147
|
pages?: Record<string, any> | boolean;
|
|
@@ -134,7 +149,7 @@ export declare class Job {
|
|
|
134
149
|
/** A path to a directory that contains the files to be published with Pages */
|
|
135
150
|
publish?: string;
|
|
136
151
|
/** Indicates that the job creates a Release. */
|
|
137
|
-
release?:
|
|
152
|
+
release?: Release;
|
|
138
153
|
/** Limit job concurrency. Can be used to ensure that the Runner will not run certain jobs simultaneously. */
|
|
139
154
|
resource_group?: string;
|
|
140
155
|
retry?: Retry | number;
|
|
@@ -148,12 +163,24 @@ export declare class Job {
|
|
|
148
163
|
start_in?: string;
|
|
149
164
|
tags?: any[];
|
|
150
165
|
timeout?: string;
|
|
151
|
-
trigger?:
|
|
166
|
+
trigger?: Trigger | string;
|
|
152
167
|
variables?: Record<string, unknown>;
|
|
153
168
|
when?: "on_success" | "on_failure" | "always" | "never" | "manual" | "delayed";
|
|
154
169
|
});
|
|
155
170
|
}
|
|
156
171
|
|
|
172
|
+
export declare class Need {
|
|
173
|
+
constructor(props: {
|
|
174
|
+
job: string;
|
|
175
|
+
artifacts?: boolean;
|
|
176
|
+
optional?: boolean;
|
|
177
|
+
parallel?: any;
|
|
178
|
+
pipeline?: string;
|
|
179
|
+
project?: string;
|
|
180
|
+
ref?: string;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
157
184
|
export declare class Parallel {
|
|
158
185
|
constructor(props: {
|
|
159
186
|
/** Defines different variables for jobs that are running in parallel. */
|
|
@@ -211,19 +238,20 @@ export declare class Service {
|
|
|
211
238
|
command?: string[];
|
|
212
239
|
docker?: Record<string, unknown>;
|
|
213
240
|
entrypoint?: string[];
|
|
214
|
-
pull_policy?: "always" | "never" | "if-not-present" | "always" | "never" | "if-not-present"[];
|
|
241
|
+
pull_policy?: "always" | "never" | "if-not-present" | ("always" | "never" | "if-not-present")[];
|
|
215
242
|
variables?: Record<string, unknown>;
|
|
216
243
|
});
|
|
217
244
|
}
|
|
218
245
|
|
|
219
246
|
export declare class Trigger {
|
|
220
247
|
constructor(props: {
|
|
221
|
-
/** Path to the project, e.g. `group/project`, or `group/sub-group/project`. */
|
|
222
|
-
project: string;
|
|
223
248
|
/** The branch name that a downstream pipeline will use */
|
|
224
249
|
branch?: string;
|
|
225
250
|
/** Specify what to forward to the downstream pipeline. */
|
|
226
251
|
forward?: Record<string, unknown>;
|
|
252
|
+
include?: string | Record<string, any>[];
|
|
253
|
+
/** Path to the project, e.g. `group/project`, or `group/sub-group/project`. */
|
|
254
|
+
project?: string;
|
|
227
255
|
/** You can mirror the pipeline status from the triggered pipeline to the source bridge job by using strategy: depend */
|
|
228
256
|
strategy?: "depend";
|
|
229
257
|
});
|
|
@@ -233,7 +261,18 @@ export declare class Workflow {
|
|
|
233
261
|
constructor(props: {
|
|
234
262
|
auto_cancel?: AutoCancel;
|
|
235
263
|
name?: string;
|
|
236
|
-
rules?:
|
|
264
|
+
rules?: WorkflowRule[];
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export declare class WorkflowRule {
|
|
269
|
+
constructor(props: {
|
|
270
|
+
auto_cancel?: AutoCancel;
|
|
271
|
+
changes?: any;
|
|
272
|
+
exists?: any;
|
|
273
|
+
if?: string;
|
|
274
|
+
variables?: Record<string, unknown>;
|
|
275
|
+
when?: "always" | "never";
|
|
237
276
|
});
|
|
238
277
|
}
|
|
239
278
|
|
package/src/generated/index.ts
CHANGED
|
@@ -12,12 +12,15 @@ export const Cache = createProperty("GitLab::CI::Cache", "gitlab");
|
|
|
12
12
|
export const Environment = createProperty("GitLab::CI::Environment", "gitlab");
|
|
13
13
|
export const Image = createProperty("GitLab::CI::Image", "gitlab");
|
|
14
14
|
export const Include = createProperty("GitLab::CI::Include", "gitlab");
|
|
15
|
+
export const Inherit = createProperty("GitLab::CI::Inherit", "gitlab");
|
|
16
|
+
export const Need = createProperty("GitLab::CI::Need", "gitlab");
|
|
15
17
|
export const Parallel = createProperty("GitLab::CI::Parallel", "gitlab");
|
|
16
18
|
export const Release = createProperty("GitLab::CI::Release", "gitlab");
|
|
17
19
|
export const Retry = createProperty("GitLab::CI::Retry", "gitlab");
|
|
18
20
|
export const Rule = createProperty("GitLab::CI::Rule", "gitlab");
|
|
19
21
|
export const Service = createProperty("GitLab::CI::Service", "gitlab");
|
|
20
22
|
export const Trigger = createProperty("GitLab::CI::Trigger", "gitlab");
|
|
23
|
+
export const WorkflowRule = createProperty("GitLab::CI::WorkflowRule", "gitlab");
|
|
21
24
|
|
|
22
25
|
// Re-exports for convenience
|
|
23
26
|
export { reference } from "../intrinsics";
|
|
@@ -7,17 +7,77 @@
|
|
|
7
7
|
"Artifacts": {
|
|
8
8
|
"resourceType": "GitLab::CI::Artifacts",
|
|
9
9
|
"kind": "property",
|
|
10
|
-
"lexicon": "gitlab"
|
|
10
|
+
"lexicon": "gitlab",
|
|
11
|
+
"constraints": {
|
|
12
|
+
"untracked": {
|
|
13
|
+
"default": false
|
|
14
|
+
},
|
|
15
|
+
"when": {
|
|
16
|
+
"default": "on_success",
|
|
17
|
+
"enum": [
|
|
18
|
+
"on_success",
|
|
19
|
+
"on_failure",
|
|
20
|
+
"always"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"access": {
|
|
24
|
+
"default": "all",
|
|
25
|
+
"enum": [
|
|
26
|
+
"none",
|
|
27
|
+
"developer",
|
|
28
|
+
"all"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"expire_in": {
|
|
32
|
+
"default": "30 days"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
11
35
|
},
|
|
12
36
|
"AutoCancel": {
|
|
13
37
|
"resourceType": "GitLab::CI::AutoCancel",
|
|
14
38
|
"kind": "property",
|
|
15
|
-
"lexicon": "gitlab"
|
|
39
|
+
"lexicon": "gitlab",
|
|
40
|
+
"constraints": {
|
|
41
|
+
"on_job_failure": {
|
|
42
|
+
"default": "none",
|
|
43
|
+
"enum": [
|
|
44
|
+
"none",
|
|
45
|
+
"all"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"on_new_commit": {
|
|
49
|
+
"enum": [
|
|
50
|
+
"conservative",
|
|
51
|
+
"interruptible",
|
|
52
|
+
"none"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
16
56
|
},
|
|
17
57
|
"Cache": {
|
|
18
58
|
"resourceType": "GitLab::CI::Cache",
|
|
19
59
|
"kind": "property",
|
|
20
|
-
"lexicon": "gitlab"
|
|
60
|
+
"lexicon": "gitlab",
|
|
61
|
+
"constraints": {
|
|
62
|
+
"policy": {
|
|
63
|
+
"pattern": "pull-push|pull|push|\\$\\w{1,255}",
|
|
64
|
+
"default": "pull-push"
|
|
65
|
+
},
|
|
66
|
+
"unprotect": {
|
|
67
|
+
"default": false
|
|
68
|
+
},
|
|
69
|
+
"untracked": {
|
|
70
|
+
"default": false
|
|
71
|
+
},
|
|
72
|
+
"when": {
|
|
73
|
+
"default": "on_success",
|
|
74
|
+
"enum": [
|
|
75
|
+
"on_success",
|
|
76
|
+
"on_failure",
|
|
77
|
+
"always"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
21
81
|
},
|
|
22
82
|
"Default": {
|
|
23
83
|
"resourceType": "GitLab::CI::Default",
|
|
@@ -27,21 +87,93 @@
|
|
|
27
87
|
"Environment": {
|
|
28
88
|
"resourceType": "GitLab::CI::Environment",
|
|
29
89
|
"kind": "property",
|
|
30
|
-
"lexicon": "gitlab"
|
|
90
|
+
"lexicon": "gitlab",
|
|
91
|
+
"constraints": {
|
|
92
|
+
"name": {
|
|
93
|
+
"minLength": 1
|
|
94
|
+
},
|
|
95
|
+
"url": {
|
|
96
|
+
"pattern": "^(https?://.+|\\$[A-Za-z]+)",
|
|
97
|
+
"format": "uri"
|
|
98
|
+
},
|
|
99
|
+
"action": {
|
|
100
|
+
"default": "start",
|
|
101
|
+
"enum": [
|
|
102
|
+
"start",
|
|
103
|
+
"prepare",
|
|
104
|
+
"stop",
|
|
105
|
+
"verify",
|
|
106
|
+
"access"
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"deployment_tier": {
|
|
110
|
+
"enum": [
|
|
111
|
+
"production",
|
|
112
|
+
"staging",
|
|
113
|
+
"testing",
|
|
114
|
+
"development",
|
|
115
|
+
"other"
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
31
119
|
},
|
|
32
120
|
"Image": {
|
|
33
121
|
"resourceType": "GitLab::CI::Image",
|
|
34
122
|
"kind": "property",
|
|
35
|
-
"lexicon": "gitlab"
|
|
123
|
+
"lexicon": "gitlab",
|
|
124
|
+
"constraints": {
|
|
125
|
+
"name": {
|
|
126
|
+
"minLength": 1
|
|
127
|
+
},
|
|
128
|
+
"pull_policy": {
|
|
129
|
+
"default": "always"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
36
132
|
},
|
|
37
133
|
"Include": {
|
|
38
134
|
"resourceType": "GitLab::CI::Include",
|
|
39
135
|
"kind": "property",
|
|
136
|
+
"lexicon": "gitlab",
|
|
137
|
+
"constraints": {
|
|
138
|
+
"project": {
|
|
139
|
+
"pattern": "(?:\\S/\\S|\\$\\S+)"
|
|
140
|
+
},
|
|
141
|
+
"local": {
|
|
142
|
+
"pattern": "\\.ya?ml$",
|
|
143
|
+
"format": "uri-reference"
|
|
144
|
+
},
|
|
145
|
+
"template": {
|
|
146
|
+
"pattern": "\\.ya?ml$",
|
|
147
|
+
"format": "uri-reference"
|
|
148
|
+
},
|
|
149
|
+
"component": {
|
|
150
|
+
"format": "uri-reference"
|
|
151
|
+
},
|
|
152
|
+
"remote": {
|
|
153
|
+
"pattern": "^https?://.+\\.ya?ml$",
|
|
154
|
+
"format": "uri-reference"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"Inherit": {
|
|
159
|
+
"resourceType": "GitLab::CI::Inherit",
|
|
160
|
+
"kind": "property",
|
|
40
161
|
"lexicon": "gitlab"
|
|
41
162
|
},
|
|
42
163
|
"Job": {
|
|
43
164
|
"resourceType": "GitLab::CI::Job",
|
|
44
165
|
"kind": "resource",
|
|
166
|
+
"lexicon": "gitlab",
|
|
167
|
+
"constraints": {
|
|
168
|
+
"coverage": {
|
|
169
|
+
"pattern": "^/.+/$",
|
|
170
|
+
"format": "regex"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"Need": {
|
|
175
|
+
"resourceType": "GitLab::CI::Need",
|
|
176
|
+
"kind": "property",
|
|
45
177
|
"lexicon": "gitlab"
|
|
46
178
|
},
|
|
47
179
|
"Parallel": {
|
|
@@ -52,7 +184,19 @@
|
|
|
52
184
|
"Release": {
|
|
53
185
|
"resourceType": "GitLab::CI::Release",
|
|
54
186
|
"kind": "property",
|
|
55
|
-
"lexicon": "gitlab"
|
|
187
|
+
"lexicon": "gitlab",
|
|
188
|
+
"constraints": {
|
|
189
|
+
"tag_name": {
|
|
190
|
+
"minLength": 1
|
|
191
|
+
},
|
|
192
|
+
"description": {
|
|
193
|
+
"minLength": 1
|
|
194
|
+
},
|
|
195
|
+
"released_at": {
|
|
196
|
+
"pattern": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:Z|[+-][01]\\d:[0-5]\\d)$",
|
|
197
|
+
"format": "date-time"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
56
200
|
},
|
|
57
201
|
"Retry": {
|
|
58
202
|
"resourceType": "GitLab::CI::Retry",
|
|
@@ -67,16 +211,50 @@
|
|
|
67
211
|
"Service": {
|
|
68
212
|
"resourceType": "GitLab::CI::Service",
|
|
69
213
|
"kind": "property",
|
|
70
|
-
"lexicon": "gitlab"
|
|
214
|
+
"lexicon": "gitlab",
|
|
215
|
+
"constraints": {
|
|
216
|
+
"name": {
|
|
217
|
+
"minLength": 1
|
|
218
|
+
},
|
|
219
|
+
"pull_policy": {
|
|
220
|
+
"default": "always"
|
|
221
|
+
},
|
|
222
|
+
"alias": {
|
|
223
|
+
"minLength": 1
|
|
224
|
+
}
|
|
225
|
+
}
|
|
71
226
|
},
|
|
72
227
|
"Trigger": {
|
|
73
228
|
"resourceType": "GitLab::CI::Trigger",
|
|
74
229
|
"kind": "property",
|
|
75
|
-
"lexicon": "gitlab"
|
|
230
|
+
"lexicon": "gitlab",
|
|
231
|
+
"constraints": {
|
|
232
|
+
"project": {
|
|
233
|
+
"pattern": "(?:\\S/\\S|\\$\\S+)"
|
|
234
|
+
},
|
|
235
|
+
"strategy": {
|
|
236
|
+
"enum": [
|
|
237
|
+
"depend"
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
}
|
|
76
241
|
},
|
|
77
242
|
"Workflow": {
|
|
78
243
|
"resourceType": "GitLab::CI::Workflow",
|
|
79
244
|
"kind": "resource",
|
|
80
245
|
"lexicon": "gitlab"
|
|
246
|
+
},
|
|
247
|
+
"WorkflowRule": {
|
|
248
|
+
"resourceType": "GitLab::CI::WorkflowRule",
|
|
249
|
+
"kind": "property",
|
|
250
|
+
"lexicon": "gitlab",
|
|
251
|
+
"constraints": {
|
|
252
|
+
"when": {
|
|
253
|
+
"enum": [
|
|
254
|
+
"always",
|
|
255
|
+
"never"
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
}
|
|
81
259
|
}
|
|
82
260
|
}
|
package/src/import/generator.ts
CHANGED
|
@@ -25,11 +25,12 @@ const PROPERTY_CONSTRUCTORS: Record<string, string> = {
|
|
|
25
25
|
cache: "Cache",
|
|
26
26
|
image: "Image",
|
|
27
27
|
retry: "Retry",
|
|
28
|
-
|
|
28
|
+
allow_failure: "AllowFailure",
|
|
29
29
|
parallel: "Parallel",
|
|
30
30
|
environment: "Environment",
|
|
31
31
|
trigger: "Trigger",
|
|
32
|
-
|
|
32
|
+
auto_cancel: "AutoCancel",
|
|
33
|
+
inherit: "Inherit",
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -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/index.ts
CHANGED
|
@@ -14,6 +14,10 @@ export { CI } from "./variables";
|
|
|
14
14
|
// After running `chant generate`, this re-exports all CI entity classes
|
|
15
15
|
export * from "./generated/index";
|
|
16
16
|
|
|
17
|
+
// Composites
|
|
18
|
+
export { DockerBuild, NodePipeline, BunPipeline, PnpmPipeline, PythonPipeline, ReviewApp } from "./composites/index";
|
|
19
|
+
export type { DockerBuildProps, NodePipelineProps, PythonPipelineProps, ReviewAppProps } from "./composites/index";
|
|
20
|
+
|
|
17
21
|
// Spec utilities (for tooling)
|
|
18
22
|
export { fetchCISchema, fetchSchemas, GITLAB_SCHEMA_VERSION } from "./codegen/fetch";
|
|
19
23
|
export { parseCISchema, gitlabShortName, gitlabServiceName } from "./codegen/parse";
|