mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
The GitLab, GitHub, Jira and Linear integration modules, their two IPC registrars, and the shared GitHub project types each carried a file-level `eslint-disable max-lines` and ran 351-614 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all eight suppressions and prunes their entries (341 -> 333). Pure move, no behavior change. Each client is cut along the seam it already had: per-operation modules for the issue APIs (create / update / comment / field options), and for Jira the request queue, site credential store, authenticated request, and site identity. The two IPC registrars keep their own handlers and delegate the rest to per-domain sub-registrars, so they remain real entry points rather than re-export shims. The IPC surface is proved intact rather than assumed: comparing (method, channel) multisets between HEAD and the split gives 52 registrations across 52 distinct channels on both sides. Provider-neutrality is preserved -- GitLab and GitHub keep separate, parallel module layouts rather than being merged behind a shared abstraction. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (the one remaining failure is a pre-existing load flake in an untouched file, green when re-run serially), no new runtime import cycles among 744 modules, and no lint suppression added anywhere.
123 lines
3.1 KiB
TypeScript
123 lines
3.1 KiB
TypeScript
import type { GitHubAssignableUser } from '../../shared/github/pull-request-types'
|
|
import type { IssueSourcePreference } from '../../shared/repo-types'
|
|
import type { LocalGitExecOptions } from './gh-utils'
|
|
import {
|
|
resolveGitHubRepoExecution,
|
|
resolveIssueGitHubApiRepositorySource
|
|
} from './github-api-repository'
|
|
import { acquire, ghExecFileAsync, release } from './gh-utils'
|
|
|
|
export async function listLabels(
|
|
repoPath: string,
|
|
preference?: IssueSourcePreference,
|
|
connectionId?: string | null,
|
|
localGitOptions: LocalGitExecOptions = {}
|
|
): Promise<string[]> {
|
|
const { ownerRepo, ghOptions } = await resolveGitHubRepoExecution(
|
|
repoPath,
|
|
async () =>
|
|
(
|
|
await resolveIssueGitHubApiRepositorySource(
|
|
repoPath,
|
|
preference,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
).source,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
if (!ownerRepo) {
|
|
return []
|
|
}
|
|
await acquire()
|
|
try {
|
|
const { stdout } = await ghExecFileAsync(
|
|
[
|
|
'api',
|
|
'--paginate',
|
|
`repos/${ownerRepo.owner}/${ownerRepo.repo}/labels`,
|
|
'--jq',
|
|
'.[].name'
|
|
],
|
|
ghOptions
|
|
)
|
|
return stdout
|
|
.trim()
|
|
.split('\n')
|
|
.filter((l) => l.length > 0)
|
|
} catch {
|
|
return []
|
|
} finally {
|
|
release()
|
|
}
|
|
}
|
|
|
|
export async function listAssignableUsers(
|
|
repoPath: string,
|
|
preference?: IssueSourcePreference,
|
|
connectionId?: string | null,
|
|
localGitOptions: LocalGitExecOptions = {}
|
|
): Promise<GitHubAssignableUser[]> {
|
|
const { ownerRepo, ghOptions } = await resolveGitHubRepoExecution(
|
|
repoPath,
|
|
async () =>
|
|
(
|
|
await resolveIssueGitHubApiRepositorySource(
|
|
repoPath,
|
|
preference,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
).source,
|
|
connectionId,
|
|
localGitOptions
|
|
)
|
|
if (!ownerRepo) {
|
|
return []
|
|
}
|
|
await acquire()
|
|
try {
|
|
// Why: paginate through all assignable users — GraphQL's assignableUsers
|
|
// maxes out at 100 per page and large orgs/repos silently lose assignees
|
|
// beyond the first page. REST /assignees with --paginate walks every page;
|
|
// --jq collapses per-page arrays into NDJSON so we don't have to stitch
|
|
// JSON arrays that gh concatenates back-to-back.
|
|
const { stdout } = await ghExecFileAsync(
|
|
[
|
|
'api',
|
|
'--paginate',
|
|
`repos/${ownerRepo.owner}/${ownerRepo.repo}/assignees?per_page=100`,
|
|
'--jq',
|
|
'.[] | {login, avatar_url}'
|
|
],
|
|
ghOptions
|
|
)
|
|
type RESTAssignee = { login?: string; avatar_url?: string | null }
|
|
const users: GitHubAssignableUser[] = []
|
|
for (const line of stdout.split('\n')) {
|
|
const trimmed = line.trim()
|
|
if (!trimmed) {
|
|
continue
|
|
}
|
|
try {
|
|
const user = JSON.parse(trimmed) as RESTAssignee
|
|
if (user.login) {
|
|
users.push({
|
|
login: user.login,
|
|
name: null,
|
|
avatarUrl: user.avatar_url ?? ''
|
|
})
|
|
}
|
|
} catch {
|
|
// Skip malformed NDJSON lines defensively.
|
|
}
|
|
}
|
|
return users
|
|
} catch {
|
|
return []
|
|
} finally {
|
|
release()
|
|
}
|
|
}
|