feat: inherit valueFrom/envFrom env from Deployment; prefer paperclip container

- SelfPodInfo gains inheritedEnvValueFrom (V1EnvVar[]) and inheritedEnvFrom (V1EnvFromSource[])
- Container selection now prefers the container named "paperclip", falls back to first
- buildJobManifest appends valueFrom env vars (skipping names already overridden)
  and sets envFrom on the opencode container when present
- Tests updated: mock updated, 5 new cases covering secretKeyRef forwarding,
  dedup, envFrom passthrough, and empty-envFrom omission

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-24 22:12:31 +00:00
parent 84dc0f5930
commit 61d2a42a66
11 changed files with 1367 additions and 60 deletions
+51
View File
@@ -6,6 +6,8 @@ const mockSelfPod: JobBuildInput["selfPod"] = {
image: "paperclip/paperclip:latest",
imagePullSecrets: [],
inheritedEnv: {},
inheritedEnvValueFrom: [],
inheritedEnvFrom: [],
pvcClaimName: null,
dnsConfig: undefined,
secretVolumes: [],
@@ -144,4 +146,53 @@ describe("buildJobManifest", () => {
expect(result.job.spec?.template?.spec?.nodeSelector).toEqual({ "kubernetes.io/arch": "amd64" });
});
it("forwards inheritedEnvValueFrom entries onto the opencode container env", () => {
const selfPod = {
...mockSelfPod,
inheritedEnvValueFrom: [
{ name: "MY_SECRET", valueFrom: { secretKeyRef: { name: "my-secret", key: "token" } } },
],
};
const result = buildJobManifest({ ctx: mockCtx, selfPod });
const env = result.job.spec?.template?.spec?.containers?.[0].env ?? [];
const secretEnv = env.find((e) => e.name === "MY_SECRET");
expect(secretEnv?.valueFrom?.secretKeyRef?.name).toBe("my-secret");
expect(secretEnv?.valueFrom?.secretKeyRef?.key).toBe("token");
});
it("does not duplicate an inheritedEnvValueFrom entry if the name is already set as a literal", () => {
const selfPod = {
...mockSelfPod,
inheritedEnv: { HOME: "/custom" },
inheritedEnvValueFrom: [
{ name: "HOME", valueFrom: { secretKeyRef: { name: "s", key: "k" } } },
],
};
const result = buildJobManifest({ ctx: mockCtx, selfPod });
const env = result.job.spec?.template?.spec?.containers?.[0].env ?? [];
const homeEntries = env.filter((e) => e.name === "HOME");
// HOME is overridden by merged (HOME=/paperclip hardcoded last), so valueFrom must not appear
expect(homeEntries.every((e) => e.value !== undefined)).toBe(true);
});
it("forwards inheritedEnvFrom onto the opencode container envFrom", () => {
const selfPod = {
...mockSelfPod,
inheritedEnvFrom: [{ secretRef: { name: "my-config-secret" } }],
};
const result = buildJobManifest({ ctx: mockCtx, selfPod });
const container = result.job.spec?.template?.spec?.containers?.[0];
expect(container?.envFrom).toEqual([{ secretRef: { name: "my-config-secret" } }]);
});
it("omits envFrom when inheritedEnvFrom is empty", () => {
const result = buildJobManifest({ ctx: mockCtx, selfPod: mockSelfPod });
const container = result.job.spec?.template?.spec?.containers?.[0];
expect(container?.envFrom).toBeUndefined();
});
});