fix: extend code queue pr preflight diagnostics
This commit is contained in:
@@ -41,7 +41,7 @@ CLI 可以从 `master` 快速演进,但必须兼容 `deploy.json` 固定的 CI
|
||||
- `schedule list|get|runs|run|retry-run|delete|upsert-pgdata-backup` 管理 backend-core 定时任务和运行历史。`schedule list`、`schedule get`、`schedule runs --limit N` 和 `schedule runs <scheduleId> --limit N` 是只读观察入口;`schedule run`、`schedule retry-run`、`schedule delete` 和 `schedule upsert-pgdata-backup` 会触发运行或写入配置,生产恢复时必须有明确授权。`schedule runs --limit N` 是全局历史视图,返回 `scope=global` 和 `scheduleId=null`;`schedule runs <scheduleId> --limit N` 是指定 schedule 历史视图,返回 `scope=schedule` 和对应 `scheduleId`。CLI 必须拒绝 `schedule runs 50` 这类纯数字位置参数,并提示使用 `schedule runs --limit 50`,避免把空数组误判成“没有历史 run”。`schedule run <id> --wait-ms N` 触发同一 schedule,并且即使 wait 超时也必须返回 `newRunId` 和 `observeCommand`;`schedule retry-run <failedRunId>` 只接受 failed run,从原 run 反查 `scheduleId` 后重触发同一 schedule,并输出 `originalRunId`、`scheduleId`、`newRunId` 和 `observeCommand`。当 backend-core 目标容器缺失或只观察到 verify-only 容器时,schedule/microservice 命令必须以非零退出并返回 `failureKind=target-stack-not-running`、`runnerDisposition=infra-blocked`、`readOnlyCommands` 和 `authorizationRequiredForRecovery`,不得把 Docker 的 `No such container` 当成成功的空历史。
|
||||
- `codex deploy <commitId>` 是旧 Code Queue 兼容部署入口,已禁用以防止维护通道直连 D601 部署 Code Queue;当前 dev 自动化只做 `ci run-dev-e2e` smoke,不提供 Code Queue CD,详细规则见 `docs/reference/codex-deploy.md`。
|
||||
- `codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]` 通过 backend-core 私有代理向稳定 `code-queue` 用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,`--dry-run` 只返回结构化请求且不实际入队。dry-run 会额外输出 `routingRecommendation`,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/证据要求等 guard 状态;该建议只用于指挥官 preflight,不会改写 payload,也不假设生产 MiniMax 可用。提交确认和 dry-run 必须返回完整 prompt、字符数和 `truncated=false`,不能套用任务详情的预览截断策略,否则长任务 prompt 无法被人工验收。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或 `code-queue-mgr` 控制面打抖;返回值会附带 `submitConcurrencyGuard` 说明本次提交的锁与等待信息。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQL;D601 scheduler 只轮询并执行已入库任务。
|
||||
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测和可选 push dry-run;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。缺少 env token 时顶层 `ok=false`、`runnerDisposition=infra-blocked`,`tokenCoverage.missing` 同时列出 `GH_TOKEN` 与 `GITHUB_TOKEN`。
|
||||
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测、可选 push dry-run,以及可选 PR body/create dry-run guard;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。缺少 env token 时顶层 `ok=false`、`runnerDisposition=infra-blocked`,`tokenCoverage.missing` 同时列出 `GH_TOKEN` 与 `GITHUB_TOKEN`。`--pr-create-dry-run` 不 POST GitHub,只证明 runner 内 PR body 生成、`scripts/cli.ts gh pr create --dry-run` 和 branch 参数形态可用;服务端创建权限仍以 token auth、repo/issue/PR read、push dry-run 和最终授权后的真实 PR 创建结果为准。
|
||||
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化审阅摘要;默认只返回任务身份、执行 Provider、工作目录、attempt 计数、原始 prompt、最终 response、最后错误和渐进披露命令,适合指挥官审阅完成未读任务且避免上下文爆炸。需要旧式详细摘要时显式加 `--detail`;需要完整 prompt/response 文本时加 `--full`;需要工具调用、judge、attempt 全量摘要时使用 `--detail --full --tool-limit N`。该摘要读取默认由主 server `code-queue-mgr` 从 PostgreSQL 返回,不依赖 D601 `code-queue-read` Service 可用。
|
||||
- `codex tasks [--view supervisor|full] [--queue id] [--status succeeded|running|queued|failed|canceled|judging|retry_wait[,..]] [--unread|--unread-only] [--limit N] [--before-id id]` 通过同一私有代理输出渐进式披露视图。默认 `supervisor` 只返回 `running`、`completedUnread`、`recentCompleted`、`queued` 和 `executionDiagnostics` 摘要,不嵌入完整 Trace、final response 或全量 overview;每个条目都带 `commands.show`、`commands.trace`、`commands.output`、`commands.read` 和 `commands.full`。`--unread` 是 `--unread-only` 的别名,必须只保留未读终态;`--status` 必须真实过滤支持的状态,未知参数或未知状态必须结构化失败,不能静默忽略。需要完整当前页任务简表时显式使用 `--view full` 或 `--full`,仍受 `--limit` 和 `--before-id` 分页约束。
|
||||
- `codex task <taskId> --trace --tail|--from-start|--after-seq N|--before-seq N --limit N` 按页拉取 Code Queue 的逻辑 trace;响应会返回 `nextAfterSeq`、`previousBeforeSeq`、`hasMore`、`hasBefore` 和下一页/上一页命令,默认 `--trace` 取最新一页,且仍以分页 trace 为主;需要完整 prompt/最终 response 时加 `--full`,需要详细 task 摘要时加 `--detail`。
|
||||
|
||||
@@ -61,7 +61,7 @@ D601 原生 k3s 的人工诊断必须显式使用 host kubeconfig:`KUBECONFIG=
|
||||
|
||||
Code Queue worker 可以在任务明确要求审查型交付时创建 Pull Request。PR 交付不是默认出口;默认集成仍遵循项目当前 master-only 规则,直到具体任务或指挥官要求改为 PR。PR 型任务必须从最新目标线创建短生命周期分支,报告源分支、目标分支、PR URL、关联 issue、验证证据和未完成风险;分支命名应使用 `probe/`、`code-queue/` 或其他明确任务前缀,禁止把隐藏分支当成长期交付状态。
|
||||
|
||||
Code Queue runtime 提供 `/api/runtime-preflight` 作为 PR 能力探测入口;CLI 稳定入口是 `bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]`。默认请求只检查本地工具、凭证可见性、Git worktree、HOME、known_hosts、agent port 和 proxy DNS;`--remote` 会增加 GitHub 网络、issue API、SSH/HTTPS `git ls-remote`、GitHub SSH、`gh auth status`、`gh repo view`、`gh issue view` 和只读 `gh pr list` 探测;`--push-dry-run` 会额外执行 `git push --dry-run`,验证远端写权限但不创建分支。探测输出只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在,不得输出 token 内容。backend-core 的稳定 `code-queue` proxy 必须把 `/api/runtime-preflight` 路由到 D601 scheduler,而不是主 server `code-queue-mgr`,因为 token 和 PR runner 能力属于执行面环境。
|
||||
Code Queue runtime 提供 `/api/runtime-preflight` 作为 PR 能力探测入口;CLI 稳定入口是 `bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]`。默认请求只检查本地工具、凭证可见性、Git worktree、HOME、known_hosts、agent port 和 proxy DNS;`--remote` 会增加 GitHub 网络、issue API、SSH/HTTPS `git ls-remote`、GitHub SSH、`gh auth status`、`gh repo view`、`gh issue view` 和只读 `gh pr list` 探测;`--push-dry-run` 会额外执行 `git push --dry-run`,验证远端写权限但不创建分支;`--pr-create-dry-run` 会在 runner 内生成受控 PR body 并执行 `scripts/cli.ts gh pr create --dry-run`,只证明 PR body guard 和命令形态可用,不 POST GitHub。探测输出只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在,不得输出 token 内容。backend-core 的稳定 `code-queue` proxy 必须把 `/api/runtime-preflight` 路由到 D601 scheduler,而不是主 server `code-queue-mgr`,因为 token 和 PR runner 能力属于执行面环境。
|
||||
|
||||
PR 创建依赖以下最小运行时能力:
|
||||
|
||||
@@ -70,7 +70,7 @@ PR 创建依赖以下最小运行时能力:
|
||||
- `CODE_QUEUE_REMOTE_CODEX_ENV_KEYS` 默认允许把 `GH_TOKEN`/`GITHUB_TOKEN` 以及 `GH_HOST`、`GITHUB_API_URL`、`GH_REPO` 从 scheduler 传给 provider dev container,供隔离执行环境内的 `gh repo view`、`gh issue view`、`gh pr list` 和 `gh pr create` 使用。
|
||||
- DEV 的持久注入路径复用 `unidesk-dev-runtime-secrets`:把 `GH_TOKEN` 或 `GITHUB_TOKEN` 写入该 Secret 后,通过受控 `deploy apply --env dev --service code-queue` 或等价 dev-only rollout 重启 Code Queue scheduler/read/write。default/prod runner 使用生产 `unidesk` namespace 的 `code-queue-env` Secret;仅修复 dev Secret 不会覆盖 default queue scheduler。不要在 PROD Code Queue 上直接 patch Secret 或 rollout,生产 token 覆盖需要显式 release/runtime 授权。
|
||||
|
||||
安全验证顺序固定为先只读、再 dry-run、最后才创建真实 PR。优先执行 `/api/runtime-preflight`、`/api/runtime-preflight?remote=1` 和带 `pushDryRun=1` 的 probe ref;只有工具、token、网络和 push dry-run 都满足且任务明确允许时,才创建 draft PR 或普通 PR。若创建真实 probe PR,最终报告必须记录 URL 并说明保留或关闭状态。
|
||||
安全验证顺序固定为先只读、再 dry-run、最后才创建真实 PR。优先执行 `/api/runtime-preflight`、`/api/runtime-preflight?remote=1`、带 `pushDryRun=1` 的 probe ref,以及带 `prCreateDryRun=1` 的 PR body/create dry-run guard;只有工具、token、网络、push dry-run 和 PR body guard 都满足且任务明确允许时,才创建 draft PR 或普通 PR。若创建真实 probe PR,最终报告必须记录 URL 并说明保留或关闭状态。
|
||||
|
||||
preflight failure 必须定位到具体缺失项。已知必须区分的状态包括:`gh`/`hub` 缺失、`GH_TOKEN`/`GITHUB_TOKEN`/`GH_HOST`/`GITHUB_API_URL`/`GH_REPO` 未注入、`/root/.config/gh/hosts.yml` 与 `/root/.git-credentials` 缺失、SSH Git 可读但 GitHub issue/API 匿名访问返回 404、匿名 HTTPS Git 不可用、以及 `HTTP_PROXY`/`HTTPS_PROXY` 指向的 egress proxy 在 runner 内不可解析。发现这类缺口时,worker 应报告 preflight error 和阻塞项,不得强行创建 PR。
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ function fixtureRuntimePreflight(tokenPresent: boolean): JsonRecord {
|
||||
knownHostsPresent: true,
|
||||
privateKeyPresent: true,
|
||||
},
|
||||
prCreateDryRun: tokenPresent ? { command: "sh", args: ["-lc", "bun scripts/cli.ts gh pr create --dry-run"], ok: true, exitCode: 0, signal: null, error: null, stdout: "{\"ok\":true,\"data\":{\"dryRun\":true}}", stderr: "" } : undefined,
|
||||
limitations: tokenPresent ? [] : ["GH_TOKEN/GITHUB_TOKEN is not present; gh cannot create PRs unless another gh credential store is mounted"],
|
||||
risks: [],
|
||||
},
|
||||
@@ -114,6 +115,14 @@ export function runCodeQueuePrPreflightContract(): JsonRecord {
|
||||
const readyTokenCoverage = asRecord(readyPreflight.tokenCoverage);
|
||||
assertCondition(readyTokenCoverage.source === "GH_TOKEN", "ready token source should be redacted to key name only", readyTokenCoverage);
|
||||
|
||||
const prCreateDryRun = codexPrPreflightQueryForTest(["--remote", "--pr-create-dry-run", "--pr-create-dry-run-head", "codequeue/pr-probe"], (path) => {
|
||||
assertCondition(path === "/api/microservices/code-queue/proxy/api/runtime-preflight?remote=1&prCreateDryRun=1&prCreateDryRunHead=codequeue%2Fpr-probe", "PR create dry-run options should map to query string", { path });
|
||||
return fixtureResponse(true);
|
||||
});
|
||||
const prCreateDryRunPreflight = asRecord(asRecord(prCreateDryRun).preflight);
|
||||
const prCreateDryRunProbe = asRecord(prCreateDryRunPreflight.prCreateDryRun);
|
||||
assertCondition(prCreateDryRunProbe.ok === true, "PR create dry-run probe should be compacted", prCreateDryRunProbe);
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
checks: [
|
||||
@@ -121,6 +130,7 @@ export function runCodeQueuePrPreflightContract(): JsonRecord {
|
||||
"missing GitHub token is infra-blocked",
|
||||
"token key names are reported without values",
|
||||
"push dry-run options are forwarded",
|
||||
"PR create dry-run options are forwarded",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -190,6 +190,8 @@ interface CodexPrPreflightOptions {
|
||||
remote: boolean;
|
||||
pushDryRun: boolean;
|
||||
pushDryRunRef: string | undefined;
|
||||
prCreateDryRun: boolean;
|
||||
prCreateDryRunHead: string | undefined;
|
||||
issueNumber: number | null;
|
||||
full: boolean;
|
||||
}
|
||||
@@ -1259,13 +1261,15 @@ function parseQueuesOptions(args: string[]): CodexQueuesOptions {
|
||||
|
||||
function parsePrPreflightOptions(args: string[]): CodexPrPreflightOptions {
|
||||
assertKnownOptions(args, {
|
||||
flags: ["--remote", "--push-dry-run", "--pushDryRun", "--full", "--raw"],
|
||||
valueOptions: ["--push-dry-run-ref", "--pushDryRunRef", "--issue", "--issue-number", "--issueNumber"],
|
||||
flags: ["--remote", "--push-dry-run", "--pushDryRun", "--pr-create-dry-run", "--prCreateDryRun", "--full", "--raw"],
|
||||
valueOptions: ["--push-dry-run-ref", "--pushDryRunRef", "--pr-create-dry-run-head", "--prCreateDryRunHead", "--issue", "--issue-number", "--issueNumber"],
|
||||
}, "codex pr-preflight");
|
||||
return {
|
||||
remote: hasFlag(args, "--remote"),
|
||||
pushDryRun: hasFlag(args, "--push-dry-run") || hasFlag(args, "--pushDryRun"),
|
||||
pushDryRunRef: optionValue(args, ["--push-dry-run-ref", "--pushDryRunRef"]),
|
||||
prCreateDryRun: hasFlag(args, "--pr-create-dry-run") || hasFlag(args, "--prCreateDryRun"),
|
||||
prCreateDryRunHead: optionValue(args, ["--pr-create-dry-run-head", "--prCreateDryRunHead"]),
|
||||
issueNumber: nullablePositiveNumberOption(args, ["--issue", "--issue-number", "--issueNumber"]),
|
||||
full: hasFlag(args, "--full") || hasFlag(args, "--raw"),
|
||||
};
|
||||
@@ -2364,6 +2368,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
|
||||
ghPrReadOnly: compactCommandProbe(remote.ghPrReadOnly),
|
||||
},
|
||||
pushDryRun: compactCommandProbe(pull.pushDryRun),
|
||||
prCreateDryRun: compactCommandProbe(pull.prCreateDryRun),
|
||||
limitations,
|
||||
risks,
|
||||
runnerDisposition: ok ? "ready" : "infra-blocked",
|
||||
@@ -2374,6 +2379,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
|
||||
local: "bun scripts/cli.ts gh auth status --repo pikasTech/unidesk",
|
||||
runner: "bun scripts/cli.ts codex pr-preflight --remote",
|
||||
runnerPushDryRun: "bun scripts/cli.ts codex pr-preflight --remote --push-dry-run --push-dry-run-ref refs/heads/probe/code-queue-pr-capability",
|
||||
runnerPrCreateDryRun: "bun scripts/cli.ts codex pr-preflight --remote --pr-create-dry-run --pr-create-dry-run-head <head-branch>",
|
||||
rawProxy: "bun scripts/cli.ts microservice proxy code-queue /api/runtime-preflight?remote=1 --raw",
|
||||
},
|
||||
};
|
||||
@@ -2387,6 +2393,8 @@ function codeQueuePrPreflight(optionArgs: string[] = [], fetcher: CodexResponseF
|
||||
remote: options.remote ? 1 : undefined,
|
||||
pushDryRun: options.pushDryRun ? 1 : undefined,
|
||||
pushDryRunRef: options.pushDryRunRef,
|
||||
prCreateDryRun: options.prCreateDryRun ? 1 : undefined,
|
||||
prCreateDryRunHead: options.prCreateDryRunHead,
|
||||
issue: options.issueNumber,
|
||||
})}`);
|
||||
const response = unwrapCodexResponse(fetcher(path));
|
||||
|
||||
+2
-2
@@ -49,7 +49,7 @@ export function rootHelp(): unknown {
|
||||
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
|
||||
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Disabled legacy Code Queue deploy path; use the dev-only artifact consumer instead." },
|
||||
{ command: "codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]", description: "Submit a Code Queue task through backend-core -> code-queue proxy; --dry-run shows the structured request without enqueueing." },
|
||||
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]", description: "Read-only PR admission check against the D601 scheduler/runner token, GitHub egress, repo visibility, and optional push dry-run." },
|
||||
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]", description: "Read-only PR admission check against the D601 scheduler/runner token, GitHub egress, repo visibility, optional push dry-run, and PR body/create dry-run guard." },
|
||||
{ command: "codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch the bounded review view by default: original prompt, final response, and drill-down commands; detail and trace are opt-in." },
|
||||
{ command: "codex tasks [--view supervisor|full] [--queue id] [--status status[,status]] [--unread|--unread-only] [--limit N] [--before-id id]", description: "Show the bounded supervisor view by default: running, unread terminal, recent completed, queued, diagnostics, and drill-down commands." },
|
||||
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." },
|
||||
@@ -215,7 +215,7 @@ function codexHelp(): unknown {
|
||||
"bun scripts/cli.ts codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]",
|
||||
"bun scripts/cli.ts codex read <taskId>",
|
||||
"bun scripts/cli.ts codex dev-ready",
|
||||
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]",
|
||||
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]",
|
||||
"bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]",
|
||||
"bun scripts/cli.ts codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--dry-run]",
|
||||
"bun scripts/cli.ts codex interrupt|cancel <taskId>",
|
||||
|
||||
@@ -5284,6 +5284,8 @@ async function route(req: Request): Promise<Response> {
|
||||
includeRemote: url.searchParams.get("remote") === "1",
|
||||
includePushDryRun: url.searchParams.get("pushDryRun") === "1",
|
||||
pushDryRunRef: url.searchParams.get("pushDryRunRef") ?? undefined,
|
||||
includePrCreateDryRun: url.searchParams.get("prCreateDryRun") === "1",
|
||||
prCreateDryRunHead: url.searchParams.get("prCreateDryRunHead") ?? undefined,
|
||||
issueNumber: Number(url.searchParams.get("issue") ?? url.searchParams.get("issueNumber") ?? NaN),
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -9,6 +9,8 @@ export interface RuntimePreflightOptions {
|
||||
includeRemote?: boolean;
|
||||
includePushDryRun?: boolean;
|
||||
pushDryRunRef?: string;
|
||||
includePrCreateDryRun?: boolean;
|
||||
prCreateDryRunHead?: string;
|
||||
issueNumber?: number;
|
||||
}
|
||||
|
||||
@@ -115,6 +117,7 @@ export interface PullRequestDeliveryPreflight {
|
||||
ghPrReadOnly: RuntimeCommandProbe | null;
|
||||
};
|
||||
pushDryRun?: RuntimeCommandProbe;
|
||||
prCreateDryRun?: RuntimeCommandProbe;
|
||||
limitations: string[];
|
||||
risks: string[];
|
||||
}
|
||||
@@ -309,6 +312,55 @@ function httpsGitUrl(repo: string | null, host: string): string | null {
|
||||
return repo === null ? null : `https://${host}/${repo}.git`;
|
||||
}
|
||||
|
||||
function prCreateDryRunProbe(repo: string | null, issueNumber: number, head: string | null): RuntimeCommandProbe {
|
||||
if (repo === null) {
|
||||
return shellProbe("echo 'cannot run PR create dry-run: GitHub repo is unknown' >&2; exit 2", 2000);
|
||||
}
|
||||
const headRef = head?.trim() || "probe/code-queue-pr-capability";
|
||||
const body = [
|
||||
"# Code Queue PR preflight",
|
||||
"",
|
||||
`Related issue: #${issueNumber}`,
|
||||
"",
|
||||
"Validation:",
|
||||
"- runtime PR create dry-run only",
|
||||
"",
|
||||
"Risk:",
|
||||
"- no remote PR is created by this probe",
|
||||
"",
|
||||
].join("\n");
|
||||
const script = [
|
||||
"set -eu",
|
||||
"root=$(git rev-parse --show-toplevel 2>/dev/null || pwd)",
|
||||
"body_file=$(mktemp /tmp/unidesk-pr-create-dry-run.XXXXXX.md)",
|
||||
"trap 'rm -f \"$body_file\"' EXIT",
|
||||
`cat > "$body_file" <<'EOF_PR_PREFLIGHT_BODY'`,
|
||||
body,
|
||||
"EOF_PR_PREFLIGHT_BODY",
|
||||
"cd \"$root\"",
|
||||
"test -f scripts/cli.ts",
|
||||
[
|
||||
"bun",
|
||||
"scripts/cli.ts",
|
||||
"gh",
|
||||
"pr",
|
||||
"create",
|
||||
"--repo",
|
||||
shellQuote(repo),
|
||||
"--title",
|
||||
shellQuote("Code Queue PR preflight dry run"),
|
||||
"--body-file",
|
||||
"\"$body_file\"",
|
||||
"--base",
|
||||
"master",
|
||||
"--head",
|
||||
shellQuote(headRef),
|
||||
"--dry-run",
|
||||
].join(" "),
|
||||
].join("\n");
|
||||
return shellProbe(script, 20_000);
|
||||
}
|
||||
|
||||
function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, checkedAt: string): PullRequestDeliveryPreflight {
|
||||
const tools = {
|
||||
git: toolStatus("git", ["--version"]),
|
||||
@@ -392,6 +444,10 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
|
||||
const pushDryRun = options.includePushDryRun === true
|
||||
? commandProbe("git", ["push", "--dry-run", "origin", `HEAD:${pushDryRunRef}`], { timeoutMs: 30_000 })
|
||||
: undefined;
|
||||
const prCreateDryRunHead = options.prCreateDryRunHead?.trim() || gitInfo.branch || "probe/code-queue-pr-capability";
|
||||
const prCreateDryRun = options.includePrCreateDryRun === true
|
||||
? prCreateDryRunProbe(githubRepo, issueProbeNumber, prCreateDryRunHead)
|
||||
: undefined;
|
||||
const limitations: string[] = [];
|
||||
const risks: string[] = [];
|
||||
if (!tools.git.ok) limitations.push("missing git CLI");
|
||||
@@ -419,12 +475,14 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
|
||||
if (tools.gh.ok && remote.ghPrReadOnly !== null && !remote.ghPrReadOnly.ok) limitations.push("gh pr read-only probe failed");
|
||||
}
|
||||
if (pushDryRun !== undefined && !pushDryRun.ok) limitations.push("git push --dry-run failed for probe branch");
|
||||
if (prCreateDryRun !== undefined && !prCreateDryRun.ok) limitations.push("PR create dry-run body/command guard failed");
|
||||
const prCliReady = tools.gh.ok || tools.hub.ok;
|
||||
const tokenOrGhStore = credentials.ghTokenPresent || credentials.githubTokenPresent || credentials.ghHostsConfigPresent || (remote?.ghAuthStatus?.ok === true);
|
||||
const remoteReady = remote === undefined || (egress.githubDefault.ok && egress.apiDefault.ok && remote.gitLsRemote.ok && remote.githubSshAuthenticated);
|
||||
const pushReady = pushDryRun === undefined || pushDryRun.ok;
|
||||
const prCreateDryRunReady = prCreateDryRun === undefined || prCreateDryRun.ok;
|
||||
return {
|
||||
ok: tools.git.ok && tools.jq.ok && prCliReady && tokenOrGhStore && gitInfo.insideWorktree && gitInfo.remoteOrigin !== null && gitInfo.homeWritable && remoteReady && pushReady,
|
||||
ok: tools.git.ok && tools.jq.ok && prCliReady && tokenOrGhStore && gitInfo.insideWorktree && gitInfo.remoteOrigin !== null && gitInfo.homeWritable && remoteReady && pushReady && prCreateDryRunReady,
|
||||
checkedAt,
|
||||
tools,
|
||||
credentials,
|
||||
@@ -438,6 +496,7 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
|
||||
git: gitInfo,
|
||||
remote,
|
||||
pushDryRun,
|
||||
prCreateDryRun,
|
||||
limitations,
|
||||
risks,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user