@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.
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/dockerfile-order.d.ts +13 -0
- package/dist/dockerfile-order.d.ts.map +1 -0
- package/dist/generated/index.d.ts +121 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/import/adapter.d.ts.map +1 -1
- package/dist/import/parser.d.ts +23 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/integrity.json +17 -8
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/add-url-checksum.d.ts +9 -0
- package/dist/lint/post-synth/add-url-checksum.d.ts.map +1 -0
- package/dist/lint/post-synth/apt-update-alone.d.ts +10 -0
- package/dist/lint/post-synth/apt-update-alone.d.ts.map +1 -0
- package/dist/lint/post-synth/cache-busting-copy.d.ts +11 -0
- package/dist/lint/post-synth/cache-busting-copy.d.ts.map +1 -0
- package/dist/lint/post-synth/copy-from-stage.d.ts +10 -0
- package/dist/lint/post-synth/copy-from-stage.d.ts.map +1 -0
- package/dist/lint/post-synth/docker-helpers.d.ts +35 -0
- package/dist/lint/post-synth/docker-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/no-root-user.d.ts +3 -2
- package/dist/lint/post-synth/no-root-user.d.ts.map +1 -1
- package/dist/lint/post-synth/relative-workdir.d.ts +10 -0
- package/dist/lint/post-synth/relative-workdir.d.ts.map +1 -0
- package/dist/lint/post-synth/repeated-cmd.d.ts +9 -0
- package/dist/lint/post-synth/repeated-cmd.d.ts.map +1 -0
- package/dist/lint/post-synth/root-after-user.d.ts +10 -0
- package/dist/lint/post-synth/root-after-user.d.ts.map +1 -0
- package/dist/lint/post-synth/unused-global-arg.d.ts +10 -0
- package/dist/lint/post-synth/unused-global-arg.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +1 -0
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/lint/rules/per-kind-order.d.ts +15 -0
- package/dist/lint/rules/per-kind-order.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +6 -0
- package/dist/okf/index.md +10 -1
- package/dist/okf/rules/DKRD012.md +2 -2
- package/dist/okf/rules/DKRD013.md +11 -0
- package/dist/okf/rules/DKRD014.md +11 -0
- package/dist/okf/rules/DKRD015.md +11 -0
- package/dist/okf/rules/DKRD016.md +11 -0
- package/dist/okf/rules/DKRD017.md +11 -0
- package/dist/okf/rules/DKRD018.md +11 -0
- package/dist/okf/rules/DKRD019.md +11 -0
- package/dist/okf/rules/DKRD020.md +11 -0
- package/dist/okf/rules/DKRS002.md +15 -0
- package/dist/okf/types/Dockerfile.md +4 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/add-url-checksum.ts +38 -0
- package/dist/rules/apt-update-alone.ts +39 -0
- package/dist/rules/cache-busting-copy.ts +64 -0
- package/dist/rules/copy-from-stage.ts +54 -0
- package/dist/rules/docker-helpers.ts +80 -0
- package/dist/rules/no-root-user.ts +19 -17
- package/dist/rules/per-kind-order.ts +130 -0
- package/dist/rules/relative-workdir.ts +38 -0
- package/dist/rules/repeated-cmd.ts +35 -0
- package/dist/rules/root-after-user.ts +44 -0
- package/dist/rules/unused-global-arg.ts +38 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skills/chant-docker-patterns.md +91 -22
- package/dist/skills/chant-docker.md +42 -16
- package/dist/spec/parse-engine.d.ts +9 -0
- package/dist/spec/parse-engine.d.ts.map +1 -1
- package/dist/types/index.d.ts +81 -0
- package/package.json +3 -3
- package/src/codegen/__snapshots__/okf.test.ts.snap +155 -4
- package/src/codegen/docs.ts +14 -0
- package/src/codegen/generate.ts +77 -19
- package/src/dockerfile-order.ts +20 -0
- package/src/docs-examples.test.ts +141 -0
- package/src/generated/index.d.ts +81 -0
- package/src/generated/index.ts +117 -0
- package/src/generated/lexicon-docker.json +6 -0
- package/src/import/adapter.ts +8 -2
- package/src/import/generator.ts +49 -33
- package/src/import/parser.ts +82 -13
- package/src/import/roundtrip.test.ts +105 -0
- package/src/lint/audit-catalog.ts +9 -1
- package/src/lint/audit-lineage.ts +14 -0
- package/src/lint/post-synth/add-url-checksum.ts +38 -0
- package/src/lint/post-synth/apt-update-alone.ts +39 -0
- package/src/lint/post-synth/cache-busting-copy.ts +64 -0
- package/src/lint/post-synth/copy-from-stage.ts +54 -0
- package/src/lint/post-synth/docker-helpers.ts +80 -0
- package/src/lint/post-synth/index.ts +16 -0
- package/src/lint/post-synth/no-root-user.ts +19 -17
- package/src/lint/post-synth/post-synth.test.ts +149 -0
- package/src/lint/post-synth/relative-workdir.ts +38 -0
- package/src/lint/post-synth/repeated-cmd.ts +35 -0
- package/src/lint/post-synth/root-after-user.ts +44 -0
- package/src/lint/post-synth/unused-global-arg.ts +38 -0
- package/src/lint/rules/index.ts +1 -0
- package/src/lint/rules/per-kind-order.ts +130 -0
- package/src/lint/rules/rules.test.ts +88 -0
- package/src/plugin.test.ts +4 -0
- package/src/plugin.ts +2 -1
- package/src/serializer.test.ts +112 -0
- package/src/serializer.ts +58 -11
- package/src/skills/chant-docker-patterns.md +91 -22
- package/src/skills/chant-docker.md +42 -16
- 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
|
+
};
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
|
96
|
+
## Python service
|
|
89
97
|
|
|
90
98
|
```typescript
|
|
91
99
|
export const pythonApp = new Dockerfile({
|
|
92
100
|
from: "python:3.12-slim",
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
44
|
-
export const
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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({
|
|
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
|
-
- `
|
|
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;
|
|
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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
73
|
+
"@intentius/chant": "^0.124.22"
|
|
74
74
|
}
|
|
75
75
|
}
|