Merge pull request #357 from pikasTech/fix/artificer-private-pr-auth
Pipelines as Code CI / agentrun-nc01-v02-ci-11c764e8d271d2e59351cf4ccad7dfce2d6a799c Success
Pipelines as Code CI / agentrun-nc01-v02-ci-11c764e8d271d2e59351cf4ccad7dfce2d6a799c Success
fix: 声明 Artificer 私有仓 repository override 凭据
This commit is contained in:
@@ -55,6 +55,17 @@ spec:
|
|||||||
kind: env
|
kind: env
|
||||||
envName: GH_TOKEN
|
envName: GH_TOKEN
|
||||||
secretKey: GH_TOKEN
|
secretKey: GH_TOKEN
|
||||||
|
- tool: github
|
||||||
|
purpose: repository-override-pikainc-pikaoa
|
||||||
|
secretRef:
|
||||||
|
namespace: agentrun-v02
|
||||||
|
name: agentrun-v02-tool-github-repository-overrides
|
||||||
|
keys:
|
||||||
|
- PIKAINC_PIKAOA_GH_TOKEN
|
||||||
|
projection:
|
||||||
|
kind: env
|
||||||
|
envName: UNIDESK_GH_TOKEN_PIKAINC_PIKAOA
|
||||||
|
secretKey: PIKAINC_PIKAOA_GH_TOKEN
|
||||||
- tool: unidesk-ssh
|
- tool: unidesk-ssh
|
||||||
purpose: ssh-passthrough
|
purpose: ssh-passthrough
|
||||||
secretRef:
|
secretRef:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { MemoryAgentRunStore } from "../../mgr/store.js";
|
|||||||
import type { JsonRecord } from "../../common/types.js";
|
import type { JsonRecord } from "../../common/types.js";
|
||||||
import { validateAipodSpec } from "../../common/aipod-specs.js";
|
import { validateAipodSpec } from "../../common/aipod-specs.js";
|
||||||
import { validateCreateCommand, validateCreateRun, validatePrimaryWorkspaceContract, validateResourceBundleRef } from "../../common/validation.js";
|
import { validateCreateCommand, validateCreateRun, validatePrimaryWorkspaceContract, validateResourceBundleRef } from "../../common/validation.js";
|
||||||
|
import { renderRunnerJobDryRun } from "../../runner/k8s-job.js";
|
||||||
import { classifyGitStderr, resolveGitBundleFetchSource } from "../../runner/resource-bundle.js";
|
import { classifyGitStderr, resolveGitBundleFetchSource } from "../../runner/resource-bundle.js";
|
||||||
import { assertNoSecretLeak, loadArtificerImageRef, type SelfTestCase } from "../harness.js";
|
import { assertNoSecretLeak, loadArtificerImageRef, type SelfTestCase } from "../harness.js";
|
||||||
|
|
||||||
@@ -70,6 +71,19 @@ const selfTest: SelfTestCase = async (context) => {
|
|||||||
const shownProviderCredential = (shownProviderCredentials.items as JsonRecord[]).find((item) => item.profile === "gpt-pika");
|
const shownProviderCredential = (shownProviderCredentials.items as JsonRecord[]).find((item) => item.profile === "gpt-pika");
|
||||||
assert.equal(shownProviderCredential?.name, "agentrun-v02-provider-gpt-pika");
|
assert.equal(shownProviderCredential?.name, "agentrun-v02-provider-gpt-pika");
|
||||||
assert.equal(shownProviderCredential?.namespace, "agentrun-v02");
|
assert.equal(shownProviderCredential?.namespace, "agentrun-v02");
|
||||||
|
const shownToolCredentials = shownItem.toolCredentials as JsonRecord;
|
||||||
|
assert.equal(shownToolCredentials.count, 4);
|
||||||
|
assert.equal(shownToolCredentials.valuesPrinted, false);
|
||||||
|
const shownRepositoryOverride = (shownToolCredentials.items as JsonRecord[]).find((item) => item.purpose === "repository-override-pikainc-pikaoa") as JsonRecord | undefined;
|
||||||
|
assert.deepEqual(shownRepositoryOverride, {
|
||||||
|
tool: "github",
|
||||||
|
purpose: "repository-override-pikainc-pikaoa",
|
||||||
|
name: "agentrun-v02-tool-github-repository-overrides",
|
||||||
|
namespace: "agentrun-v02",
|
||||||
|
keys: ["PIKAINC_PIKAOA_GH_TOKEN"],
|
||||||
|
projection: { kind: "env", envName: "UNIDESK_GH_TOKEN_PIKAINC_PIKAOA", secretKey: "PIKAINC_PIKAOA_GH_TOKEN" },
|
||||||
|
valuesPrinted: false,
|
||||||
|
});
|
||||||
const shownImageRef = shownItem.imageRef as JsonRecord;
|
const shownImageRef = shownItem.imageRef as JsonRecord;
|
||||||
assert.equal(shownImageRef.kind, "env-image-dockerfile");
|
assert.equal(shownImageRef.kind, "env-image-dockerfile");
|
||||||
assert.equal(shownImageRef.repoUrl, "git@github.com:pikasTech/agentrun.git");
|
assert.equal(shownImageRef.repoUrl, "git@github.com:pikasTech/agentrun.git");
|
||||||
@@ -124,6 +138,17 @@ const selfTest: SelfTestCase = async (context) => {
|
|||||||
assert.deepEqual(disclosedProviderCredential.secretRef, { name: "agentrun-v02-provider-gpt-pika", namespace: "agentrun-v02", keys: ["auth.json", "config.toml"], valuesPrinted: false });
|
assert.deepEqual(disclosedProviderCredential.secretRef, { name: "agentrun-v02-provider-gpt-pika", namespace: "agentrun-v02", keys: ["auth.json", "config.toml"], valuesPrinted: false });
|
||||||
const toolCredentials = secretScope.toolCredentials as JsonRecord[];
|
const toolCredentials = secretScope.toolCredentials as JsonRecord[];
|
||||||
assert.equal(toolCredentials.some((item) => item.tool === "github" && ((item.projection as JsonRecord).kind) === "env" && ((item.projection as JsonRecord).envName) === "GH_TOKEN" && ((item.secretRef as JsonRecord).name) === "agentrun-v01-tool-github-pr"), true);
|
assert.equal(toolCredentials.some((item) => item.tool === "github" && ((item.projection as JsonRecord).kind) === "env" && ((item.projection as JsonRecord).envName) === "GH_TOKEN" && ((item.secretRef as JsonRecord).name) === "agentrun-v01-tool-github-pr"), true);
|
||||||
|
const repositoryOverride = toolCredentials.find((item) => item.tool === "github" && item.purpose === "repository-override-pikainc-pikaoa") as JsonRecord | undefined;
|
||||||
|
assert.deepEqual(repositoryOverride, {
|
||||||
|
tool: "github",
|
||||||
|
purpose: "repository-override-pikainc-pikaoa",
|
||||||
|
secretRef: {
|
||||||
|
namespace: "agentrun-v02",
|
||||||
|
name: "agentrun-v02-tool-github-repository-overrides",
|
||||||
|
keys: ["PIKAINC_PIKAOA_GH_TOKEN"],
|
||||||
|
},
|
||||||
|
projection: { kind: "env", envName: "UNIDESK_GH_TOKEN_PIKAINC_PIKAOA", secretKey: "PIKAINC_PIKAOA_GH_TOKEN" },
|
||||||
|
});
|
||||||
assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && ((item.projection as JsonRecord).envName) === "UNIDESK_SSH_CLIENT_TOKEN"), true);
|
assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && ((item.projection as JsonRecord).envName) === "UNIDESK_SSH_CLIENT_TOKEN"), true);
|
||||||
const githubSshCredential = toolCredentials.find((item) => item.tool === "github" && item.purpose === "github-ssh") as JsonRecord | undefined;
|
const githubSshCredential = toolCredentials.find((item) => item.tool === "github" && item.purpose === "github-ssh") as JsonRecord | undefined;
|
||||||
assert.equal(((githubSshCredential?.projection as JsonRecord).kind), "volume");
|
assert.equal(((githubSshCredential?.projection as JsonRecord).kind), "volume");
|
||||||
@@ -141,6 +166,42 @@ const selfTest: SelfTestCase = async (context) => {
|
|||||||
assert.equal((bundle.requiredSkills as JsonRecord[]).some((item) => item.name === "unidesk-gh"), true);
|
assert.equal((bundle.requiredSkills as JsonRecord[]).some((item) => item.name === "unidesk-gh"), true);
|
||||||
assertNoSecretLeak(rendered);
|
assertNoSecretLeak(rendered);
|
||||||
|
|
||||||
|
const runnerRun = store.createRun(validateCreateRun({
|
||||||
|
tenantId: task.tenantId,
|
||||||
|
projectId: task.projectId,
|
||||||
|
providerId: task.providerId,
|
||||||
|
backendProfile: task.backendProfile,
|
||||||
|
workspaceRef: task.workspaceRef,
|
||||||
|
executionPolicy: task.executionPolicy,
|
||||||
|
resourceBundleRef: task.resourceBundleRef,
|
||||||
|
traceSink: { kind: "self-test", source: "artificer-private-repository-override" },
|
||||||
|
}));
|
||||||
|
const runnerCommand = store.createCommand(runnerRun.id, validateCreateCommand({ type: "turn", payload: { prompt: "验证私有仓凭据投影" } }));
|
||||||
|
const runnerJob = renderRunnerJobDryRun({
|
||||||
|
run: runnerRun,
|
||||||
|
commandId: runnerCommand.id,
|
||||||
|
managerUrl: server.baseUrl,
|
||||||
|
runnerApiKeySecretRef: { name: "agentrun-v02-api-key", key: "HWLAB_API_KEY" },
|
||||||
|
image: "agentrun-selftest:latest",
|
||||||
|
namespace: "agentrun-v02",
|
||||||
|
attemptId: "attempt_artificer_private_repository_override",
|
||||||
|
sourceCommit: "self-test",
|
||||||
|
});
|
||||||
|
const overrideEnv = runnerEnvEntry(runnerJob.manifest as JsonRecord, "UNIDESK_GH_TOKEN_PIKAINC_PIKAOA");
|
||||||
|
assert.ok(overrideEnv, "Artificer runner Job must project the repository override credential");
|
||||||
|
assert.equal(overrideEnv.value, undefined);
|
||||||
|
assert.deepEqual(((overrideEnv.valueFrom as JsonRecord).secretKeyRef), {
|
||||||
|
name: "agentrun-v02-tool-github-repository-overrides",
|
||||||
|
key: "PIKAINC_PIKAOA_GH_TOKEN",
|
||||||
|
});
|
||||||
|
const defaultGithubEnv = runnerEnvEntry(runnerJob.manifest as JsonRecord, "GH_TOKEN");
|
||||||
|
assert.deepEqual(((defaultGithubEnv?.valueFrom as JsonRecord).secretKeyRef), { name: "agentrun-v01-tool-github-pr", key: "GH_TOKEN" });
|
||||||
|
const runnerToolCredentialSummary = runnerJob.toolCredentials as JsonRecord;
|
||||||
|
assert.equal(runnerToolCredentialSummary.count, 4);
|
||||||
|
assert.equal(runnerToolCredentialSummary.valuesPrinted, false);
|
||||||
|
assert.equal((runnerToolCredentialSummary.items as JsonRecord[]).every((item) => item.valuesPrinted === false), true);
|
||||||
|
assertNoSecretLeak(runnerJob);
|
||||||
|
|
||||||
const modelOverride = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "override model", model: "gpt-5.4", reasoningEffort: "high" }) as JsonRecord;
|
const modelOverride = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "override model", model: "gpt-5.4", reasoningEffort: "high" }) as JsonRecord;
|
||||||
const modelOverrideTask = modelOverride.queueTask as JsonRecord;
|
const modelOverrideTask = modelOverride.queueTask as JsonRecord;
|
||||||
const modelOverridePayload = modelOverrideTask.payload as JsonRecord;
|
const modelOverridePayload = modelOverrideTask.payload as JsonRecord;
|
||||||
@@ -292,7 +353,7 @@ const selfTest: SelfTestCase = async (context) => {
|
|||||||
assert.equal((storedTask.payload as JsonRecord).model, "gpt-5.6-sol");
|
assert.equal((storedTask.payload as JsonRecord).model, "gpt-5.6-sol");
|
||||||
assert.equal((storedTask.payload as JsonRecord).reasoningEffort, "medium");
|
assert.equal((storedTask.payload as JsonRecord).reasoningEffort, "medium");
|
||||||
assert.deepEqual((storedTask.payload as JsonRecord).modelConfig, taskPayload.modelConfig);
|
assert.deepEqual((storedTask.payload as JsonRecord).modelConfig, taskPayload.modelConfig);
|
||||||
return { name: "aipod-spec", tests: ["aipod-spec-yaml-parser-runtime-compatible", "aipod-spec-artificer-image-ref-render", "aipod-spec-artificer-v02-runtime-authority", "aipod-spec-gpt-pika-secret-namespace", "aipod-spec-model-defaults", "aipod-spec-model-reasoning-overrides", "aipod-spec-model-override-keeps-provider", "aipod-spec-control-override-fail-closed", "aipod-spec-payload-control-bypass-fail-closed", "aipod-spec-missing-gpt-pika-credential", "aipod-spec-durable-task-model-snapshot", "aipod-spec-primary-workspace-contract", "aipod-spec-artificer-github-url-render", "aipod-spec-artificer-github-ssh-required-keys", "aipod-spec-git-mirror-url", "git-fetch-stderr-classification", "resource-bundle-source-authority-validation", "resource-bundle-target-conflict-validation", "queue-submit-aipod-dry-run", "session-send-aipod-model-dry-run", "session-send-aipod-cancelled-durable-identity", "session-send-aipod-boundary-conflict-fail-closed", "aipod-cli-help"] };
|
return { name: "aipod-spec", tests: ["aipod-spec-yaml-parser-runtime-compatible", "aipod-spec-artificer-image-ref-render", "aipod-spec-artificer-v02-runtime-authority", "aipod-spec-gpt-pika-secret-namespace", "aipod-spec-model-defaults", "aipod-spec-model-reasoning-overrides", "aipod-spec-model-override-keeps-provider", "aipod-spec-control-override-fail-closed", "aipod-spec-payload-control-bypass-fail-closed", "aipod-spec-missing-gpt-pika-credential", "aipod-spec-durable-task-model-snapshot", "aipod-spec-primary-workspace-contract", "aipod-spec-artificer-github-url-render", "aipod-spec-artificer-github-ssh-required-keys", "aipod-spec-artificer-private-repository-override", "aipod-spec-artificer-private-repository-override-runner-projection", "aipod-spec-git-mirror-url", "git-fetch-stderr-classification", "resource-bundle-source-authority-validation", "resource-bundle-target-conflict-validation", "queue-submit-aipod-dry-run", "session-send-aipod-model-dry-run", "session-send-aipod-cancelled-durable-identity", "session-send-aipod-boundary-conflict-fail-closed", "aipod-cli-help"] };
|
||||||
} finally {
|
} finally {
|
||||||
await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
||||||
}
|
}
|
||||||
@@ -311,6 +372,15 @@ console.log(JSON.stringify({ name: spec.metadata.name, hasBunGlobal: typeof glob
|
|||||||
return JSON.parse(stdout) as JsonRecord;
|
return JSON.parse(stdout) as JsonRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runnerEnvEntry(manifest: JsonRecord, name: string): JsonRecord | undefined {
|
||||||
|
const spec = manifest.spec as JsonRecord;
|
||||||
|
const template = spec.template as JsonRecord;
|
||||||
|
const podSpec = template.spec as JsonRecord;
|
||||||
|
const containers = podSpec.containers as JsonRecord[];
|
||||||
|
const runner = containers[0] as JsonRecord;
|
||||||
|
return (runner.env as JsonRecord[]).find((item) => item.name === name);
|
||||||
|
}
|
||||||
|
|
||||||
async function runCliJson(context: { root: string }, managerUrl: string, args: string[]): Promise<JsonRecord> {
|
async function runCliJson(context: { root: string }, managerUrl: string, args: string[]): Promise<JsonRecord> {
|
||||||
const proc = spawn(process.execPath, [`${context.root}/scripts/agentrun-cli.ts`, "--manager-url", managerUrl, ...args], { stdio: ["ignore", "pipe", "pipe"] });
|
const proc = spawn(process.execPath, [`${context.root}/scripts/agentrun-cli.ts`, "--manager-url", managerUrl, ...args], { stdio: ["ignore", "pipe", "pipe"] });
|
||||||
const [stdout, stderr, code] = await Promise.all([readStream(proc.stdout), readStream(proc.stderr), new Promise<number | null>((resolve) => proc.on("close", resolve))]);
|
const [stdout, stderr, code] = await Promise.all([readStream(proc.stdout), readStream(proc.stderr), new Promise<number | null>((resolve) => proc.on("close", resolve))]);
|
||||||
|
|||||||
Reference in New Issue
Block a user