diff --git a/scripts/gitops-render.mjs b/scripts/gitops-render.mjs index 167d2a9a..bb7250bd 100644 --- a/scripts/gitops-render.mjs +++ b/scripts/gitops-render.mjs @@ -5583,6 +5583,7 @@ function runtimeSecretPlaneExternalSecret({ config, secret, index, namespace, la const data = Array.isArray(secret?.data) ? secret.data : []; assert.ok(data.length > 0, `secretPlane.secrets[${index}].data must not be empty`); const sourceRefs = data.map((item) => requiredSecretPlaneString(item?.remoteRef, `secretPlane.secrets[${index}].data.remoteRef`)); + const issueRef = String(config.issue ?? "pikasTech/HWLAB#2234"); return { apiVersion: "external-secrets.io/v1", kind: "ExternalSecret", @@ -5593,10 +5594,11 @@ function runtimeSecretPlaneExternalSecret({ config, secret, index, namespace, la ...labels, "app.kubernetes.io/name": externalSecretName, "app.kubernetes.io/component": "external-secret", - "hwlab.pikastech.local/secret-plane": String(config.issue ?? "pikasTech/HWLAB#2234") + "hwlab.pikastech.local/secret-plane": secretPlaneIssueLabelValue(issueRef) }, annotations: { ...annotations, + "hwlab.pikastech.local/secret-plane-issue": issueRef, "hwlab.pikastech.local/source-ref": sourceRefs.join(","), "hwlab.pikastech.local/values-printed": "false" } @@ -5622,6 +5624,16 @@ function runtimeSecretPlaneExternalSecret({ config, secret, index, namespace, la }; } +function secretPlaneIssueLabelValue(issueRef) { + const text = String(issueRef ?? "").trim(); + const issueNumber = text.match(/#([0-9]+)$/u)?.[1]; + const raw = issueNumber ? `issue-${issueNumber}` : text; + const normalized = raw.replace(/[^A-Za-z0-9_.-]+/gu, "-").replace(/^[^A-Za-z0-9]+|[^A-Za-z0-9]+$/gu, ""); + const label = normalized.slice(0, 63).replace(/[^A-Za-z0-9]+$/u, ""); + assert.ok(label.length > 0, "secretPlane.issue must produce a Kubernetes label-safe value"); + return label; +} + function requiredSecretPlaneString(value, label) { assert.equal(typeof value, "string", `${label} must be a string`); const trimmed = value.trim(); diff --git a/scripts/gitops-render.test.ts b/scripts/gitops-render.test.ts index 0a6e67e5..50cf28be 100644 --- a/scripts/gitops-render.test.ts +++ b/scripts/gitops-render.test.ts @@ -584,6 +584,8 @@ test("v03 render keeps node identity as data instead of generated structure", as const smokeExternalSecret = (externalSecretsJson.items ?? []).find((item) => item.kind === "ExternalSecret" && item.metadata?.name === "hwlab-secret-plane-smoke"); assert.ok(smokeExternalSecret, "expected v03 secret-plane smoke ExternalSecret"); assert.equal(smokeExternalSecret.metadata?.namespace, "hwlab-v03"); + assert.equal(smokeExternalSecret.metadata?.labels?.["hwlab.pikastech.local/secret-plane"], "issue-2234"); + assert.equal(smokeExternalSecret.metadata?.annotations?.["hwlab.pikastech.local/secret-plane-issue"], "pikasTech/HWLAB#2234"); assert.equal(smokeExternalSecret.spec?.secretStoreRef?.kind, "ClusterSecretStore"); assert.equal(smokeExternalSecret.spec?.secretStoreRef?.name, "hwlab-secret-plane-vault-cluster"); assert.equal(smokeExternalSecret.spec?.target?.name, "hwlab-secret-plane-smoke"); @@ -696,6 +698,8 @@ test("v03 render includes D518 secret-plane smoke on D518 gitops root", async () const smokeExternalSecret = (externalSecretsJson.items ?? []).find((item) => item.kind === "ExternalSecret" && item.metadata?.name === "hwlab-secret-plane-smoke"); assert.ok(smokeExternalSecret, "expected D518 v03 secret-plane smoke ExternalSecret"); assert.equal(smokeExternalSecret.metadata?.namespace, "hwlab-v03"); + assert.equal(smokeExternalSecret.metadata?.labels?.["hwlab.pikastech.local/secret-plane"], "issue-2234"); + assert.equal(smokeExternalSecret.metadata?.annotations?.["hwlab.pikastech.local/secret-plane-issue"], "pikasTech/HWLAB#2234"); assert.equal(smokeExternalSecret.spec?.secretStoreRef?.kind, "ClusterSecretStore"); assert.equal(smokeExternalSecret.spec?.secretStoreRef?.name, "hwlab-secret-plane-vault-cluster"); assert.equal(smokeExternalSecret.spec?.target?.name, "hwlab-secret-plane-smoke");