@tahminator/pipeline 1.0.91 → 1.0.92-beta.g1f7c613a

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/README.md CHANGED
@@ -327,12 +327,14 @@ await flux.diff({
327
327
 
328
328
  Each Kustomization is rendered as `kustomize build` of `spec.path` with `targetNamespace`, `namePrefix`, `nameSuffix`, `components`, `images`, `patches` and `commonMetadata` applied on top, followed by `postBuild.substitute`. Paths without a `kustomization.yaml` get one generated, as Flux does. Values from `postBuild.substituteFrom` live in the cluster, so those variables are left as-is and listed in the diff's notes. A build failure is shown in the comment instead of being skipped. `HelmRelease` objects are diffed as manifests; charts are not templated.
329
329
 
330
- The diff is split per Kubernetes object (matched by API group, kind, namespace and name), and each object is labelled with the file that defines it, taken from kustomize's `originAnnotations`. Generated ConfigMaps and Secrets point at the `kustomization.yaml` whose generator creates them. Files that only patch an object are not recorded, so a change made by a patch is attributed to the object's original file. A renamed object (e.g. a new generator hash suffix) shows as one removal and one addition.
330
+ The diff is split per Kubernetes object (matched by API group, kind, namespace and name), and each object is labelled with the file that defines it, taken from kustomize's `originAnnotations`. Generated ConfigMaps and Secrets point at the `kustomization.yaml` whose generator creates them, and are matched without their content-hash suffix, so editing a generator's data shows as a line diff of the same object rather than a removal and an addition. Files that only patch an object are not recorded, so a change made by a patch is attributed to the object's original file. Any other renamed object shows as one removal and one addition.
331
331
 
332
332
  ### `DockerClient`
333
333
 
334
334
  Interface with Docker to build & deploy images
335
335
 
336
+ `buildImage` runs BuildKit on the runner's network with `--network host`, so every `RUN` instruction can reach whatever the runner can, including hosts on the Headscale VPN connected by the setup action. Dockerfiles don't need `RUN --network=host`.
337
+
336
338
  ```ts
337
339
  // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management
338
340
  await using client = await DockerClient.create(
@@ -423,6 +425,18 @@ View the [action file](./actions/setup/action.yaml) for all available inputs.
423
425
  cargo-nextest@0.9.100
424
426
  ```
425
427
 
428
+ Language toolchains are installed and cached by the setup action, so jobs don't need separate `setup-go` / `setup-java` / `rust-toolchain` steps:
429
+
430
+ ```yaml
431
+ - uses: tahminator/pipeline/actions/setup@<version>
432
+ with:
433
+ INSTALL_GO: "true" # version read from GO_VERSION_FILE (default go.mod) unless GO_VERSION is set
434
+ INSTALL_JAVA: "true" # JAVA_VERSION "21", JAVA_DISTRIBUTION "temurin", JAVA_CACHE "maven" by default
435
+ INSTALL_RUST: "true" # also caches the Cargo registry and target dir
436
+ RUST_TOOLCHAIN: "1.93.0"
437
+ RUST_COMPONENTS: "clippy, rustfmt"
438
+ ```
439
+
426
440
  ### `NPMClient`
427
441
 
428
442
  Interface with NPM registry in order to publish packages to `npmjs.com`
@@ -48,7 +48,10 @@ export class DockerClient {
48
48
  console.log("Building image with following tags:");
49
49
  tags.forEach((tag) => console.log(Utils.Colors.cyan(tag)));
50
50
  try {
51
- await $ `docker buildx create --use --name ${dockerRepository}-builder`;
51
+ await $ `docker buildx create --use --name ${dockerRepository}-builder \
52
+ --driver docker-container \
53
+ --driver-opt network=host \
54
+ --buildkitd-flags ${"--allow-insecure-entitlement network.host"}`;
52
55
  }
53
56
  catch {
54
57
  await $ `docker buildx use ${dockerRepository}-builder`;
@@ -68,6 +71,8 @@ export class DockerClient {
68
71
  --cache-from=type=gha \
69
72
  --cache-to=type=gha,mode=max \
70
73
  ${buildArgFlags} \
74
+ --network host \
75
+ --allow network.host \
71
76
  ${tagFlags} \
72
77
  .`;
73
78
  console.log(shouldUpload ?
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import path from "node:path";
5
5
  import { findFluxKustomizations } from "./discover";
6
6
  import { formatFluxDiff, label } from "./format";
7
- import { renderKustomization } from "./render";
7
+ import { objectKey, renderKustomization } from "./render";
8
8
  import {} from "./types";
9
9
  /** GitHub rejects comment bodies above 65536 characters. */
10
10
  const MAX_COMMENT_LENGTH = 65_536;
@@ -155,21 +155,14 @@ export class FluxClient {
155
155
  }
156
156
  }
157
157
  }
158
- /**
159
- * Pairs base and head objects by API group, kind, namespace and name, and diffs the ones whose
160
- * rendered YAML differs. An apiVersion bump within a group is a change, not a remove + add.
161
- */
158
+ /** Pairs base and head objects by {@link objectKey} and diffs the ones whose rendered YAML differs. */
162
159
  async function diffObjects(baseObjects, headObjects, nextDir) {
163
- const key = (object) => {
164
- const group = object.apiVersion.includes("/") ? object.apiVersion.split("/")[0] : "";
165
- return [group, object.kind, object.namespace, object.name].join("\0");
166
- };
167
160
  const pairs = new Map();
168
161
  for (const object of baseObjects) {
169
- pairs.set(key(object), { base: object });
162
+ pairs.set(objectKey(object), { base: object });
170
163
  }
171
164
  for (const object of headObjects) {
172
- const k = key(object);
165
+ const k = objectKey(object);
173
166
  pairs.set(k, { ...pairs.get(k), head: object });
174
167
  }
175
168
  const diffs = await Promise.all([...pairs.values()].map(async ({ base, head }) => {
@@ -9,9 +9,17 @@ export type RenderedObject = {
9
9
  * kustomization.yaml whose generator creates it); `undefined` if kustomize did not record one
10
10
  */
11
11
  file?: string;
12
+ /** created by a ConfigMap/Secret generator, so `name` may end in a content hash */
13
+ generated: boolean;
12
14
  /** the object's rendered YAML, without the origin annotation */
13
15
  yaml: string;
14
16
  };
17
+ /**
18
+ * Identifies the same object across two renders: API group, kind, namespace and name.
19
+ * An apiVersion bump within a group keeps the key; generated objects drop the content hash
20
+ * from their name so an edited ConfigMap/Secret pairs with its previous version.
21
+ */
22
+ export declare function objectKey(object: RenderedObject): string;
15
23
  type RenderedKustomization = {
16
24
  /** rendered objects, in kustomize's output order; empty when `error` is set */
17
25
  objects: RenderedObject[];
@@ -12,6 +12,19 @@ const SUBSTITUTE_DISABLED = /kustomize\.toolkit\.fluxcd\.io\/substitute:\s*["']?
12
12
  const VARIABLE = /\$(\$)?\{([A-Za-z_][A-Za-z0-9_]*)(?:(:?[-=])([^}]*))?\}/g;
13
13
  /** kustomize's `originAnnotations` build metadata; records the file that defines each object. */
14
14
  const ORIGIN_ANNOTATION = "config.kubernetes.io/origin";
15
+ const GENERATORS = ["ConfigMapGenerator", "SecretGenerator"];
16
+ /** kustomize's name hash: 10 characters from its vowel-free alphabet, appended last. */
17
+ const NAME_HASH = /-[2456789bcdfghkmt]{10}$/;
18
+ /**
19
+ * Identifies the same object across two renders: API group, kind, namespace and name.
20
+ * An apiVersion bump within a group keeps the key; generated objects drop the content hash
21
+ * from their name so an edited ConfigMap/Secret pairs with its previous version.
22
+ */
23
+ export function objectKey(object) {
24
+ const group = object.apiVersion.includes("/") ? object.apiVersion.split("/")[0] : "";
25
+ const name = object.generated ? object.name.replace(NAME_HASH, "") : object.name;
26
+ return [group, object.kind, object.namespace, name].join("\0");
27
+ }
15
28
  /**
16
29
  * Renders a Flux `Kustomization` the way kustomize-controller does: `kustomize build` of
17
30
  * `spec.path` with the overlay fields from the spec (`targetNamespace`, `namePrefix`,
@@ -86,21 +99,21 @@ export async function renderKustomization(root, kustomization) {
86
99
  }
87
100
  }
88
101
  function toRenderedObject(doc, root, wrapper) {
89
- const origin = doc.getIn(["metadata", "annotations", ORIGIN_ANNOTATION]);
102
+ const raw = doc.getIn(["metadata", "annotations", ORIGIN_ANNOTATION]);
90
103
  doc.deleteIn(["metadata", "annotations", ORIGIN_ANNOTATION]);
91
104
  const annotations = doc.getIn(["metadata", "annotations"]);
92
105
  if (yaml.isMap(annotations) && annotations.items.length === 0) {
93
106
  doc.deleteIn(["metadata", "annotations"]);
94
107
  }
108
+ const origin = typeof raw === "string" ? yaml.parse(raw) : null;
95
109
  const value = (doc.toJS() ?? {});
96
110
  return {
97
111
  apiVersion: String(value.apiVersion ?? ""),
98
112
  kind: String(value.kind ?? ""),
99
113
  namespace: String(value.metadata?.namespace ?? ""),
100
114
  name: String(value.metadata?.name ?? ""),
101
- file: typeof origin === "string" ?
102
- originFile(origin, root, wrapper)
103
- : undefined,
115
+ file: origin ? originFile(origin, root, wrapper) : undefined,
116
+ generated: GENERATORS.includes(origin?.configuredBy?.kind ?? ""),
104
117
  yaml: doc.toString({ lineWidth: 0 }).replace(/^---\n/, ""),
105
118
  };
106
119
  }
@@ -110,10 +123,9 @@ function toRenderedObject(doc, root, wrapper) {
110
123
  * `<repo>//<path>?ref=<ref>`.
111
124
  */
112
125
  function originFile(origin, root, wrapper) {
113
- const parsed = yaml.parse(origin);
114
- const file = parsed?.path ?? parsed?.configuredIn;
115
- if (parsed?.repo) {
116
- return `${parsed.repo}//${file ?? ""}${parsed.ref ? `?ref=${parsed.ref}` : ""}`;
126
+ const file = origin.path ?? origin.configuredIn;
127
+ if (origin.repo) {
128
+ return `${origin.repo}//${file ?? ""}${origin.ref ? `?ref=${origin.ref}` : ""}`;
117
129
  }
118
130
  return file ? path.relative(root, path.resolve(wrapper, file)) : undefined;
119
131
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "author": "Tahmid Ahmed",
5
5
  "description": "A collection of Bun shell scripts that can be re-used in various CICD pipelines.",
6
- "version": "1.0.91",
6
+ "version": "1.0.92-beta.g1f7c613a",
7
7
  "repository": {
8
8
  "url": "git+https://github.com/tahminator/pipeline.git"
9
9
  },