fix: support labels for gh issue create
This commit is contained in:
@@ -245,6 +245,32 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
|
||||
sendJson(res, 201, { id: 9001, body: String(parsed.body ?? ""), html_url: "https://github.com/pikasTech/unidesk/issues/20#issuecomment-9001", user: { login: "tester" }, created_at: "2026-05-20T06:00:00Z", updated_at: "2026-05-20T06:00:00Z" });
|
||||
return;
|
||||
}
|
||||
if (req.method === "POST" && req.url === "/repos/pikasTech/unidesk/issues") {
|
||||
const parsed = JSON.parse(body) as JsonRecord;
|
||||
const labels = Array.isArray(parsed.labels) ? parsed.labels.map(String) : [];
|
||||
if (labels.includes("missing-label")) {
|
||||
sendJson(res, 422, {
|
||||
message: "Validation Failed",
|
||||
errors: [{ resource: "Issue", field: "labels", code: "invalid", value: "missing-label" }],
|
||||
documentation_url: "https://docs.github.com/rest/issues/issues#create-an-issue",
|
||||
});
|
||||
return;
|
||||
}
|
||||
sendJson(res, 201, {
|
||||
id: 9100,
|
||||
number: 91,
|
||||
title: String(parsed.title ?? ""),
|
||||
body: String(parsed.body ?? ""),
|
||||
state: "open",
|
||||
html_url: "https://github.com/pikasTech/unidesk/issues/91",
|
||||
comments: 0,
|
||||
user: { login: "tester" },
|
||||
labels: labels.map((name) => ({ name })),
|
||||
created_at: "2026-05-20T06:05:00Z",
|
||||
updated_at: "2026-05-20T06:05:00Z",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (req.method === "DELETE" && req.url === "/repos/pikasTech/unidesk/issues/comments/9001") {
|
||||
res.statusCode = 204;
|
||||
res.end();
|
||||
@@ -415,12 +441,40 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
|
||||
|
||||
const appendFile = join(tmp, "append.md");
|
||||
writeFileSync(appendFile, "\n- appended `code`\n| c | d |\n| --- | --- |\n| 3 | 4 |\n", "utf8");
|
||||
const issueCreateDryRun = await runCli(["gh", "issue", "create", "--repo", "pikasTech/unidesk", "--title", "body file dry-run", "--body-file", appendFile, "--dry-run"], env);
|
||||
const issueCreateRequestCountBeforeDryRun = mock.requests.length;
|
||||
const issueCreateDryRun = await runCli(["gh", "issue", "create", "--repo", "pikasTech/unidesk", "--title", "body file dry-run", "--body-file", appendFile, "--label", "cli,infra", "--label", "ops", "--dry-run"], env);
|
||||
assertCondition(issueCreateDryRun.status === 0, "issue create dry-run should succeed", issueCreateDryRun.json ?? { stdout: issueCreateDryRun.stdout });
|
||||
const issueCreateDryRunData = dataOf(issueCreateDryRun.json ?? {});
|
||||
const issueCreateBodySource = issueCreateDryRunData.bodySource as JsonRecord;
|
||||
assertCondition(issueCreateDryRunData.planned === true && issueCreateBodySource.kind === "body-file" && issueCreateBodySource.path === appendFile, "issue create dry-run should expose body-file source", issueCreateDryRunData);
|
||||
const issueCreateDryRunLabels = issueCreateDryRunData.labels as unknown[];
|
||||
assertCondition(Array.isArray(issueCreateDryRunLabels) && issueCreateDryRunLabels.join(",") === "cli,infra,ops", "issue create dry-run should parse repeated and comma labels", issueCreateDryRunData);
|
||||
const issueCreateDryRunRequest = issueCreateDryRunData.request as JsonRecord;
|
||||
const issueCreateDryRunRequestBody = issueCreateDryRunRequest.body as JsonRecord;
|
||||
assertCondition(Array.isArray(issueCreateDryRunRequestBody.labels) && (issueCreateDryRunRequestBody.labels as unknown[]).join(",") === "cli,infra,ops", "issue create dry-run request plan should include labels", issueCreateDryRunData);
|
||||
assertCondition(issueCreateDryRunData.request && typeof issueCreateDryRunData.request === "object", "issue create dry-run should expose request plan", issueCreateDryRunData);
|
||||
const issueCreateDryRunWriteCount = mock.requests.slice(issueCreateRequestCountBeforeDryRun).filter((request) => request.method === "POST" && request.url === "/repos/pikasTech/unidesk/issues").length;
|
||||
assertCondition(issueCreateDryRunWriteCount === 0, "issue create dry-run must not POST GitHub", { requests: mock.requests.slice(issueCreateRequestCountBeforeDryRun) });
|
||||
|
||||
const issueCreateRequestCountBeforeWrite = mock.requests.length;
|
||||
const issueCreate = await runCli(["gh", "issue", "create", "--repo", "pikasTech/unidesk", "--title", "body file write", "--body-file", appendFile, "--label", "cli", "--label", "infra,ops"], env);
|
||||
assertCondition(issueCreate.status === 0, "issue create with labels should succeed", issueCreate.json ?? { stdout: issueCreate.stdout });
|
||||
const issueCreateData = dataOf(issueCreate.json ?? {});
|
||||
assertCondition(issueCreateData.command === "issue create", "issue create should report command name", issueCreateData);
|
||||
const issueCreateLabels = issueCreateData.labels as unknown[];
|
||||
assertCondition(Array.isArray(issueCreateLabels) && issueCreateLabels.join(",") === "cli,infra,ops", "issue create should report labels", issueCreateData);
|
||||
const issueCreateRequest = mock.requests.slice(issueCreateRequestCountBeforeWrite).find((request) => request.method === "POST" && request.url === "/repos/pikasTech/unidesk/issues");
|
||||
assertCondition(issueCreateRequest !== undefined, "issue create should POST to GitHub REST issues endpoint", { requests: mock.requests.slice(issueCreateRequestCountBeforeWrite) });
|
||||
const issueCreatePayload = JSON.parse(issueCreateRequest?.body ?? "{}") as JsonRecord;
|
||||
assertCondition(Array.isArray(issueCreatePayload.labels) && (issueCreatePayload.labels as unknown[]).join(",") === "cli,infra,ops", "issue create REST payload should include labels", issueCreatePayload);
|
||||
|
||||
const issueCreateMissingLabel = await runCli(["gh", "issue", "create", "--repo", "pikasTech/unidesk", "--title", "bad label", "--body-file", appendFile, "--label", "missing-label"], env);
|
||||
assertCondition(issueCreateMissingLabel.status !== 0, "issue create missing label should fail structurally", issueCreateMissingLabel.json ?? { stdout: issueCreateMissingLabel.stdout });
|
||||
const missingLabelData = failedDataOf(issueCreateMissingLabel.json ?? {});
|
||||
assertCondition(missingLabelData.degradedReason === "validation-failed", "missing label should map to validation-failed", missingLabelData);
|
||||
const missingLabelDetails = missingLabelData.details as JsonRecord;
|
||||
const missingLabelNestedDetails = missingLabelDetails.details as JsonRecord;
|
||||
assertCondition(Array.isArray(missingLabelNestedDetails.errors), "missing label error should preserve GitHub validation errors", missingLabelData);
|
||||
|
||||
const appendDryRun = await runCli(["gh", "issue", "update", "20", "--repo", "pikasTech/unidesk", "--mode", "append", "--body-file", appendFile, "--dry-run"], env);
|
||||
assertCondition(appendDryRun.status === 0, "issue update append dry-run should succeed", appendDryRun.json ?? { stdout: appendDryRun.stdout });
|
||||
@@ -464,7 +518,8 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
|
||||
"issue list default fields include labels and filter pull requests",
|
||||
"issue scan-escape classifies pollution, explanatory mentions, and body risks",
|
||||
"issue cleanup-plan remains dry-run with body/comment cleanup suggestions",
|
||||
"issue create dry-run exposes body-file source and request plan",
|
||||
"issue create dry-run parses repeated/comma labels and exposes request plan",
|
||||
"issue create sends labels through REST and preserves GitHub validation errors for missing labels",
|
||||
"issue list unsupported fields and states fail structurally",
|
||||
"issue view supports body,title,state,comments selection",
|
||||
"unsupported --json fields fail structurally",
|
||||
|
||||
Reference in New Issue
Block a user