@intentius/chant-lexicon-gitlab 0.0.3 → 0.0.6

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.
@@ -132,6 +132,7 @@ const PROPERTY_ENTITIES: Array<{
132
132
  { typeName: "GitLab::CI::Artifacts", source: "#/definitions/artifacts", description: "Job artifact configuration" },
133
133
  { typeName: "GitLab::CI::Cache", source: "#/definitions/cache_item", description: "Cache configuration" },
134
134
  { typeName: "GitLab::CI::Image", source: "#/definitions/image", description: "Docker image for a job" },
135
+ { typeName: "GitLab::CI::Service", source: "#/definitions/services:item", description: "Docker service container for a job" },
135
136
  { typeName: "GitLab::CI::Rule", source: "#/definitions/rules:item", description: "Conditional rule for job execution" },
136
137
  { typeName: "GitLab::CI::Retry", source: "#/definitions/retry", description: "Job retry configuration" },
137
138
  { typeName: "GitLab::CI::AllowFailure", source: "#/definitions/allow_failure", description: "Allow failure configuration" },
@@ -266,17 +267,14 @@ function extractPropertyEntity(
266
267
  * Resolve a source path to a schema definition.
267
268
  */
268
269
  function resolveSource(schema: CISchema, source: string): CISchemaDefinition | null {
269
- // Special case: rules array item extraction
270
- if (source === "#/definitions/rules:item") {
271
- const rulesDef = schema.definitions?.rules;
272
- if (!rulesDef) return null;
273
- // rules is an array — get items
274
- const items = rulesDef.items;
275
- if (!items) return null;
276
- return findObjectVariant(items);
277
- }
278
-
279
270
  if (source.startsWith("#/definitions/")) {
271
+ // Array item extraction: "#/definitions/foo:item" → foo.items object variant
272
+ if (source.includes(":item")) {
273
+ const defName = source.slice("#/definitions/".length).replace(":item", "");
274
+ const arrayDef = schema.definitions?.[defName];
275
+ if (!arrayDef?.items) return null;
276
+ return findObjectVariant(arrayDef.items);
277
+ }
280
278
  const defName = source.slice("#/definitions/".length);
281
279
  return schema.definitions?.[defName] ?? null;
282
280
  }
@@ -10,7 +10,7 @@ describe("generated lexicon-gitlab.json", () => {
10
10
  const registry = JSON.parse(content);
11
11
 
12
12
  test("is valid JSON with expected entries", () => {
13
- expect(Object.keys(registry)).toHaveLength(15);
13
+ expect(Object.keys(registry)).toHaveLength(16);
14
14
  });
15
15
 
16
16
  test("contains all resource entities", () => {
@@ -30,7 +30,7 @@ describe("generated lexicon-gitlab.json", () => {
30
30
  const propertyNames = [
31
31
  "AllowFailure", "Artifacts", "AutoCancel", "Cache",
32
32
  "Environment", "Image", "Include", "Parallel",
33
- "Release", "Retry", "Rule", "Trigger",
33
+ "Release", "Retry", "Rule", "Service", "Trigger",
34
34
  ];
35
35
  for (const name of propertyNames) {
36
36
  expect(registry[name]).toBeDefined();
@@ -55,7 +55,7 @@ describe("generated index.d.ts", () => {
55
55
  "Job", "Default", "Workflow",
56
56
  "AllowFailure", "Artifacts", "AutoCancel", "Cache",
57
57
  "Environment", "Image", "Include", "Parallel",
58
- "Release", "Retry", "Rule", "Trigger",
58
+ "Release", "Retry", "Rule", "Service", "Trigger",
59
59
  ];
60
60
  for (const cls of expectedClasses) {
61
61
  expect(content).toContain(`export declare class ${cls}`);
@@ -203,6 +203,19 @@ export declare class Rule {
203
203
  });
204
204
  }
205
205
 
206
+ export declare class Service {
207
+ constructor(props: {
208
+ /** Full name of the image that should be used. It should contain the Registry part if needed. */
209
+ name: string;
210
+ alias?: string;
211
+ command?: string[];
212
+ docker?: Record<string, unknown>;
213
+ entrypoint?: string[];
214
+ pull_policy?: "always" | "never" | "if-not-present" | "always" | "never" | "if-not-present"[];
215
+ variables?: Record<string, unknown>;
216
+ });
217
+ }
218
+
206
219
  export declare class Trigger {
207
220
  constructor(props: {
208
221
  /** Path to the project, e.g. `group/project`, or `group/sub-group/project`. */
@@ -16,6 +16,7 @@ export const Parallel = createProperty("GitLab::CI::Parallel", "gitlab");
16
16
  export const Release = createProperty("GitLab::CI::Release", "gitlab");
17
17
  export const Retry = createProperty("GitLab::CI::Retry", "gitlab");
18
18
  export const Rule = createProperty("GitLab::CI::Rule", "gitlab");
19
+ export const Service = createProperty("GitLab::CI::Service", "gitlab");
19
20
  export const Trigger = createProperty("GitLab::CI::Trigger", "gitlab");
20
21
 
21
22
  // Re-exports for convenience
@@ -64,6 +64,11 @@
64
64
  "kind": "property",
65
65
  "lexicon": "gitlab"
66
66
  },
67
+ "Service": {
68
+ "resourceType": "GitLab::CI::Service",
69
+ "kind": "property",
70
+ "lexicon": "gitlab"
71
+ },
67
72
  "Trigger": {
68
73
  "resourceType": "GitLab::CI::Trigger",
69
74
  "kind": "property",
@@ -96,7 +96,6 @@ describe("gitlabPlugin", () => {
96
96
  test("returns init templates", () => {
97
97
  const templates = gitlabPlugin.initTemplates!();
98
98
  expect(templates).toBeDefined();
99
- expect(templates["_.ts"]).toBeDefined();
100
99
  expect(templates["config.ts"]).toBeDefined();
101
100
  expect(templates["test.ts"]).toBeDefined();
102
101
  });
@@ -192,7 +191,7 @@ describe("gitlabPlugin", () => {
192
191
  const result = await catalog.handler();
193
192
  const parsed = JSON.parse(result);
194
193
  expect(Array.isArray(parsed)).toBe(true);
195
- expect(parsed.length).toBe(15);
194
+ expect(parsed.length).toBe(16);
196
195
  const job = parsed.find((e: { className: string }) => e.className === "Job");
197
196
  expect(job).toBeDefined();
198
197
  expect(job.kind).toBe("resource");
package/src/plugin.ts CHANGED
@@ -41,20 +41,19 @@ export const gitlabPlugin: LexiconPlugin = {
41
41
 
42
42
  initTemplates(): Record<string, string> {
43
43
  return {
44
- "_.ts": `export * from "./config";\n`,
45
44
  "config.ts": `/**
46
45
  * Shared pipeline configuration
47
46
  */
48
47
 
49
- import * as gl from "@intentius/chant-lexicon-gitlab";
48
+ import { Image, Cache } from "@intentius/chant-lexicon-gitlab";
50
49
 
51
50
  // Default image for all jobs
52
- export const defaultImage = new gl.Image({
51
+ export const defaultImage = new Image({
53
52
  name: "node:20-alpine",
54
53
  });
55
54
 
56
55
  // Standard cache configuration
57
- export const npmCache = new gl.Cache({
56
+ export const npmCache = new Cache({
58
57
  key: "$CI_COMMIT_REF_SLUG",
59
58
  paths: ["node_modules/"],
60
59
  policy: "pull-push",
@@ -64,15 +63,15 @@ export const npmCache = new gl.Cache({
64
63
  * Test job
65
64
  */
66
65
 
67
- import * as gl from "@intentius/chant-lexicon-gitlab";
68
- import * as _ from "./_";
66
+ import { Job, Artifacts } from "@intentius/chant-lexicon-gitlab";
67
+ import { defaultImage, npmCache } from "./config";
69
68
 
70
- export const test = new gl.Job({
69
+ export const test = new Job({
71
70
  stage: "test",
72
- image: _.defaultImage,
73
- cache: _.npmCache,
71
+ image: defaultImage,
72
+ cache: npmCache,
74
73
  script: ["npm ci", "npm test"],
75
- artifacts: new gl.Artifacts({
74
+ artifacts: new Artifacts({
76
75
  reports: { junit: "coverage/junit.xml" },
77
76
  paths: ["coverage/"],
78
77
  expireIn: "1 week",
@@ -142,18 +141,9 @@ export const test = new gl.Job({
142
141
 
143
142
  async validate(options?: { verbose?: boolean }): Promise<void> {
144
143
  const { validate } = await import("./validate");
144
+ const { printValidationResult } = await import("@intentius/chant/codegen/validate");
145
145
  const result = await validate();
146
-
147
- for (const check of result.checks) {
148
- const status = check.ok ? "OK" : "FAIL";
149
- const msg = check.error ? ` — ${check.error}` : "";
150
- console.error(` [${status}] ${check.name}${msg}`);
151
- }
152
-
153
- if (!result.success) {
154
- throw new Error("Validation failed");
155
- }
156
- console.error("All validation checks passed.");
146
+ printValidationResult(result);
157
147
  },
158
148
 
159
149
  async coverage(options?: { verbose?: boolean; minOverall?: number }): Promise<void> {
@@ -166,7 +156,7 @@ export const test = new gl.Job({
166
156
 
167
157
  async package(options?: { verbose?: boolean; force?: boolean }): Promise<void> {
168
158
  const { packageLexicon } = await import("./codegen/package");
169
- const { writeFileSync, mkdirSync } = await import("fs");
159
+ const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
170
160
  const { join, dirname } = await import("path");
171
161
  const { fileURLToPath } = await import("url");
172
162
 
@@ -174,24 +164,7 @@ export const test = new gl.Job({
174
164
 
175
165
  const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
176
166
  const distDir = join(pkgDir, "dist");
177
- mkdirSync(join(distDir, "types"), { recursive: true });
178
- mkdirSync(join(distDir, "rules"), { recursive: true });
179
- mkdirSync(join(distDir, "skills"), { recursive: true });
180
-
181
- writeFileSync(join(distDir, "manifest.json"), JSON.stringify(spec.manifest, null, 2));
182
- writeFileSync(join(distDir, "meta.json"), spec.registry);
183
- writeFileSync(join(distDir, "types", "index.d.ts"), spec.typesDTS);
184
-
185
- for (const [name, content] of spec.rules) {
186
- writeFileSync(join(distDir, "rules", name), content);
187
- }
188
- for (const [name, content] of spec.skills) {
189
- writeFileSync(join(distDir, "skills", name), content);
190
- }
191
-
192
- if (spec.integrity) {
193
- writeFileSync(join(distDir, "integrity.json"), JSON.stringify(spec.integrity, null, 2));
194
- }
167
+ writeBundleSpec(spec, distDir);
195
168
 
196
169
  console.error(`Packaged ${stats.resources} entities, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
197
170
  },