Merge pull request #941 from pikasTech/fix/v02-agentrun-bundle-source-933

fix: require explicit AgentRun bundle source commit
This commit is contained in:
Lyon
2026-06-05 22:26:21 +08:00
committed by GitHub
3 changed files with 83 additions and 27 deletions
+26 -27
View File
@@ -60,6 +60,7 @@ export function describeAgentRunAdapterAvailability(env = process.env, options =
const blockers = []; const blockers = [];
let managerUrl = null; let managerUrl = null;
let repoUrl = null; let repoUrl = null;
let resourceBundleSourceCommit = null;
try { try {
managerUrl = resolveAgentRunManagerUrl(env); managerUrl = resolveAgentRunManagerUrl(env);
@@ -71,6 +72,11 @@ export function describeAgentRunAdapterAvailability(env = process.env, options =
} catch (error) { } catch (error) {
blockers.push(agentRunAvailabilityBlocker(error, "repo-url")); blockers.push(agentRunAvailabilityBlocker(error, "repo-url"));
} }
try {
resourceBundleSourceCommit = requireAgentRunSourceCommit(env);
} catch (error) {
blockers.push(agentRunAvailabilityBlocker(error, "resource-bundle-source-commit"));
}
const providerId = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID); const providerId = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID);
const runnerNamespace = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE, env.AGENTRUN_RUNTIME_NAMESPACE, DEFAULT_RUNNER_NAMESPACE); const runnerNamespace = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE, env.AGENTRUN_RUNTIME_NAMESPACE, DEFAULT_RUNNER_NAMESPACE);
@@ -145,6 +151,7 @@ export function describeAgentRunAdapterAvailability(env = process.env, options =
managerHost, managerHost,
repoUrl, repoUrl,
repoHost, repoHost,
resourceBundleSourceCommit,
managerConfigured: Boolean(managerUrl), managerConfigured: Boolean(managerUrl),
repoConfigured: Boolean(repoUrl), repoConfigured: Boolean(repoUrl),
internalServiceDns: managerHost === "agentrun-mgr.agentrun-v01.svc.cluster.local", internalServiceDns: managerHost === "agentrun-mgr.agentrun-v01.svc.cluster.local",
@@ -235,6 +242,7 @@ export function initialAgentRunChatResult({ params = {}, options = {}, traceId }
providerId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID), providerId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID),
managerUrl: resolveAgentRunManagerUrl(env), managerUrl: resolveAgentRunManagerUrl(env),
repoUrl: resolveAgentRunRepoUrl(env), repoUrl: resolveAgentRunRepoUrl(env),
resourceBundleSourceCommit: requireAgentRunSourceCommit(env),
valuesPrinted: false valuesPrinted: false
}, },
valuesPrinted: false valuesPrinted: false
@@ -710,22 +718,20 @@ function newSessionIdAfterEviction(baseSessionId, traceId) {
return baseSessionId + "-reset-" + profile; return baseSessionId + "-reset-" + profile;
} }
function buildAgentRunCreateRunInput({ params, env, traceId, backendProfile, sessionId, toolCapabilities = null }) { function buildAgentRunCreateRunInput({ params, env, traceId, backendProfile, sessionId, toolCapabilities = null }) {
const commitId = fullSourceCommit(env); const commitId = requireAgentRunSourceCommit(env);
const threadId = safeOpaqueId(params.threadId); const threadId = safeOpaqueId(params.threadId);
const hwlabProjectId = firstNonEmpty(params.projectId); const hwlabProjectId = firstNonEmpty(params.projectId);
const toolAliases = filteredResourceToolAliases(toolCapabilities); const toolAliases = filteredResourceToolAliases(toolCapabilities);
const resourceBundleRef = commitId const resourceBundleRef = {
? { kind: "git",
kind: "git", repoUrl: resolveAgentRunRepoUrl(env),
repoUrl: resolveAgentRunRepoUrl(env), commitId,
commitId, submodules: false,
submodules: false, lfs: false,
lfs: false, toolAliases,
toolAliases, promptRefs: HWLAB_RESOURCE_PROMPT_REFS,
promptRefs: HWLAB_RESOURCE_PROMPT_REFS, skillRefs: HWLAB_RESOURCE_SKILL_REFS
skillRefs: HWLAB_RESOURCE_SKILL_REFS };
}
: null;
return { return {
tenantId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_TENANT_ID, DEFAULT_TENANT_ID), tenantId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_TENANT_ID, DEFAULT_TENANT_ID),
projectId: agentRunProjectIdForEnv(env), projectId: agentRunProjectIdForEnv(env),
@@ -1705,20 +1711,13 @@ function resolveAgentRunRepoUrl(env = process.env) {
return url.toString().replace(/\/+$/u, ""); return url.toString().replace(/\/+$/u, "");
} }
function fullSourceCommit(env) { function requireAgentRunSourceCommit(env) {
for (const value of [ const text = String(env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT ?? "").trim().toLowerCase();
env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT, if (/^[0-9a-f]{40}$/u.test(text)) return text;
env.HWLAB_BOOT_COMMIT, throw Object.assign(new Error("HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT must be a full 40-character source commit before AgentRun dispatch"), {
env.HWLAB_GITOPS_SOURCE_COMMIT, code: "agentrun_bundle_source_commit_invalid",
env.HWLAB_COMMIT_ID, statusCode: 500
env.HWLAB_GIT_SHA, });
env.GIT_COMMIT,
env.HWLAB_REVISION
]) {
const text = String(value ?? "").trim().toLowerCase();
if (/^[0-9a-f]{40}$/u.test(text)) return text;
}
return null;
} }
function modelForBackendProfile(profile, env = process.env) { function modelForBackendProfile(profile, env = process.env) {
+1
View File
@@ -390,6 +390,7 @@ function buildCodeAgentStructuredReadiness({ provider, dbDurable, sessionRunner,
ready, ready,
adapter: codeAgent?.adapter ?? codeAgent?.agentRun?.adapter ?? null, adapter: codeAgent?.adapter ?? codeAgent?.agentRun?.adapter ?? null,
agentRunReady: agentRun ? codeAgent?.agentRun?.ready === true : null, agentRunReady: agentRun ? codeAgent?.agentRun?.ready === true : null,
resourceBundleSourceCommit: agentRun ? codeAgent?.agentRun?.resourceBundleSourceCommit ?? null : null,
providerReady: provider?.ready === true, providerReady: provider?.ready === true,
durableDbReady: dbDurable?.ready === true, durableDbReady: dbDurable?.ready === true,
sessionRunnerReady: sessionRunner?.ready === true, sessionRunnerReady: sessionRunner?.ready === true,
+56
View File
@@ -725,6 +725,7 @@ test("cloud api health reports AgentRun adapter readiness without repo-owned cod
AGENTRUN_MGR_URL: "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080", AGENTRUN_MGR_URL: "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080",
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "agentrun-v01", HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "G14", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "G14",
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek",
HWLAB_CODE_AGENT_DEEPSEEK_MODEL: "deepseek-chat", HWLAB_CODE_AGENT_DEEPSEEK_MODEL: "deepseek-chat",
HWLAB_BOOT_READ_URL: "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git", HWLAB_BOOT_READ_URL: "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git",
@@ -779,6 +780,7 @@ test("cloud api health reports AgentRun adapter readiness without repo-owned cod
assert.equal(payload.readiness.codeAgent.ready, true); assert.equal(payload.readiness.codeAgent.ready, true);
assert.equal(payload.readiness.codeAgent.adapter, "agentrun-v01"); assert.equal(payload.readiness.codeAgent.adapter, "agentrun-v01");
assert.equal(payload.readiness.codeAgent.agentRunReady, true); assert.equal(payload.readiness.codeAgent.agentRunReady, true);
assert.equal(payload.readiness.codeAgent.resourceBundleSourceCommit, "0123456789abcdef0123456789abcdef01234567");
assert.equal(payload.readiness.codeAgent.codexStdioFeasible, false); assert.equal(payload.readiness.codeAgent.codexStdioFeasible, false);
assert.deepEqual(payload.readiness.codeAgent.currentBlockers, []); assert.deepEqual(payload.readiness.codeAgent.currentBlockers, []);
const serialized = JSON.stringify(payload); const serialized = JSON.stringify(payload);
@@ -795,6 +797,60 @@ test("cloud api health reports AgentRun adapter readiness without repo-owned cod
} }
}); });
test("cloud api health blocks AgentRun adapter when explicit bundle source commit is missing", async () => {
const server = createCloudApiServer({
env: {
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
AGENTRUN_MGR_URL: "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080",
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "G14",
HWLAB_COMMIT_ID: "0123456789abcdef0123456789abcdef01234567",
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek",
HWLAB_BOOT_READ_URL: "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git",
HWLAB_CLOUD_DB_URL: "postgres://hwlab_test:password@db.internal.local:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
dbProbe: {
probe: async ({ endpointSource, timeoutMs }) => ({
attempted: true,
networkAttempted: true,
endpointSource,
probeType: "tcp-connect",
endpointRedacted: true,
valueRedacted: true,
timeoutMs,
durationMs: 1,
result: "connected",
classification: "tcp_connected",
errorCode: null,
missingEnv: []
})
},
runtimeStore: {
async readiness() {
return durableReadyRuntime();
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const response = await fetch(`http://127.0.0.1:${port}/health/live`);
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.codeAgent.status, "blocked");
assert.equal(payload.codeAgent.ready, false);
assert.equal(payload.codeAgent.reason, "agentrun_bundle_source_commit_invalid");
assert.equal(payload.codeAgent.agentRun.ready, false);
assert.deepEqual(payload.codeAgent.blockerCodes, ["agentrun_bundle_source_commit_invalid"]);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api health blocks full Code Agent readiness when Codex stdio command probe fails", async () => { test("cloud api health blocks full Code Agent readiness when Codex stdio command probe fails", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-health-codex-stdio-probe-fail-")); const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-health-codex-stdio-probe-fail-"));
const codexHome = path.join(workspace, "codex-home"); const codexHome = path.join(workspace, "codex-home");