@intentius/chant-lexicon-docker 0.100.0 → 0.102.0
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/generated/index.d.ts +6 -6
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/import/adapter.d.ts.map +1 -1
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/variables.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/serializer.test.ts +49 -0
- package/src/serializer.ts +48 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const Service: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
2
|
-
export declare const Volume: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
3
|
-
export declare const Network: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
4
|
-
export declare const DockerConfig: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
5
|
-
export declare const DockerSecret: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
6
|
-
export declare const Dockerfile: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("
|
|
1
|
+
export declare const Service: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
2
|
+
export declare const Volume: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
3
|
+
export declare const Network: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
4
|
+
export declare const DockerConfig: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
5
|
+
export declare const DockerSecret: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
6
|
+
export declare const Dockerfile: new (props: Record<string, unknown>, attributes?: Record<string, unknown>) => import("@intentius/chant").Declarable & Record<string, string>;
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,8IAA2D,CAAC;AAChF,eAAO,MAAM,MAAM,8IAA0D,CAAC;AAC9E,eAAO,MAAM,OAAO,8IAA2D,CAAC;AAChF,eAAO,MAAM,YAAY,8IAA0D,CAAC;AACpF,eAAO,MAAM,YAAY,8IAA0D,CAAC;AAEpF,eAAO,MAAM,UAAU,8IAAqD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/import/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAc,MAAM,gCAAgC,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAgD5F;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/import/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAc,MAAM,gCAAgC,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAgD5F;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAMjC;CACF;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,aAAa,EAAE,CAIxC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/import/generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAoE,MAAM,UAAU,CAAC;AAE3G,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/import/generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAoE,MAAM,UAAU,CAAC;AAE3G,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAsC7C;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0BD;;;GAGG;AACH,qBAAa,YAAY;IACvB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0BD;;;GAGG;AACH,qBAAa,YAAY;IACvB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAoElC;CACF;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CA4BjD;CACF"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "e7a19357c7fdbc452830f8f1b348c87e98b2cb424e84b6aa584020b35b1f2650",
|
|
5
5
|
"meta.json": "2996be8fdb14f3f935e1e1441fed8f69af7b6aeeefb14dac96301cc41eba8046",
|
|
6
6
|
"types/index.d.ts": "7b2e6f784b7dbecbbe7924fa1aec7e2520ac0547069311253a006bc2267a5646",
|
|
7
7
|
"rules/no-latest-tag.ts": "efd060453d8ca3d5b85c3972906c6650b6f63eb5f67cb6cdb0f16a41cad91228",
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
"skills/chant-docker.md": "3ff0708e3c76e245f202948949c768464563af2b60cc9aa2ca52f494ef8357f1",
|
|
16
16
|
"skills/chant-docker-patterns.md": "ad1b0196d8150b2df579a699ff107f604340c799c04f3460ebf65003e287b12a"
|
|
17
17
|
},
|
|
18
|
-
"composite": "
|
|
18
|
+
"composite": "14a64046d656bdf511370cac18a4325fbef0dea043179957339b9e08993fe703"
|
|
19
19
|
}
|
package/dist/manifest.json
CHANGED
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,
|
|
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;AA+RlG,eAAO,MAAM,gBAAgB,EAAE,UAqE9B,CAAC"}
|
package/dist/variables.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variables.d.ts","sourceRoot":"","sources":["../src/variables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qDAAqD;AACrD,eAAO,MAAM,WAAW;IACtB,gDAAgD
|
|
1
|
+
{"version":3,"file":"variables.d.ts","sourceRoot":"","sources":["../src/variables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qDAAqD;AACrD,eAAO,MAAM,WAAW;IACtB,gDAAgD;aAChD,eAAe,EAAE,iBAAiB;IAClC,iCAAiC;aACjC,WAAW,EAAE,aAAa;IAC1B,wBAAwB;aACxB,iBAAiB,EAAE,mBAAmB;IACtC,8BAA8B;aAC9B,aAAa,EAAE,eAAe;IAC9B,2BAA2B;aAC3B,oBAAoB,EAAE,sBAAsB;CACpC,CAAC;AAEX,oDAAoD;AACpD,eAAO,MAAM,YAAY;IACvB,kDAAkD;aAClD,oBAAoB,EAAE,sBAAsB;IAC5C,0CAA0C;aAC1C,YAAY,EAAE,cAAc;IAC5B,sCAAsC;aACtC,sBAAsB,EAAE,wBAAwB;IAChD,wCAAwC;aACxC,gBAAgB,EAAE,kBAAkB;IACpC,8BAA8B;aAC9B,6BAA6B,EAAE,+BAA+B;IAC9D,8BAA8B;aAC9B,uBAAuB,EAAE,yBAAyB;CAC1C,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AACvE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-docker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.102.0",
|
|
4
4
|
"description": "Docker lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"validate": "tsx src/validate-cli.ts",
|
|
61
61
|
"docs": "tsx src/codegen/docs-cli.ts",
|
|
62
62
|
"prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
|
|
63
|
-
"build": "
|
|
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
66
|
"@intentius/chant-lexicon-otel": "*"
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"typescript": "^5.9.3"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@intentius/chant": "^0.
|
|
73
|
+
"@intentius/chant": "^0.102.0",
|
|
74
74
|
"typescript": "^5.9.3"
|
|
75
75
|
}
|
|
76
76
|
}
|
package/src/serializer.test.ts
CHANGED
|
@@ -317,3 +317,52 @@ describe("dockerSerializer.serialize — name gen", () => {
|
|
|
317
317
|
expect(output).toContain("myWebServer:");
|
|
318
318
|
});
|
|
319
319
|
});
|
|
320
|
+
|
|
321
|
+
// ── Telemetry attribution (#2558, D22) ─────────────────────────────
|
|
322
|
+
|
|
323
|
+
describe("telemetry attribution", () => {
|
|
324
|
+
const run = (props: Record<string, unknown>, telemetry?: { workspace?: string; member?: string; environment?: string }) => {
|
|
325
|
+
const result = dockerSerializer.serialize(new Map([["api", new MockService(props)]]), [], telemetry ? { telemetry } : undefined);
|
|
326
|
+
return typeof result === "string" ? result : result.primary;
|
|
327
|
+
};
|
|
328
|
+
const attrs = { workspace: "acme", member: "delivery", environment: "prod" };
|
|
329
|
+
|
|
330
|
+
test("a build with no telemetry context is the same bytes as one that passes an empty context", () => {
|
|
331
|
+
const props = { image: "nginx:1" };
|
|
332
|
+
expect(run(props)).toBe(dockerSerializer.serialize(new Map([["api", new MockService(props)]]), [], {}) as string);
|
|
333
|
+
expect(run(props)).not.toContain("OTEL_");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("stamps the service name and the resource attributes into the environment", () => {
|
|
337
|
+
const out = run({ image: "nginx:1" }, attrs);
|
|
338
|
+
expect(out).toContain("OTEL_SERVICE_NAME: api");
|
|
339
|
+
expect(out).toContain("OTEL_RESOURCE_ATTRIBUTES: chant.workspace=acme,chant.member=delivery,chant.decl=api,deployment.environment.name=prod");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("leaves out an attribute the build does not know", () => {
|
|
343
|
+
const out = run({ image: "nginx:1" }, { workspace: "acme" });
|
|
344
|
+
expect(out).toContain("OTEL_RESOURCE_ATTRIBUTES: chant.workspace=acme,chant.decl=api");
|
|
345
|
+
expect(out).not.toContain("chant.member");
|
|
346
|
+
expect(out).not.toContain("deployment.environment.name");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("stamps service.version when the image is pinned by digest", () => {
|
|
350
|
+
const out = run({ image: "ghcr.io/acme/api@sha256:abc123" }, attrs);
|
|
351
|
+
expect(out).toContain("service.version=sha256%3Aabc123");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("keeps the keys a map environment already sets and appends the missing attributes", () => {
|
|
355
|
+
const out = run({ image: "x", environment: { OTEL_SERVICE_NAME: "checkout", OTEL_RESOURCE_ATTRIBUTES: "chant.member=mine,team=pay", PORT: "80" } }, attrs);
|
|
356
|
+
expect(out).toContain("OTEL_SERVICE_NAME: checkout");
|
|
357
|
+
expect(out).toContain("OTEL_RESOURCE_ATTRIBUTES: chant.member=mine,team=pay,chant.workspace=acme,chant.decl=api,deployment.environment.name=prod");
|
|
358
|
+
expect(out).toContain("PORT:");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("adds the variables to a list environment, keeping an entry already there", () => {
|
|
362
|
+
const out = run({ image: "x", environment: ["PORT=80", "OTEL_SERVICE_NAME=checkout"] }, attrs);
|
|
363
|
+
expect(out).toContain("- PORT=80");
|
|
364
|
+
expect(out).toContain("- OTEL_SERVICE_NAME=checkout");
|
|
365
|
+
expect(out).not.toContain("OTEL_SERVICE_NAME=api");
|
|
366
|
+
expect(out).toContain("- OTEL_RESOURCE_ATTRIBUTES=chant.workspace=acme,chant.member=delivery");
|
|
367
|
+
});
|
|
368
|
+
});
|
package/src/serializer.ts
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
13
13
|
import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
14
|
-
import type { Serializer, SerializerResult } from "@intentius/chant/serializer";
|
|
14
|
+
import type { SerializeContext, Serializer, SerializerResult } from "@intentius/chant/serializer";
|
|
15
|
+
import { mergeResourceAttributes, telemetryEnvironment, type TelemetryAttribution } from "@intentius/chant/telemetry-attribution";
|
|
15
16
|
import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
16
17
|
import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
|
|
17
18
|
import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
|
|
18
|
-
import {
|
|
19
|
+
import { emitYAMLEntry } from "@intentius/chant/yaml";
|
|
19
20
|
|
|
20
21
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
21
22
|
|
|
@@ -97,6 +98,45 @@ function toYAMLValue(value: unknown, entityNames: Map<Declarable, string>): unkn
|
|
|
97
98
|
return walkValue(preprocessed, entityNames, dockerVisitor(entityNames));
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
// ── Telemetry attribution (#2558, D22) ────────────────────────────
|
|
102
|
+
|
|
103
|
+
/** The digest of an image reference pinned with `@sha256:...`, or undefined. */
|
|
104
|
+
function imageDigest(image: unknown): string | undefined {
|
|
105
|
+
if (typeof image !== "string") return undefined;
|
|
106
|
+
const at = image.lastIndexOf("@");
|
|
107
|
+
return at === -1 ? undefined : image.slice(at + 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Stamp one service's attributes as `OTEL_SERVICE_NAME` and
|
|
112
|
+
* `OTEL_RESOURCE_ATTRIBUTES` in its environment. A value the service already
|
|
113
|
+
* sets is kept: its own `OTEL_SERVICE_NAME` wins, and its own resource
|
|
114
|
+
* attributes keep their keys while the missing ones are appended.
|
|
115
|
+
*/
|
|
116
|
+
function stampTelemetry(service: Record<string, unknown>, name: string, telemetry: TelemetryAttribution): void {
|
|
117
|
+
const version = imageDigest(service.image);
|
|
118
|
+
const vars = telemetryEnvironment(telemetry, { service: name, decl: name, ...(version ? { version } : {}) });
|
|
119
|
+
const env = service.environment;
|
|
120
|
+
if (Array.isArray(env)) {
|
|
121
|
+
const entries = env.map(String);
|
|
122
|
+
const has = (key: string) => entries.findIndex((e) => e === key || e.startsWith(`${key}=`));
|
|
123
|
+
if (has("OTEL_SERVICE_NAME") === -1) entries.push(`OTEL_SERVICE_NAME=${vars.OTEL_SERVICE_NAME}`);
|
|
124
|
+
const at = has("OTEL_RESOURCE_ATTRIBUTES");
|
|
125
|
+
if (at === -1) entries.push(`OTEL_RESOURCE_ATTRIBUTES=${vars.OTEL_RESOURCE_ATTRIBUTES}`);
|
|
126
|
+
else if (entries[at]!.includes("=")) {
|
|
127
|
+
entries[at] = `OTEL_RESOURCE_ATTRIBUTES=${mergeResourceAttributes(entries[at]!.slice(entries[at]!.indexOf("=") + 1), vars.OTEL_RESOURCE_ATTRIBUTES)}`;
|
|
128
|
+
}
|
|
129
|
+
service.environment = entries;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const map: Record<string, unknown> = env && typeof env === "object" ? { ...(env as Record<string, unknown>) } : {};
|
|
133
|
+
if (!("OTEL_SERVICE_NAME" in map)) map.OTEL_SERVICE_NAME = vars.OTEL_SERVICE_NAME;
|
|
134
|
+
const own = map.OTEL_RESOURCE_ATTRIBUTES;
|
|
135
|
+
if (own === undefined) map.OTEL_RESOURCE_ATTRIBUTES = vars.OTEL_RESOURCE_ATTRIBUTES;
|
|
136
|
+
else if (typeof own === "string") map.OTEL_RESOURCE_ATTRIBUTES = mergeResourceAttributes(own, vars.OTEL_RESOURCE_ATTRIBUTES);
|
|
137
|
+
service.environment = map;
|
|
138
|
+
}
|
|
139
|
+
|
|
100
140
|
// ── Compose serialization ─────────────────────────────────────────
|
|
101
141
|
|
|
102
142
|
function serializeCompose(
|
|
@@ -107,6 +147,7 @@ function serializeCompose(
|
|
|
107
147
|
secrets: Map<string, Declarable>,
|
|
108
148
|
defaultLabels: Record<string, string>,
|
|
109
149
|
entityNames: Map<Declarable, string>,
|
|
150
|
+
telemetry?: TelemetryAttribution,
|
|
110
151
|
): string {
|
|
111
152
|
const doc: Record<string, unknown> = {};
|
|
112
153
|
|
|
@@ -124,6 +165,7 @@ function serializeCompose(
|
|
|
124
165
|
for (const [k, v] of Object.entries(props)) {
|
|
125
166
|
if (v !== undefined && v !== null) cleaned[k] = v;
|
|
126
167
|
}
|
|
168
|
+
if (telemetry) stampTelemetry(cleaned, name, telemetry);
|
|
127
169
|
servicesSection[name] = cleaned;
|
|
128
170
|
}
|
|
129
171
|
doc.services = servicesSection;
|
|
@@ -194,13 +236,13 @@ function emitComposeDocument(doc: Record<string, unknown>): string {
|
|
|
194
236
|
for (const key of ORDER) {
|
|
195
237
|
if (key in doc && doc[key] !== undefined) {
|
|
196
238
|
emitted.add(key);
|
|
197
|
-
sections.push(
|
|
239
|
+
sections.push(emitYAMLEntry(key, doc[key]));
|
|
198
240
|
}
|
|
199
241
|
}
|
|
200
242
|
|
|
201
243
|
for (const [key, value] of Object.entries(doc)) {
|
|
202
244
|
if (!emitted.has(key) && value !== undefined) {
|
|
203
|
-
sections.push(
|
|
245
|
+
sections.push(emitYAMLEntry(key, value));
|
|
204
246
|
}
|
|
205
247
|
}
|
|
206
248
|
|
|
@@ -263,6 +305,7 @@ export const dockerSerializer: Serializer = {
|
|
|
263
305
|
serialize(
|
|
264
306
|
entities: Map<string, Declarable>,
|
|
265
307
|
_outputs?: LexiconOutput[],
|
|
308
|
+
context?: SerializeContext,
|
|
266
309
|
): string | SerializerResult {
|
|
267
310
|
const entityNames = new Map<Declarable, string>();
|
|
268
311
|
for (const [name, entity] of entities) {
|
|
@@ -310,7 +353,7 @@ export const dockerSerializer: Serializer = {
|
|
|
310
353
|
|
|
311
354
|
const composeYaml = serializeCompose(
|
|
312
355
|
services, volumes, networks, configs, secrets,
|
|
313
|
-
defaultLabels, entityNames,
|
|
356
|
+
defaultLabels, entityNames, context?.telemetry,
|
|
314
357
|
);
|
|
315
358
|
|
|
316
359
|
if (dockerfiles.size === 0) {
|