Merge pull request #357 from pikasTech/fix/artificer-private-pr-auth
Pipelines as Code CI / agentrun-nc01-v02-ci-11c764e8d271d2e59351cf4ccad7dfce2d6a799c Success

fix: 声明 Artificer 私有仓 repository override 凭据
This commit is contained in:
Lyon
2026-07-14 19:09:43 +08:00
committed by GitHub
2 changed files with 82 additions and 1 deletions
+11
View File
@@ -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:
+71 -1
View File
@@ -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))]);