mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 18:47:40 +00:00
Issue create (including the empty-body fallback and follow-up PATCH), issue body edits, and issue comments passed the body as `--raw-field body=...` on argv. Windows caps the command line at 32,767 chars, so bodies with pasted inline images failed with `spawn ENAMETOOLONG` before reaching GitHub. These calls now use `gh api -X <METHOD> <endpoint> --input -` with a JSON payload on stdin. ghExecFileWithScopeAsync also now forwards `options.stdin` to the child process; it was previously dropped. Fixes #15832 Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
import type {
|
|
GitHubCreateIssueFields,
|
|
GitHubCreateIssueResult
|
|
} from '../../shared/issue-mutation-types'
|
|
import type { IssueSourcePreference } from '../../shared/repo-types'
|
|
import type { LocalGitExecOptions } from './gh-utils'
|
|
import {
|
|
resolveGitHubRepoExecution,
|
|
resolveIssueGitHubApiRepositorySource
|
|
} from './github-api-repository'
|
|
import { acquire, extractExecError, ghExecFileAsync, release } from './gh-utils'
|
|
|
|
function githubIssueErrorMessage(error: unknown): string {
|
|
const { stderr, stdout } = extractExecError(error)
|
|
return stderr.trim() || stdout.trim()
|
|
}
|
|
|
|
/** Explicit owner/repo preserves the Tasks-selected source independently of cwd remotes. */
|
|
export async function createIssue(
|
|
repoPath: string,
|
|
title: string,
|
|
body: string,
|
|
preference?: IssueSourcePreference,
|
|
connectionId?: string | null,
|
|
fields?: GitHubCreateIssueFields,
|
|
localGitOptions: LocalGitExecOptions = {}
|
|
): Promise<GitHubCreateIssueResult> {
|
|
const trimmedTitle = title.trim()
|
|
if (!trimmedTitle) {
|
|
return { ok: false, error: 'Title is required' }
|
|
}
|
|
const { ownerRepo, ghOptions } = await resolveGitHubRepoExecution(
|
|
repoPath,
|
|
async () =>
|
|
(
|
|
await resolveIssueGitHubApiRepositorySource(
|
|
repoPath,
|
|
preference,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
).source,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
if (!ownerRepo) {
|
|
return { ok: false, error: 'Could not resolve GitHub owner/repo for this repository' }
|
|
}
|
|
await acquire()
|
|
try {
|
|
const issuesEndpoint = `repos/${ownerRepo.owner}/${ownerRepo.repo}/issues`
|
|
// Why: stdin keeps large inline-image bodies off argv, which Windows caps at 32K chars.
|
|
const createIssueRequest = (issueBody: string) =>
|
|
ghExecFileAsync(['api', '-X', 'POST', issuesEndpoint, '--input', '-'], {
|
|
...ghOptions,
|
|
stdin: JSON.stringify({
|
|
title: trimmedTitle,
|
|
body: issueBody,
|
|
...(fields?.labels?.length ? { labels: fields.labels } : {}),
|
|
...(fields?.assignees?.length ? { assignees: fields.assignees } : {})
|
|
})
|
|
})
|
|
|
|
const parseIssue = (stdout: string) =>
|
|
JSON.parse(stdout) as { number?: number; html_url?: string; url?: string }
|
|
|
|
let data: { number?: number; html_url?: string; url?: string }
|
|
try {
|
|
const { stdout } = await createIssueRequest(body)
|
|
data = parseIssue(stdout)
|
|
} catch (err) {
|
|
const message = githubIssueErrorMessage(err)
|
|
if (!/body is too long \(maximum is \d+ characters\)/i.test(message)) {
|
|
return { ok: false, error: message }
|
|
}
|
|
|
|
// GitHub can accept a body on update that it rejects during creation; establish the issue first.
|
|
const { stdout } = await createIssueRequest('')
|
|
data = parseIssue(stdout)
|
|
if (typeof data.number !== 'number') {
|
|
return { ok: false, error: 'Unexpected response from GitHub' }
|
|
}
|
|
|
|
try {
|
|
await ghExecFileAsync(
|
|
['api', '-X', 'PATCH', `${issuesEndpoint}/${data.number}`, '--input', '-'],
|
|
{ ...ghOptions, stdin: JSON.stringify({ body }) }
|
|
)
|
|
} catch (patchErr) {
|
|
const patchMessage = githubIssueErrorMessage(patchErr)
|
|
const identity = data.html_url ?? data.url ?? `#${data.number}`
|
|
return {
|
|
ok: true,
|
|
number: data.number,
|
|
url: String(data.html_url ?? data.url ?? ''),
|
|
bodySaveWarning: `Issue ${identity} was created, but saving its body failed: ${patchMessage}`
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeof data.number !== 'number') {
|
|
return { ok: false, error: 'Unexpected response from GitHub' }
|
|
}
|
|
return {
|
|
ok: true,
|
|
number: data.number,
|
|
url: String(data.html_url ?? data.url ?? '')
|
|
}
|
|
} catch (err) {
|
|
return { ok: false, error: githubIssueErrorMessage(err) }
|
|
} finally {
|
|
release()
|
|
}
|
|
}
|