@intentius/chant-lexicon-gitlab 0.0.15 → 0.0.18

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 (67) hide show
  1. package/dist/integrity.json +18 -4
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/wgl016.ts +82 -0
  4. package/dist/rules/wgl017.ts +54 -0
  5. package/dist/rules/wgl018.ts +39 -0
  6. package/dist/rules/wgl019.ts +44 -0
  7. package/dist/rules/wgl020.ts +56 -0
  8. package/dist/rules/wgl021.ts +62 -0
  9. package/dist/rules/wgl022.ts +44 -0
  10. package/dist/rules/wgl023.ts +51 -0
  11. package/dist/rules/wgl024.ts +46 -0
  12. package/dist/rules/wgl025.ts +49 -0
  13. package/dist/rules/wgl026.ts +67 -0
  14. package/dist/rules/wgl027.ts +54 -0
  15. package/dist/rules/wgl028.ts +67 -0
  16. package/dist/rules/yaml-helpers.ts +82 -0
  17. package/dist/skills/chant-gitlab.md +1 -1
  18. package/dist/skills/gitlab-ci-patterns.md +309 -0
  19. package/package.json +3 -3
  20. package/src/codegen/fetch.test.ts +30 -0
  21. package/src/codegen/generate.test.ts +65 -0
  22. package/src/codegen/idempotency.test.ts +28 -0
  23. package/src/codegen/naming.test.ts +93 -0
  24. package/src/codegen/snapshot.test.ts +28 -19
  25. package/src/composites/composites.test.ts +160 -0
  26. package/src/coverage.test.ts +15 -7
  27. package/src/import/roundtrip.test.ts +132 -0
  28. package/src/lint/post-synth/wgl016.test.ts +72 -0
  29. package/src/lint/post-synth/wgl016.ts +82 -0
  30. package/src/lint/post-synth/wgl017.test.ts +53 -0
  31. package/src/lint/post-synth/wgl017.ts +54 -0
  32. package/src/lint/post-synth/wgl018.test.ts +69 -0
  33. package/src/lint/post-synth/wgl018.ts +39 -0
  34. package/src/lint/post-synth/wgl019.test.ts +76 -0
  35. package/src/lint/post-synth/wgl019.ts +44 -0
  36. package/src/lint/post-synth/wgl020.test.ts +54 -0
  37. package/src/lint/post-synth/wgl020.ts +56 -0
  38. package/src/lint/post-synth/wgl021.test.ts +62 -0
  39. package/src/lint/post-synth/wgl021.ts +62 -0
  40. package/src/lint/post-synth/wgl022.test.ts +86 -0
  41. package/src/lint/post-synth/wgl022.ts +44 -0
  42. package/src/lint/post-synth/wgl023.test.ts +88 -0
  43. package/src/lint/post-synth/wgl023.ts +51 -0
  44. package/src/lint/post-synth/wgl024.test.ts +77 -0
  45. package/src/lint/post-synth/wgl024.ts +46 -0
  46. package/src/lint/post-synth/wgl025.test.ts +85 -0
  47. package/src/lint/post-synth/wgl025.ts +49 -0
  48. package/src/lint/post-synth/wgl026.test.ts +87 -0
  49. package/src/lint/post-synth/wgl026.ts +67 -0
  50. package/src/lint/post-synth/wgl027.test.ts +84 -0
  51. package/src/lint/post-synth/wgl027.ts +54 -0
  52. package/src/lint/post-synth/wgl028.test.ts +95 -0
  53. package/src/lint/post-synth/wgl028.ts +67 -0
  54. package/src/lint/post-synth/yaml-helpers.ts +82 -0
  55. package/src/lsp/completions.test.ts +16 -6
  56. package/src/lsp/hover.test.ts +18 -7
  57. package/src/plugin.test.ts +15 -2
  58. package/src/plugin.ts +66 -3
  59. package/src/skills/gitlab-ci-patterns.md +309 -0
  60. package/src/testdata/pipelines/deploy-envs.gitlab-ci.yml +60 -0
  61. package/src/testdata/pipelines/docker-build.gitlab-ci.yml +41 -0
  62. package/src/testdata/pipelines/includes-templates.gitlab-ci.yml +52 -0
  63. package/src/testdata/pipelines/monorepo.gitlab-ci.yml +51 -0
  64. package/src/testdata/pipelines/multi-stage.gitlab-ci.yml +56 -0
  65. package/src/testdata/pipelines/simple.gitlab-ci.yml +9 -0
  66. package/src/validate.test.ts +12 -6
  67. package/src/variables.test.ts +58 -0
@@ -0,0 +1,309 @@
1
+ ---
2
+ skill: gitlab-ci-patterns
3
+ description: GitLab CI/CD pipeline stages, caching, artifacts, includes, and advanced patterns
4
+ user-invocable: true
5
+ ---
6
+
7
+ # GitLab CI/CD Pipeline Patterns
8
+
9
+ ## Pipeline Stage Design
10
+
11
+ ### Standard Stage Ordering
12
+
13
+ ```typescript
14
+ import { Job, Image, Cache, Artifacts } from "@intentius/chant-lexicon-gitlab";
15
+
16
+ // Stages execute in order. Jobs within a stage run in parallel.
17
+ // Default stages: .pre, build, test, deploy, .post
18
+
19
+ export const lint = new Job({
20
+ stage: "build",
21
+ image: new Image({ name: "node:22-alpine" }),
22
+ script: ["npm ci", "npm run lint"],
23
+ });
24
+
25
+ export const test = new Job({
26
+ stage: "test",
27
+ image: new Image({ name: "node:22-alpine" }),
28
+ script: ["npm ci", "npm test"],
29
+ });
30
+
31
+ export const deploy = new Job({
32
+ stage: "deploy",
33
+ script: ["./deploy.sh"],
34
+ rules: [{ if: '$CI_COMMIT_BRANCH == "main"' }],
35
+ });
36
+ ```
37
+
38
+ ### Parallel Jobs with needs
39
+
40
+ Use `needs` to create a DAG and skip waiting for the full stage:
41
+
42
+ ```typescript
43
+ export const unitTests = new Job({
44
+ stage: "test",
45
+ script: ["npm run test:unit"],
46
+ needs: ["build"],
47
+ });
48
+
49
+ export const integrationTests = new Job({
50
+ stage: "test",
51
+ script: ["npm run test:integration"],
52
+ needs: ["build"],
53
+ });
54
+
55
+ export const deploy = new Job({
56
+ stage: "deploy",
57
+ script: ["./deploy.sh"],
58
+ needs: ["unitTests", "integrationTests"],
59
+ });
60
+ ```
61
+
62
+ ## Caching Strategies
63
+
64
+ ### Language-Specific Cache Keys
65
+
66
+ ```typescript
67
+ import { Cache } from "@intentius/chant-lexicon-gitlab";
68
+
69
+ // Node.js: cache node_modules by lockfile hash
70
+ export const nodeCache = new Cache({
71
+ key: { files: ["package-lock.json"] },
72
+ paths: ["node_modules/"],
73
+ policy: "pull-push",
74
+ });
75
+
76
+ // Python: cache pip downloads
77
+ export const pipCache = new Cache({
78
+ key: { files: ["requirements.txt"] },
79
+ paths: [".pip-cache/"],
80
+ policy: "pull-push",
81
+ });
82
+ ```
83
+
84
+ ### Cache Policies
85
+
86
+ | Policy | Behavior | Use for |
87
+ |--------|----------|---------|
88
+ | `pull-push` | Download and upload cache | Build jobs that install dependencies |
89
+ | `pull` | Download only, never upload | Test/deploy jobs (read from build cache) |
90
+ | `push` | Upload only, never download | Initial cache population |
91
+
92
+ ```typescript
93
+ export const build = new Job({
94
+ stage: "build",
95
+ cache: new Cache({
96
+ key: "$CI_COMMIT_REF_SLUG",
97
+ paths: ["node_modules/", "dist/"],
98
+ policy: "pull-push", // build populates cache
99
+ }),
100
+ script: ["npm ci", "npm run build"],
101
+ });
102
+
103
+ export const test = new Job({
104
+ stage: "test",
105
+ cache: new Cache({
106
+ key: "$CI_COMMIT_REF_SLUG",
107
+ paths: ["node_modules/"],
108
+ policy: "pull", // test only reads cache
109
+ }),
110
+ script: ["npm test"],
111
+ });
112
+ ```
113
+
114
+ ## Artifacts
115
+
116
+ ### Pass Build Output Between Jobs
117
+
118
+ ```typescript
119
+ import { Artifacts } from "@intentius/chant-lexicon-gitlab";
120
+
121
+ export const buildArtifacts = new Artifacts({
122
+ paths: ["dist/"],
123
+ expire_in: "1 day",
124
+ });
125
+
126
+ export const testReports = new Artifacts({
127
+ reports: { junit: "coverage/junit.xml", coverage_report: { coverage_format: "cobertura", path: "coverage/cobertura.xml" } },
128
+ paths: ["coverage/"],
129
+ expire_in: "1 week",
130
+ });
131
+
132
+ export const build = new Job({
133
+ stage: "build",
134
+ script: ["npm ci", "npm run build"],
135
+ artifacts: buildArtifacts,
136
+ });
137
+
138
+ export const test = new Job({
139
+ stage: "test",
140
+ script: ["npm ci", "npm test -- --coverage"],
141
+ artifacts: testReports,
142
+ needs: ["build"],
143
+ });
144
+ ```
145
+
146
+ ### Artifact Types
147
+
148
+ | Type | Purpose | GitLab feature |
149
+ |------|---------|----------------|
150
+ | `junit` | Test results | MR test report widget |
151
+ | `coverage_report` | Code coverage | MR coverage visualization |
152
+ | `dotenv` | Export variables | Pass variables to downstream jobs |
153
+ | `terraform` | Terraform plans | MR Terraform widget |
154
+
155
+ ## Include Patterns
156
+
157
+ ### Reusable Pipeline Components
158
+
159
+ ```typescript
160
+ import { Include } from "@intentius/chant-lexicon-gitlab";
161
+
162
+ // Include from same project
163
+ export const localInclude = new Include({
164
+ local: ".gitlab/ci/deploy.yml",
165
+ });
166
+
167
+ // Include from another project
168
+ export const projectInclude = new Include({
169
+ project: "devops/pipeline-templates",
170
+ ref: "main",
171
+ file: "/templates/docker-build.yml",
172
+ });
173
+
174
+ // Include from remote URL
175
+ export const remoteInclude = new Include({
176
+ remote: "https://example.com/ci-templates/security-scan.yml",
177
+ });
178
+
179
+ // Include a GitLab CI template
180
+ export const templateInclude = new Include({
181
+ template: "Security/SAST.gitlab-ci.yml",
182
+ });
183
+ ```
184
+
185
+ ### Composites for Common Pipelines
186
+
187
+ Use composites instead of raw includes for type-safe pipeline generation:
188
+
189
+ ```typescript
190
+ import { NodePipeline } from "@intentius/chant-lexicon-gitlab";
191
+
192
+ export const app = NodePipeline({
193
+ nodeVersion: "22",
194
+ installCommand: "npm ci",
195
+ buildScript: "build",
196
+ testScript: "test",
197
+ });
198
+ ```
199
+
200
+ ## Rules and Conditional Execution
201
+
202
+ ### Branch-Based Rules
203
+
204
+ ```typescript
205
+ export const deployStaging = new Job({
206
+ stage: "deploy",
207
+ script: ["./deploy.sh staging"],
208
+ rules: [
209
+ { if: '$CI_COMMIT_BRANCH == "develop"', when: "on_success" },
210
+ ],
211
+ });
212
+
213
+ export const deployProd = new Job({
214
+ stage: "deploy",
215
+ script: ["./deploy.sh production"],
216
+ rules: [
217
+ { if: '$CI_COMMIT_BRANCH == "main"', when: "manual" },
218
+ ],
219
+ });
220
+ ```
221
+
222
+ ### MR vs Branch Pipelines
223
+
224
+ ```typescript
225
+ // Run on merge requests only
226
+ export const mrTest = new Job({
227
+ stage: "test",
228
+ script: ["npm test"],
229
+ rules: [{ if: "$CI_MERGE_REQUEST_IID" }],
230
+ });
231
+
232
+ // Run on default branch only
233
+ export const release = new Job({
234
+ stage: "deploy",
235
+ script: ["npm publish"],
236
+ rules: [{ if: '$CI_COMMIT_BRANCH == "main"' }],
237
+ });
238
+ ```
239
+
240
+ ## Review Apps
241
+
242
+ ### Deploy Per-MR Environments
243
+
244
+ ```typescript
245
+ import { ReviewApp } from "@intentius/chant-lexicon-gitlab";
246
+
247
+ export const review = ReviewApp({
248
+ name: "review",
249
+ deployScript: "kubectl apply -f manifests.yaml",
250
+ stopScript: "kubectl delete -f manifests.yaml",
251
+ autoStopIn: "1 week",
252
+ });
253
+ ```
254
+
255
+ This generates a deploy job with `environment` and a stop job with `action: stop` that triggers when the MR is merged or closed.
256
+
257
+ ## Docker Build Pattern
258
+
259
+ ### Multi-Stage Build and Push
260
+
261
+ ```typescript
262
+ import { DockerBuild } from "@intentius/chant-lexicon-gitlab";
263
+
264
+ export const docker = DockerBuild({
265
+ dockerfile: "Dockerfile",
266
+ context: ".",
267
+ tagLatest: true,
268
+ registry: "$CI_REGISTRY",
269
+ imageName: "$CI_REGISTRY_IMAGE",
270
+ });
271
+ ```
272
+
273
+ This generates a job using Docker-in-Docker (`dind`) service with proper `DOCKER_TLS_CERTDIR` configuration.
274
+
275
+ ## Matrix Builds
276
+
277
+ ### Test Across Multiple Versions
278
+
279
+ ```typescript
280
+ export const test = new Job({
281
+ stage: "test",
282
+ parallel: {
283
+ matrix: [
284
+ { NODE_VERSION: ["18", "20", "22"] },
285
+ ],
286
+ },
287
+ image: new Image({ name: "node:${NODE_VERSION}-alpine" }),
288
+ script: ["npm ci", "npm test"],
289
+ });
290
+ ```
291
+
292
+ ## Pipeline Security
293
+
294
+ ### Protected Variables
295
+
296
+ Use protected variables for production secrets. They are only available on protected branches/tags:
297
+
298
+ ```typescript
299
+ export const deploy = new Job({
300
+ stage: "deploy",
301
+ script: ["./deploy.sh"],
302
+ variables: { DEPLOY_ENV: "production" },
303
+ rules: [
304
+ { if: '$CI_COMMIT_BRANCH == "main"', when: "manual" },
305
+ ],
306
+ });
307
+ ```
308
+
309
+ Set `DEPLOY_TOKEN` as a protected, masked variable in project settings.
@@ -0,0 +1,60 @@
1
+ stages:
2
+ - build
3
+ - deploy
4
+ - cleanup
5
+
6
+ build:
7
+ stage: build
8
+ script:
9
+ - npm ci
10
+ - npm run build
11
+ artifacts:
12
+ paths:
13
+ - dist/
14
+
15
+ deploy-review:
16
+ stage: deploy
17
+ script:
18
+ - deploy-review.sh
19
+ environment:
20
+ name: review/$CI_COMMIT_REF_SLUG
21
+ url: https://$CI_ENVIRONMENT_SLUG.review.example.com
22
+ on_stop: stop-review
23
+ auto_stop_in: 1 week
24
+ resource_group: review/$CI_COMMIT_REF_SLUG
25
+ rules:
26
+ - if: $CI_MERGE_REQUEST_IID
27
+
28
+ stop-review:
29
+ stage: cleanup
30
+ script:
31
+ - teardown-review.sh
32
+ environment:
33
+ name: review/$CI_COMMIT_REF_SLUG
34
+ action: stop
35
+ rules:
36
+ - if: $CI_MERGE_REQUEST_IID
37
+ when: manual
38
+ needs:
39
+ - deploy-review
40
+
41
+ deploy-staging:
42
+ stage: deploy
43
+ script:
44
+ - deploy.sh staging
45
+ environment:
46
+ name: staging
47
+ url: https://staging.example.com
48
+ rules:
49
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
50
+
51
+ deploy-production:
52
+ stage: deploy
53
+ script:
54
+ - deploy.sh production
55
+ environment:
56
+ name: production
57
+ url: https://example.com
58
+ rules:
59
+ - if: $CI_COMMIT_TAG
60
+ when: manual
@@ -0,0 +1,41 @@
1
+ stages:
2
+ - build
3
+ - push
4
+
5
+ variables:
6
+ DOCKER_TLS_CERTDIR: "/certs"
7
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
8
+
9
+ build-image:
10
+ stage: build
11
+ image: docker:27-cli
12
+ services:
13
+ - name: docker:27-dind
14
+ alias: docker
15
+ variables:
16
+ DOCKER_HOST: tcp://docker:2376
17
+ before_script:
18
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
19
+ script:
20
+ - docker build -t $IMAGE_TAG .
21
+ - docker push $IMAGE_TAG
22
+ rules:
23
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
24
+ - if: $CI_MERGE_REQUEST_IID
25
+
26
+ push-latest:
27
+ stage: push
28
+ image: docker:27-cli
29
+ services:
30
+ - name: docker:27-dind
31
+ alias: docker
32
+ needs:
33
+ - build-image
34
+ before_script:
35
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
36
+ script:
37
+ - docker pull $IMAGE_TAG
38
+ - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
39
+ - docker push $CI_REGISTRY_IMAGE:latest
40
+ rules:
41
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
@@ -0,0 +1,52 @@
1
+ include:
2
+ - template: Auto-DevOps.gitlab-ci.yml
3
+ - project: my-group/ci-templates
4
+ ref: main
5
+ file: /templates/build.yml
6
+
7
+ stages:
8
+ - build
9
+ - test
10
+ - deploy
11
+
12
+ default:
13
+ image: node:22-alpine
14
+ interruptible: true
15
+ retry:
16
+ max: 2
17
+ when:
18
+ - runner_system_failure
19
+ - stuck_or_timeout_failure
20
+
21
+ .test-template: &test-defaults
22
+ stage: test
23
+ before_script:
24
+ - npm ci
25
+
26
+ build:
27
+ stage: build
28
+ extends: .build-template
29
+ script:
30
+ - npm ci
31
+ - npm run build
32
+
33
+ unit-test:
34
+ <<: *test-defaults
35
+ script:
36
+ - npm test
37
+
38
+ integration-test:
39
+ <<: *test-defaults
40
+ script:
41
+ - npm run test:integration
42
+ rules:
43
+ - if: $CI_MERGE_REQUEST_IID
44
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
45
+
46
+ deploy:
47
+ stage: deploy
48
+ extends: .deploy-template
49
+ script:
50
+ - deploy.sh
51
+ rules:
52
+ - if: $CI_COMMIT_TAG
@@ -0,0 +1,51 @@
1
+ stages:
2
+ - build
3
+ - test
4
+ - deploy
5
+
6
+ variables:
7
+ GIT_DEPTH: 20
8
+
9
+ frontend:
10
+ stage: build
11
+ trigger:
12
+ include: frontend/.gitlab-ci.yml
13
+ strategy: depend
14
+ rules:
15
+ - changes:
16
+ - frontend/**/*
17
+
18
+ backend:
19
+ stage: build
20
+ trigger:
21
+ include: backend/.gitlab-ci.yml
22
+ strategy: depend
23
+ rules:
24
+ - changes:
25
+ - backend/**/*
26
+
27
+ e2e-matrix:
28
+ stage: test
29
+ image: cypress/included:13
30
+ needs:
31
+ - frontend
32
+ - backend
33
+ parallel:
34
+ matrix:
35
+ - BROWSER: [chrome, firefox]
36
+ VIEWPORT: [desktop, mobile]
37
+ script:
38
+ - cypress run --browser $BROWSER --config viewportPreset=$VIEWPORT
39
+ artifacts:
40
+ when: always
41
+ paths:
42
+ - cypress/screenshots/
43
+ - cypress/videos/
44
+ expire_in: 3 days
45
+
46
+ deploy-all:
47
+ stage: deploy
48
+ script:
49
+ - deploy.sh
50
+ rules:
51
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
@@ -0,0 +1,56 @@
1
+ stages:
2
+ - build
3
+ - test
4
+ - deploy
5
+
6
+ build-app:
7
+ stage: build
8
+ image: node:22-alpine
9
+ script:
10
+ - npm ci
11
+ - npm run build
12
+ artifacts:
13
+ paths:
14
+ - dist/
15
+ expire_in: 1 hour
16
+ cache:
17
+ key:
18
+ files:
19
+ - package-lock.json
20
+ paths:
21
+ - .npm/
22
+
23
+ lint:
24
+ stage: test
25
+ image: node:22-alpine
26
+ needs:
27
+ - build-app
28
+ script:
29
+ - npm ci
30
+ - npm run lint
31
+
32
+ unit-tests:
33
+ stage: test
34
+ image: node:22-alpine
35
+ needs:
36
+ - build-app
37
+ script:
38
+ - npm ci
39
+ - npm test -- --coverage
40
+ artifacts:
41
+ reports:
42
+ junit: junit.xml
43
+ coverage_report:
44
+ coverage_format: cobertura
45
+ path: coverage/cobertura-coverage.xml
46
+
47
+ deploy-staging:
48
+ stage: deploy
49
+ needs:
50
+ - lint
51
+ - unit-tests
52
+ script:
53
+ - deploy.sh staging
54
+ environment:
55
+ name: staging
56
+ url: https://staging.example.com
@@ -0,0 +1,9 @@
1
+ stages:
2
+ - test
3
+
4
+ unit-test:
5
+ stage: test
6
+ image: node:22-alpine
7
+ script:
8
+ - npm ci
9
+ - npm test
@@ -1,31 +1,37 @@
1
1
  import { describe, test, expect } from "bun:test";
2
- import { validate } from "./validate";
3
- import { dirname } from "path";
2
+ import { existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
 
6
6
  const basePath = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ const lexiconPath = join(basePath, "src", "generated", "lexicon-gitlab.json");
8
+ const hasGenerated = existsSync(lexiconPath);
7
9
 
8
10
  describe("validate", () => {
9
- test("runs validation checks on current generated artifacts", async () => {
11
+ test.skipIf(!hasGenerated)("runs validation checks on current generated artifacts", async () => {
12
+ const { validate } = await import("./validate");
10
13
  const result = await validate({ basePath });
11
14
  expect(result.checks.length).toBeGreaterThan(0);
12
15
  });
13
16
 
14
- test("checks lexicon JSON exists and parses", async () => {
17
+ test.skipIf(!hasGenerated)("checks lexicon JSON exists and parses", async () => {
18
+ const { validate } = await import("./validate");
15
19
  const result = await validate({ basePath });
16
20
  const jsonCheck = result.checks.find((c) => c.name === "lexicon-json-exists");
17
21
  expect(jsonCheck).toBeDefined();
18
22
  expect(jsonCheck?.ok).toBe(true);
19
23
  });
20
24
 
21
- test("checks types exist", async () => {
25
+ test.skipIf(!hasGenerated)("checks types exist", async () => {
26
+ const { validate } = await import("./validate");
22
27
  const result = await validate({ basePath });
23
28
  const typesCheck = result.checks.find((c) => c.name === "types-exist");
24
29
  expect(typesCheck).toBeDefined();
25
30
  expect(typesCheck?.ok).toBe(true);
26
31
  });
27
32
 
28
- test("checks required names are present", async () => {
33
+ test.skipIf(!hasGenerated)("checks required names are present", async () => {
34
+ const { validate } = await import("./validate");
29
35
  const result = await validate({ basePath });
30
36
  const requiredCheck = result.checks.find((c) => c.name === "required-names");
31
37
  expect(requiredCheck).toBeDefined();
@@ -0,0 +1,58 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { CI } from "./variables";
3
+
4
+ describe("CI variables", () => {
5
+ test("all values start with $CI_", () => {
6
+ for (const [key, value] of Object.entries(CI)) {
7
+ expect(value).toMatch(/^\$CI_/);
8
+ }
9
+ });
10
+
11
+ test("all values are non-empty strings", () => {
12
+ for (const [key, value] of Object.entries(CI)) {
13
+ expect(typeof value).toBe("string");
14
+ expect(value.length).toBeGreaterThan(0);
15
+ }
16
+ });
17
+
18
+ test("contains expected commit variables", () => {
19
+ expect(CI.CommitBranch).toBe("$CI_COMMIT_BRANCH");
20
+ expect(CI.CommitRef).toBe("$CI_COMMIT_REF_NAME");
21
+ expect(CI.CommitRefSlug).toBe("$CI_COMMIT_REF_SLUG");
22
+ expect(CI.CommitSha).toBe("$CI_COMMIT_SHA");
23
+ expect(CI.CommitTag).toBe("$CI_COMMIT_TAG");
24
+ });
25
+
26
+ test("contains expected project variables", () => {
27
+ expect(CI.ProjectDir).toBe("$CI_PROJECT_DIR");
28
+ expect(CI.ProjectId).toBe("$CI_PROJECT_ID");
29
+ expect(CI.ProjectName).toBe("$CI_PROJECT_NAME");
30
+ expect(CI.ProjectPath).toBe("$CI_PROJECT_PATH");
31
+ });
32
+
33
+ test("contains expected pipeline variables", () => {
34
+ expect(CI.PipelineId).toBe("$CI_PIPELINE_ID");
35
+ expect(CI.PipelineSource).toBe("$CI_PIPELINE_SOURCE");
36
+ expect(CI.JobId).toBe("$CI_JOB_ID");
37
+ expect(CI.JobName).toBe("$CI_JOB_NAME");
38
+ expect(CI.JobStage).toBe("$CI_JOB_STAGE");
39
+ });
40
+
41
+ test("contains expected registry variables", () => {
42
+ expect(CI.Registry).toBe("$CI_REGISTRY");
43
+ expect(CI.RegistryImage).toBe("$CI_REGISTRY_IMAGE");
44
+ expect(CI.RegistryUser).toBe("$CI_REGISTRY_USER");
45
+ expect(CI.RegistryPassword).toBe("$CI_REGISTRY_PASSWORD");
46
+ });
47
+
48
+ test("contains environment variables", () => {
49
+ expect(CI.Environment).toBe("$CI_ENVIRONMENT_NAME");
50
+ expect(CI.EnvironmentSlug).toBe("$CI_ENVIRONMENT_SLUG");
51
+ expect(CI.DefaultBranch).toBe("$CI_DEFAULT_BRANCH");
52
+ expect(CI.MergeRequestIid).toBe("$CI_MERGE_REQUEST_IID");
53
+ });
54
+
55
+ test("exports at least 20 variables", () => {
56
+ expect(Object.keys(CI).length).toBeGreaterThanOrEqual(20);
57
+ });
58
+ });