@intentius/chant-lexicon-docker 0.124.21 → 0.124.22

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 (106) hide show
  1. package/dist/codegen/docs.d.ts.map +1 -1
  2. package/dist/codegen/generate.d.ts.map +1 -1
  3. package/dist/dockerfile-order.d.ts +13 -0
  4. package/dist/dockerfile-order.d.ts.map +1 -0
  5. package/dist/generated/index.d.ts +121 -0
  6. package/dist/generated/index.d.ts.map +1 -1
  7. package/dist/import/adapter.d.ts.map +1 -1
  8. package/dist/import/parser.d.ts +23 -0
  9. package/dist/import/parser.d.ts.map +1 -1
  10. package/dist/integrity.json +17 -8
  11. package/dist/lint/audit-catalog.d.ts.map +1 -1
  12. package/dist/lint/audit-lineage.d.ts.map +1 -1
  13. package/dist/lint/post-synth/add-url-checksum.d.ts +9 -0
  14. package/dist/lint/post-synth/add-url-checksum.d.ts.map +1 -0
  15. package/dist/lint/post-synth/apt-update-alone.d.ts +10 -0
  16. package/dist/lint/post-synth/apt-update-alone.d.ts.map +1 -0
  17. package/dist/lint/post-synth/cache-busting-copy.d.ts +11 -0
  18. package/dist/lint/post-synth/cache-busting-copy.d.ts.map +1 -0
  19. package/dist/lint/post-synth/copy-from-stage.d.ts +10 -0
  20. package/dist/lint/post-synth/copy-from-stage.d.ts.map +1 -0
  21. package/dist/lint/post-synth/docker-helpers.d.ts +35 -0
  22. package/dist/lint/post-synth/docker-helpers.d.ts.map +1 -1
  23. package/dist/lint/post-synth/index.d.ts.map +1 -1
  24. package/dist/lint/post-synth/no-root-user.d.ts +3 -2
  25. package/dist/lint/post-synth/no-root-user.d.ts.map +1 -1
  26. package/dist/lint/post-synth/relative-workdir.d.ts +10 -0
  27. package/dist/lint/post-synth/relative-workdir.d.ts.map +1 -0
  28. package/dist/lint/post-synth/repeated-cmd.d.ts +9 -0
  29. package/dist/lint/post-synth/repeated-cmd.d.ts.map +1 -0
  30. package/dist/lint/post-synth/root-after-user.d.ts +10 -0
  31. package/dist/lint/post-synth/root-after-user.d.ts.map +1 -0
  32. package/dist/lint/post-synth/unused-global-arg.d.ts +10 -0
  33. package/dist/lint/post-synth/unused-global-arg.d.ts.map +1 -0
  34. package/dist/lint/rules/index.d.ts +1 -0
  35. package/dist/lint/rules/index.d.ts.map +1 -1
  36. package/dist/lint/rules/per-kind-order.d.ts +15 -0
  37. package/dist/lint/rules/per-kind-order.d.ts.map +1 -0
  38. package/dist/manifest.json +1 -1
  39. package/dist/meta.json +6 -0
  40. package/dist/okf/index.md +10 -1
  41. package/dist/okf/rules/DKRD012.md +2 -2
  42. package/dist/okf/rules/DKRD013.md +11 -0
  43. package/dist/okf/rules/DKRD014.md +11 -0
  44. package/dist/okf/rules/DKRD015.md +11 -0
  45. package/dist/okf/rules/DKRD016.md +11 -0
  46. package/dist/okf/rules/DKRD017.md +11 -0
  47. package/dist/okf/rules/DKRD018.md +11 -0
  48. package/dist/okf/rules/DKRD019.md +11 -0
  49. package/dist/okf/rules/DKRD020.md +11 -0
  50. package/dist/okf/rules/DKRS002.md +15 -0
  51. package/dist/okf/types/Dockerfile.md +4 -1
  52. package/dist/plugin.d.ts.map +1 -1
  53. package/dist/rules/add-url-checksum.ts +38 -0
  54. package/dist/rules/apt-update-alone.ts +39 -0
  55. package/dist/rules/cache-busting-copy.ts +64 -0
  56. package/dist/rules/copy-from-stage.ts +54 -0
  57. package/dist/rules/docker-helpers.ts +80 -0
  58. package/dist/rules/no-root-user.ts +19 -17
  59. package/dist/rules/per-kind-order.ts +130 -0
  60. package/dist/rules/relative-workdir.ts +38 -0
  61. package/dist/rules/repeated-cmd.ts +35 -0
  62. package/dist/rules/root-after-user.ts +44 -0
  63. package/dist/rules/unused-global-arg.ts +38 -0
  64. package/dist/serializer.d.ts.map +1 -1
  65. package/dist/skills/chant-docker-patterns.md +91 -22
  66. package/dist/skills/chant-docker.md +42 -16
  67. package/dist/spec/parse-engine.d.ts +9 -0
  68. package/dist/spec/parse-engine.d.ts.map +1 -1
  69. package/dist/types/index.d.ts +81 -0
  70. package/package.json +3 -3
  71. package/src/codegen/__snapshots__/okf.test.ts.snap +155 -4
  72. package/src/codegen/docs.ts +14 -0
  73. package/src/codegen/generate.ts +77 -19
  74. package/src/dockerfile-order.ts +20 -0
  75. package/src/docs-examples.test.ts +141 -0
  76. package/src/generated/index.d.ts +81 -0
  77. package/src/generated/index.ts +117 -0
  78. package/src/generated/lexicon-docker.json +6 -0
  79. package/src/import/adapter.ts +8 -2
  80. package/src/import/generator.ts +49 -33
  81. package/src/import/parser.ts +82 -13
  82. package/src/import/roundtrip.test.ts +105 -0
  83. package/src/lint/audit-catalog.ts +9 -1
  84. package/src/lint/audit-lineage.ts +14 -0
  85. package/src/lint/post-synth/add-url-checksum.ts +38 -0
  86. package/src/lint/post-synth/apt-update-alone.ts +39 -0
  87. package/src/lint/post-synth/cache-busting-copy.ts +64 -0
  88. package/src/lint/post-synth/copy-from-stage.ts +54 -0
  89. package/src/lint/post-synth/docker-helpers.ts +80 -0
  90. package/src/lint/post-synth/index.ts +16 -0
  91. package/src/lint/post-synth/no-root-user.ts +19 -17
  92. package/src/lint/post-synth/post-synth.test.ts +149 -0
  93. package/src/lint/post-synth/relative-workdir.ts +38 -0
  94. package/src/lint/post-synth/repeated-cmd.ts +35 -0
  95. package/src/lint/post-synth/root-after-user.ts +44 -0
  96. package/src/lint/post-synth/unused-global-arg.ts +38 -0
  97. package/src/lint/rules/index.ts +1 -0
  98. package/src/lint/rules/per-kind-order.ts +130 -0
  99. package/src/lint/rules/rules.test.ts +88 -0
  100. package/src/plugin.test.ts +4 -0
  101. package/src/plugin.ts +2 -1
  102. package/src/serializer.test.ts +112 -0
  103. package/src/serializer.ts +58 -11
  104. package/src/skills/chant-docker-patterns.md +91 -22
  105. package/src/skills/chant-docker.md +42 -16
  106. package/src/spec/parse-engine.ts +17 -5
@@ -0,0 +1,44 @@
1
+ /**
2
+ * DKRD014: Root-only command after a non-root USER
3
+ *
4
+ * A RUN that installs packages or manages users and ownership (apt-get,
5
+ * apk add, useradd, chown, ...) fails or misbehaves after USER switches to
6
+ * an unprivileged user. Run root setup first, then switch USER.
7
+ */
8
+
9
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
+ import { commandText, eachDockerfile, instrText, isRootUser, stageLabel } from "./docker-helpers";
11
+
12
+ const ROOT_ONLY = /(^|[\s;&|(])(apt-get\s+(install|update|upgrade|dist-upgrade|remove|purge)|apt\s+(install|update|upgrade)|apk\s+(add|update|upgrade|del)|yum\s+(install|update)|dnf\s+(install|update|upgrade)|microdnf\s+install|zypper\s+(install|in|refresh)|useradd|adduser|groupadd|addgroup|usermod|chown)\b/;
13
+
14
+ export const dkrd014: PostSynthCheck = {
15
+ id: "DKRD014",
16
+ description: "RUN needs root but comes after USER switched to a non-root user",
17
+
18
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
19
+ const diagnostics: PostSynthDiagnostic[] = [];
20
+
21
+ for (const { fileName, parsed } of eachDockerfile(ctx.outputs)) {
22
+ for (const stage of parsed.stages) {
23
+ let user: string | undefined;
24
+ for (const instr of stage.instructions) {
25
+ if (instr.instruction === "USER") {
26
+ user = instr.value.trim();
27
+ continue;
28
+ }
29
+ if (instr.instruction !== "RUN" || user === undefined || isRootUser(user)) continue;
30
+ const cmd = commandText(instr);
31
+ if (/\bsudo\b/.test(cmd) || !ROOT_ONLY.test(cmd)) continue;
32
+ diagnostics.push({
33
+ checkId: "DKRD014",
34
+ severity: "warning",
35
+ message: `${fileName} ${stageLabel(stage)}: ${instrText(instr)} needs root but runs as USER ${user}. Move it before the USER step, or switch to USER root for it.`,
36
+ lexicon: "docker",
37
+ });
38
+ }
39
+ }
40
+ }
41
+
42
+ return diagnostics;
43
+ },
44
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * DKRD017: ARG before FROM that nothing uses
3
+ *
4
+ * An ARG declared before the first FROM is only visible to FROM lines, and
5
+ * to stages that declare it again (`ARG NAME`). One that neither uses does
6
+ * nothing.
7
+ */
8
+
9
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
+ import { eachDockerfile } from "./docker-helpers";
11
+
12
+ export const dkrd017: PostSynthCheck = {
13
+ id: "DKRD017",
14
+ description: "ARG before the first FROM is not used by any FROM or stage",
15
+
16
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
+ const diagnostics: PostSynthDiagnostic[] = [];
18
+
19
+ for (const { fileName, content, parsed } of eachDockerfile(ctx.outputs)) {
20
+ const fromLines = content.split("\n").filter((l) => /^\s*FROM\s/i.test(l)).join("\n");
21
+ for (const arg of parsed.globalArgs) {
22
+ const name = arg.value.trim().split("=")[0]!.trim();
23
+ if (!name) continue;
24
+ const used = new RegExp(`\\$\\{?${name}\\b`).test(fromLines);
25
+ const redeclared = parsed.stages.some((s) => s.instructions.some((i) => i.instruction === "ARG" && i.value.trim().split("=")[0]!.trim() === name));
26
+ if (used || redeclared) continue;
27
+ diagnostics.push({
28
+ checkId: "DKRD017",
29
+ severity: "warning",
30
+ message: `${fileName}: ARG ${name} is declared before FROM but no FROM uses it and no stage declares it again with ARG ${name}. Use it, declare it in the stage that needs it, or remove it.`,
31
+ lexicon: "docker",
32
+ });
33
+ }
34
+ }
35
+
36
+ return diagnostics;
37
+ },
38
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAoB,UAAU,EAAoB,MAAM,6BAA6B,CAAC;AA6SlG,eAAO,MAAM,gBAAgB,EAAE,UAqE9B,CAAC"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAoB,UAAU,EAAoB,MAAM,6BAA6B,CAAC;AA4VlG,eAAO,MAAM,gBAAgB,EAAE,UAqE9B,CAAC"}
@@ -53,55 +53,124 @@ export const nginx = new Service({
53
53
 
54
54
  ## Multi-stage Node.js Dockerfile
55
55
 
56
+ Manifests before source so the install layer is cached; the runtime stage takes only what it needs and runs as `node`.
57
+
56
58
  ```typescript
57
59
  export const nodeApp = new Dockerfile({
58
60
  stages: [
59
61
  {
60
62
  from: "node:20-alpine",
61
63
  as: "deps",
62
- workdir: "/app",
63
- copy: ["package*.json ./"],
64
- run: ["npm ci --only=production"],
64
+ steps: [
65
+ { workdir: "/app" },
66
+ { copy: "package.json package-lock.json ./" },
67
+ { run: "npm ci --omit=dev" },
68
+ ],
65
69
  },
66
70
  {
67
71
  from: "node:20-alpine",
68
72
  as: "build",
69
- workdir: "/app",
70
- copy: ["--from=deps /app/node_modules ./node_modules", ". ."],
71
- run: ["npm run build"],
73
+ steps: [
74
+ { workdir: "/app" },
75
+ { copy: "package.json package-lock.json ./" },
76
+ { run: "npm ci" },
77
+ { copy: ". ." },
78
+ { run: "npm run build" },
79
+ ],
72
80
  },
73
81
  {
74
82
  from: "node:20-alpine",
75
- workdir: "/app",
76
- copy: [
77
- "--from=deps /app/node_modules ./node_modules",
78
- "--from=build /app/dist ./dist",
83
+ steps: [
84
+ { workdir: "/app" },
85
+ { copy: "--from=deps /app/node_modules ./node_modules" },
86
+ { copy: "--from=build /app/dist ./dist" },
87
+ { user: "node" },
88
+ { expose: 3000 },
89
+ { cmd: ["node", "dist/index.js"] },
79
90
  ],
80
- user: "node",
81
- expose: ["3000"],
82
- cmd: `["node", "dist/index.js"]`,
83
91
  },
84
92
  ],
85
93
  });
86
94
  ```
87
95
 
88
- ## Python service with virtualenv
96
+ ## Python service
89
97
 
90
98
  ```typescript
91
99
  export const pythonApp = new Dockerfile({
92
100
  from: "python:3.12-slim",
93
- workdir: "/app",
94
- env: ["PYTHONUNBUFFERED=1", "PYTHONDONTWRITEBYTECODE=1"],
95
- copy: ["requirements.txt ."],
96
- run: [
97
- "pip install --no-cache-dir --no-deps -r requirements.txt",
101
+ steps: [
102
+ { workdir: "/app" },
103
+ { env: "PYTHONUNBUFFERED=1" },
104
+ { env: "PYTHONDONTWRITEBYTECODE=1" },
105
+ { copy: "requirements.txt ." },
106
+ { run: "pip install --no-cache-dir -r requirements.txt" },
107
+ { copy: ". ." },
108
+ { user: "1000:1000" },
109
+ { cmd: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] },
98
110
  ],
99
- copy: [". ."],
100
- user: "1000:1000",
101
- cmd: `["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]`,
102
111
  });
103
112
  ```
104
113
 
114
+ ## Root setup, then USER
115
+
116
+ ```typescript
117
+ export const tool = new Dockerfile({
118
+ from: "debian:12-slim",
119
+ steps: [
120
+ { run: "apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*" },
121
+ { run: "useradd --system --create-home app" },
122
+ { workdir: "/home/app" },
123
+ { copy: "--chown=app:app bin/ ./bin/" },
124
+ { user: "app" },
125
+ { entrypoint: ["./bin/tool"] },
126
+ ],
127
+ });
128
+ ```
129
+
130
+ ## Sharing stages and steps across modules
131
+
132
+ Export `const` stages and step lists from one module and import them into each Dockerfile. This is the chant answer to `INCLUDE`.
133
+
134
+ ```typescript
135
+ // src/shared/node.ts
136
+ import type { DockerfileStage, DockerfileStep } from "@intentius/chant-lexicon-docker";
137
+
138
+ export const nodeBuild: DockerfileStage = {
139
+ from: "node:20-alpine",
140
+ as: "build",
141
+ steps: [
142
+ { workdir: "/app" },
143
+ { copy: "package.json package-lock.json ./" },
144
+ { run: "npm ci" },
145
+ { copy: ". ." },
146
+ { run: "npm run build" },
147
+ { run: "npm prune --omit=dev" },
148
+ ],
149
+ };
150
+
151
+ export const nodeRuntime: DockerfileStep[] = [
152
+ { workdir: "/app" },
153
+ { copy: "--from=build /app/node_modules ./node_modules" },
154
+ { copy: "--from=build /app/dist ./dist" },
155
+ { user: "node" },
156
+ ];
157
+ ```
158
+
159
+ ```typescript
160
+ // src/web.ts
161
+ import { Dockerfile, type DockerfileStage } from "@intentius/chant-lexicon-docker";
162
+ import { nodeBuild, nodeRuntime } from "./shared/node";
163
+
164
+ const runtime: DockerfileStage = {
165
+ from: "node:20-alpine",
166
+ steps: [...nodeRuntime, { expose: 3000 }, { cmd: ["node", "dist/server.js"] }],
167
+ };
168
+
169
+ export const web = new Dockerfile({ stages: [nodeBuild, runtime] });
170
+ ```
171
+
172
+ For a shared Dockerfile that takes parameters, write a `Composite((props) => ({ image: new Dockerfile({...}) }))`; spreads are allowed inside it. The `dockerfile-sharing` example in the lexicon has all three patterns.
173
+
105
174
  ## Network isolation
106
175
 
107
176
  ```typescript
@@ -39,15 +39,21 @@ export const nfsdata = new Volume({ driver: "local", driver_opts: { type: "nfs",
39
39
 
40
40
  ## Dockerfile — generates Dockerfile.{name}:
41
41
 
42
+ Each stage is `from` plus `steps`. A step is an object with one key naming one instruction. Steps are written exactly in the order listed.
43
+
42
44
  ```typescript
43
- // Single-stage:
44
- export const builder = new Dockerfile({
45
+ // Single stage. Manifests before source, so the install layer is cached; root setup before USER.
46
+ export const server = new Dockerfile({
45
47
  from: "node:20-alpine",
46
- workdir: "/app",
47
- copy: ["package*.json ./"],
48
- run: ["npm ci --only=production"],
49
- user: "node",
50
- cmd: `["node", "dist/index.js"]`,
48
+ steps: [
49
+ { workdir: "/app" },
50
+ { copy: "package.json package-lock.json ./" },
51
+ { run: "npm ci --omit=dev" },
52
+ { copy: "src/ ./src/" },
53
+ { user: "node" },
54
+ { expose: 3000 },
55
+ { cmd: ["node", "src/index.js"] }, // a list is written in JSON exec form
56
+ ],
51
57
  });
52
58
 
53
59
  // Multi-stage build:
@@ -56,21 +62,36 @@ export const app = new Dockerfile({
56
62
  {
57
63
  from: "node:20-alpine",
58
64
  as: "build",
59
- workdir: "/app",
60
- copy: ["package*.json ./"],
61
- run: ["npm ci", "npm run build"],
65
+ steps: [
66
+ { workdir: "/app" },
67
+ { copy: "package.json package-lock.json ./" },
68
+ { run: "npm ci" },
69
+ { copy: ". ." },
70
+ { run: "npm run build" },
71
+ { run: "npm prune --omit=dev" },
72
+ ],
62
73
  },
63
74
  {
64
75
  from: "node:20-alpine",
65
- workdir: "/app",
66
- copy: ["--from=build /app/dist ./dist", "--from=build /app/node_modules ./node_modules"],
67
- user: "node",
68
- cmd: `["node", "dist/index.js"]`,
76
+ steps: [
77
+ { workdir: "/app" },
78
+ { copy: "--from=build /app/node_modules ./node_modules" },
79
+ { copy: "--from=build /app/dist ./dist" },
80
+ { user: "node" },
81
+ { cmd: ["node", "dist/index.js"] },
82
+ ],
69
83
  },
70
84
  ],
71
85
  });
72
86
  ```
73
87
 
88
+ Step keys: `arg`, `env`, `workdir`, `user`, `run`, `copy`, `add`, `expose`, `volume`, `label`, `entrypoint`, `cmd`, `healthcheck`, `shell`, `stopsignal`, `onbuild`. `from` and `as` are stage fields, and `globalArgs: ["NAME=value"]` writes `ARG` lines before the first `FROM`. `run`, `cmd`, `entrypoint` and `shell` take a string or a list (exec form); `expose` takes a number too.
89
+
90
+ Rules:
91
+ - Use `steps`. The older per-kind fields (`run: [...]`, `copy: [...]`, `user`) are written in a fixed order (ARG, ENV, WORKDIR, USER, RUN, COPY, ADD, EXPOSE, VOLUME, LABEL, ENTRYPOINT, CMD, HEALTHCHECK), not the order written, so every RUN lands before every COPY. Lint rule DKRS002 flags this, and `chant lint --fix` rewrites them as steps.
92
+ - A stage can't have both `steps` and per-kind fields, and a step can't name two instructions. Both are build errors.
93
+ - To share stages or steps between Dockerfiles in different modules, export a `const` stage (`DockerfileStage`) or step list (`DockerfileStep[]`) and import it. List the stage in `stages`, or spread the steps: `steps: [...nodeRuntime, { cmd: ["node", "dist/server.js"] }]`. This is chant's equivalent of an INCLUDE. Spreading a function call's result is a lint error (EVL004); put parameterized Dockerfiles in a `Composite`.
94
+
74
95
  ## Variable interpolation with env():
75
96
 
76
97
  ```typescript
@@ -112,7 +133,10 @@ export const api = new Service({
112
133
  },
113
134
  ports: ["8080:8080"],
114
135
  });
115
- export const builder = new Dockerfile({ from: "node:20-alpine", ... });
136
+ export const builder = new Dockerfile({
137
+ from: "node:20-alpine",
138
+ steps: [{ workdir: "/app" }, { copy: ". ." }, { run: "npm ci" }, { user: "node" }, { cmd: ["node", "index.js"] }],
139
+ });
116
140
  ```
117
141
 
118
142
  ## Output structure
@@ -126,4 +150,6 @@ export const builder = new Dockerfile({ from: "node:20-alpine", ... });
126
150
  - `DKRD001`: Post-synth: no `:latest` images in generated YAML
127
151
  - `DKRD002`: Post-synth: no unused named volumes
128
152
  - `DKRD003`: Post-synth: SSH port 22 not exposed externally
129
- - `DKRD010–012`: Dockerfile best practices (apt recommends, COPY vs ADD, USER)
153
+ - `DKRS002`: Dockerfile per-kind fields declared in an order the fixed order changes; use `steps`
154
+ - `DKRD010–012`: Dockerfile best practices (apt recommends, COPY vs ADD, final stage USER)
155
+ - `DKRD013–020`: order-aware Dockerfile checks: COPY . before a dependency install, root-only RUN after USER, repeated CMD/ENTRYPOINT, COPY --from an undefined stage, unused ARG before FROM, relative WORKDIR, ADD of a URL without --checksum, apt-get update in its own RUN
@@ -14,6 +14,15 @@ export interface DockerfileInstruction {
14
14
  name: string;
15
15
  description?: string;
16
16
  multi?: boolean;
17
+ /**
18
+ * Only available as a step (`{ shell: ... }` in `steps`), not as a per-kind
19
+ * field. The per-kind fields keep the set and fixed order they always had.
20
+ */
21
+ stepOnly?: boolean;
22
+ /** As a step, the value may be a JSON exec-form array as well as a string. */
23
+ execForm?: boolean;
24
+ /** As a step, the value may be a number as well as a string. */
25
+ numeric?: boolean;
17
26
  }
18
27
  /**
19
28
  * Parse the Docker Engine API YAML buffer for Dockerfile instruction types.
@@ -1 +1 @@
1
- {"version":3,"file":"parse-engine.d.ts","sourceRoot":"","sources":["../../src/spec/parse-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAsBnE"}
1
+ {"version":3,"file":"parse-engine.d.ts","sourceRoot":"","sources":["../../src/spec/parse-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAyBnE"}
@@ -56,39 +56,120 @@ export interface DockerSecretProps {
56
56
  }
57
57
  export declare const DockerSecret: new (props?: DockerSecretProps) => Declarable;
58
58
 
59
+ /** The Dockerfile instructions a step can name. FROM is the stage's own `from` field. */
60
+ export type DockerfileInstructionName = "arg" | "env" | "run" | "copy" | "add" | "workdir" | "user" | "expose" | "volume" | "label" | "entrypoint" | "cmd" | "healthcheck" | "shell" | "stopsignal" | "onbuild";
61
+ type DockerfileStepOf<K extends DockerfileInstructionName, V> = { [P in K]: V } & { [P in Exclude<DockerfileInstructionName, K>]?: never };
62
+ /**
63
+ * One Dockerfile instruction, named by its single key: `{ run: "npm ci" }` is `RUN npm ci`.
64
+ * A string[] value (run, cmd, entrypoint, shell) is written in JSON exec form.
65
+ */
66
+ export type DockerfileStep =
67
+ /** ARG: Build-time argument */
68
+ | DockerfileStepOf<"arg", string>
69
+ /** ENV: Environment variable */
70
+ | DockerfileStepOf<"env", string>
71
+ /** RUN: Run a command during build */
72
+ | DockerfileStepOf<"run", string | string[]>
73
+ /** COPY: Copy files from build context */
74
+ | DockerfileStepOf<"copy", string>
75
+ /** ADD: Add files (supports URLs and archives) */
76
+ | DockerfileStepOf<"add", string>
77
+ /** WORKDIR: Set working directory */
78
+ | DockerfileStepOf<"workdir", string>
79
+ /** USER: Set user for subsequent instructions */
80
+ | DockerfileStepOf<"user", string>
81
+ /** EXPOSE: Document exposed ports */
82
+ | DockerfileStepOf<"expose", string | number>
83
+ /** VOLUME: Create mount points */
84
+ | DockerfileStepOf<"volume", string>
85
+ /** LABEL: Add metadata labels */
86
+ | DockerfileStepOf<"label", string>
87
+ /** ENTRYPOINT: Set the entrypoint executable */
88
+ | DockerfileStepOf<"entrypoint", string | string[]>
89
+ /** CMD: Default command arguments */
90
+ | DockerfileStepOf<"cmd", string | string[]>
91
+ /** HEALTHCHECK: Health check instruction */
92
+ | DockerfileStepOf<"healthcheck", string>
93
+ /** SHELL: Set the shell for shell-form commands (JSON array form) */
94
+ | DockerfileStepOf<"shell", string | string[]>
95
+ /** STOPSIGNAL: Signal sent to stop the container */
96
+ | DockerfileStepOf<"stopsignal", string>
97
+ /** ONBUILD: Instruction to run when the image is used as a base */
98
+ | DockerfileStepOf<"onbuild", string>;
99
+ /**
100
+ * One build stage: FROM, then its instructions.
101
+ * Without `steps`, the per-kind fields are written in a fixed order: ARG, ENV, WORKDIR, USER, RUN, COPY, ADD, EXPOSE, VOLUME, LABEL, ENTRYPOINT, CMD, HEALTHCHECK.
102
+ */
59
103
  export interface DockerfileStage {
60
104
  from: string;
61
105
  as?: string;
106
+ /** Instructions after FROM, written exactly in this order. Can't be combined with the per-kind fields. */
107
+ steps?: DockerfileStep[];
108
+ /** ARG as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
62
109
  arg?: string[];
110
+ /** ENV as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
63
111
  env?: string[];
112
+ /** RUN as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
64
113
  run?: string[];
114
+ /** COPY as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
65
115
  copy?: string[];
116
+ /** ADD as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
66
117
  add?: string[];
118
+ /** WORKDIR as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
67
119
  workdir?: string;
120
+ /** USER as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
68
121
  user?: string;
122
+ /** EXPOSE as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
69
123
  expose?: string[];
124
+ /** VOLUME as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
70
125
  volume?: string[];
126
+ /** LABEL as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
71
127
  label?: string[];
128
+ /** ENTRYPOINT as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
72
129
  entrypoint?: string;
130
+ /** CMD as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
73
131
  cmd?: string;
132
+ /** HEALTHCHECK as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
74
133
  healthcheck?: string;
75
134
  }
135
+ /**
136
+ * A Dockerfile: `stages` for a multi-stage build, or `from` and the stage fields for one stage.
137
+ * Without `steps`, the per-kind fields are written in a fixed order: ARG, ENV, WORKDIR, USER, RUN, COPY, ADD, EXPOSE, VOLUME, LABEL, ENTRYPOINT, CMD, HEALTHCHECK.
138
+ */
76
139
  export interface DockerfileProps {
77
140
  name?: string;
141
+ /** ARG instructions written before the first FROM, e.g. `["NODE_VERSION=20"]`. */
142
+ globalArgs?: string[];
78
143
  from?: string;
144
+ as?: string;
79
145
  stages?: DockerfileStage[];
146
+ /** Instructions after FROM, written exactly in this order. Can't be combined with the per-kind fields. */
147
+ steps?: DockerfileStep[];
148
+ /** ARG as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
80
149
  arg?: string[];
150
+ /** ENV as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
81
151
  env?: string[];
152
+ /** RUN as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
82
153
  run?: string[];
154
+ /** COPY as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
83
155
  copy?: string[];
156
+ /** ADD as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
84
157
  add?: string[];
158
+ /** WORKDIR as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
85
159
  workdir?: string;
160
+ /** USER as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
86
161
  user?: string;
162
+ /** EXPOSE as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
87
163
  expose?: string[];
164
+ /** VOLUME as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
88
165
  volume?: string[];
166
+ /** LABEL as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
89
167
  label?: string[];
168
+ /** ENTRYPOINT as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
90
169
  entrypoint?: string;
170
+ /** CMD as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
91
171
  cmd?: string;
172
+ /** HEALTHCHECK as a per-kind field, written in the fixed order. Use `steps` to choose the order. */
92
173
  healthcheck?: string;
93
174
  }
94
175
  export declare const Dockerfile: new (props?: DockerfileProps) => Declarable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-docker",
3
- "version": "0.124.21",
3
+ "version": "0.124.22",
4
4
  "description": "Docker lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -63,13 +63,13 @@
63
63
  "build": "../../scripts/tsc7.sh -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete"
64
64
  },
65
65
  "dependencies": {
66
- "@intentius/chant-lexicon-otel": "^0.124.21"
66
+ "@intentius/chant-lexicon-otel": "^0.124.22"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@intentius/chant": "*",
70
70
  "typescript": "^5.9.3"
71
71
  },
72
72
  "peerDependencies": {
73
- "@intentius/chant": "^0.124.21"
73
+ "@intentius/chant": "^0.124.22"
74
74
  }
75
75
  }