@ddtcorex/dsh-maestro-review 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/cordis.patch.yml +18 -0
- package/lib/ci-clone.d.ts +21 -0
- package/lib/ci-clone.d.ts.map +1 -0
- package/lib/ci-clone.js +35 -0
- package/lib/ci-clone.js.map +1 -0
- package/lib/ci-coexist.d.ts +11 -0
- package/lib/ci-coexist.d.ts.map +1 -0
- package/lib/ci-coexist.js +38 -0
- package/lib/ci-coexist.js.map +1 -0
- package/lib/config-store.d.ts +13 -6
- package/lib/config-store.d.ts.map +1 -1
- package/lib/config-store.js.map +1 -1
- package/lib/events.d.ts +27 -0
- package/lib/events.d.ts.map +1 -1
- package/lib/gitlab-auth.d.ts +11 -0
- package/lib/gitlab-auth.d.ts.map +1 -0
- package/lib/gitlab-auth.js +15 -0
- package/lib/gitlab-auth.js.map +1 -0
- package/lib/gitlab-client.d.ts +7 -0
- package/lib/gitlab-client.d.ts.map +1 -1
- package/lib/gitlab-client.js +23 -1
- package/lib/gitlab-client.js.map +1 -1
- package/lib/incremental.d.ts.map +1 -1
- package/lib/incremental.js +4 -2
- package/lib/incremental.js.map +1 -1
- package/lib/orchestrator.d.ts +56 -6
- package/lib/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator.js +243 -68
- package/lib/orchestrator.js.map +1 -1
- package/lib/providers/ci-trigger.d.ts +44 -0
- package/lib/providers/ci-trigger.d.ts.map +1 -0
- package/lib/providers/ci-trigger.js +150 -0
- package/lib/providers/ci-trigger.js.map +1 -0
- package/lib/providers/gitlab.d.ts.map +1 -1
- package/lib/providers/gitlab.js +16 -3
- package/lib/providers/gitlab.js.map +1 -1
- package/lib/review-history.d.ts +2 -1
- package/lib/review-history.d.ts.map +1 -1
- package/lib/review-history.js +67 -40
- package/lib/review-history.js.map +1 -1
- package/lib/review-intake.d.ts +9 -0
- package/lib/review-intake.d.ts.map +1 -1
- package/lib/review-intake.js +34 -1
- package/lib/review-intake.js.map +1 -1
- package/lib/review-marker.d.ts +17 -0
- package/lib/review-marker.d.ts.map +1 -0
- package/lib/review-marker.js +23 -0
- package/lib/review-marker.js.map +1 -0
- package/lib/review-signals.d.ts.map +1 -1
- package/lib/review-signals.js +8 -6
- package/lib/review-signals.js.map +1 -1
- package/lib/settings-rpc.js +1 -1
- package/lib/settings-rpc.js.map +1 -1
- package/package.json +14 -9
- package/profiles/reviewer-ci/cordis.patch.yml +61 -0
- package/profiles/reviewer-ci/package.json +19 -0
- package/profiles/reviewer-ci/pnpm-lock.yaml +62 -0
- package/profiles/reviewer-ci/pnpm-workspace.yaml +12 -0
- package/src/host/ci-clone.ts +54 -0
- package/src/host/ci-coexist.ts +45 -0
- package/src/host/config-store.ts +14 -1
- package/src/host/events.ts +29 -0
- package/src/host/gitlab-auth.ts +13 -0
- package/src/host/gitlab-client.ts +23 -1
- package/src/host/incremental.ts +6 -4
- package/src/host/orchestrator.ts +247 -46
- package/src/host/providers/ci-trigger.ts +168 -0
- package/src/host/providers/gitlab.ts +19 -4
- package/src/host/review-history.ts +65 -35
- package/src/host/review-intake.ts +36 -1
- package/src/host/review-marker.ts +25 -0
- package/src/host/review-signals.ts +9 -6
- package/src/host/settings-rpc.ts +1 -1
- package/templates/reviewer-project.gitlab-ci.yml +90 -0
- package/templates/source-project.gitlab-ci.yml +41 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
lockfileVersion: '9.0'
|
|
2
|
+
|
|
3
|
+
settings:
|
|
4
|
+
autoInstallPeers: true
|
|
5
|
+
excludeLinksFromLockfile: false
|
|
6
|
+
|
|
7
|
+
importers:
|
|
8
|
+
|
|
9
|
+
.:
|
|
10
|
+
dependencies:
|
|
11
|
+
'@ddtcorex/dsh-maestro-review':
|
|
12
|
+
specifier: link:../../packages/dsh-maestro-review
|
|
13
|
+
version: link:../../packages/dsh-maestro-review
|
|
14
|
+
'@ddtcorex/maestro-skills':
|
|
15
|
+
specifier: ^2.11.0
|
|
16
|
+
version: 2.11.0(@deepseek-ai/cordis@4.0.2)
|
|
17
|
+
'@deepseek-ai/dsh-agent-presets':
|
|
18
|
+
specifier: link:../../deepseek-harness/packages/preset/agent-presets
|
|
19
|
+
version: link:../../deepseek-harness/packages/preset/agent-presets
|
|
20
|
+
'@deepseek-ai/dsh-base':
|
|
21
|
+
specifier: link:../../deepseek-harness/packages/bundle/base
|
|
22
|
+
version: link:../../deepseek-harness/packages/bundle/base
|
|
23
|
+
|
|
24
|
+
packages:
|
|
25
|
+
|
|
26
|
+
'@ddtcorex/maestro-skills@2.11.0':
|
|
27
|
+
resolution: {integrity: sha512-S+Nh8jYct1shXuluvveXZIo38wnyTVFMf1A4fNrUi7Ht3Bo6rFio++YqCjf3RL6RIO+mvytTq5ux1hMnc2Ti0w==}
|
|
28
|
+
peerDependencies:
|
|
29
|
+
'@deepseek-ai/cordis': ^4.0.0
|
|
30
|
+
|
|
31
|
+
'@deepseek-ai/cordis@4.0.2':
|
|
32
|
+
resolution: {integrity: sha512-asOnXP1TzFSFQlHb1iegDZp0z/8WD1c7YNrwJR/Tx2bzNuMXfcekE/I67Iv6SQXeLB4csxqCngzQKANP7gdw0g==}
|
|
33
|
+
hasBin: true
|
|
34
|
+
peerDependencies:
|
|
35
|
+
'@deepseek-ai/cordis-plugin-include': ^1.0.7
|
|
36
|
+
'@deepseek-ai/cordis-plugin-loader': ^1.0.3
|
|
37
|
+
peerDependenciesMeta:
|
|
38
|
+
'@deepseek-ai/cordis-plugin-include':
|
|
39
|
+
optional: true
|
|
40
|
+
'@deepseek-ai/cordis-plugin-loader':
|
|
41
|
+
optional: true
|
|
42
|
+
|
|
43
|
+
'@deepseek-ai/cosmokit@1.8.3':
|
|
44
|
+
resolution: {integrity: sha512-qBo+ronVM6Eu2WNVJXi8JcMiqZ19T9BRIpV+5qJUFPXjGH/Z0QKcQMC/IZJ7L394YTOtJgcovbk9qP0w2GsBXQ==}
|
|
45
|
+
|
|
46
|
+
'@standard-schema/spec@1.1.0':
|
|
47
|
+
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
|
48
|
+
|
|
49
|
+
snapshots:
|
|
50
|
+
|
|
51
|
+
'@ddtcorex/maestro-skills@2.11.0(@deepseek-ai/cordis@4.0.2)':
|
|
52
|
+
dependencies:
|
|
53
|
+
'@deepseek-ai/cordis': 4.0.2
|
|
54
|
+
|
|
55
|
+
'@deepseek-ai/cordis@4.0.2':
|
|
56
|
+
dependencies:
|
|
57
|
+
'@deepseek-ai/cosmokit': 1.8.3
|
|
58
|
+
'@standard-schema/spec': 1.1.0
|
|
59
|
+
|
|
60
|
+
'@deepseek-ai/cosmokit@1.8.3': {}
|
|
61
|
+
|
|
62
|
+
'@standard-schema/spec@1.1.0': {}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Marks profiles/reviewer-ci as its OWN standalone pnpm workspace root — without
|
|
2
|
+
# this file, pnpm's workspace-root detection walks up and finds
|
|
3
|
+
# packages/dsh-maestro-review's own pnpm-workspace.yaml two levels up (this
|
|
4
|
+
# profile is nested inside that package's directory tree), silently folding
|
|
5
|
+
# the profile into that unrelated workspace instead of installing it
|
|
6
|
+
# independently (verified 2026-09-03: `pnpm --dir profiles/reviewer-ci install`
|
|
7
|
+
# printed "Scope: all 2 workspace projects" and resolved relative to `../..`
|
|
8
|
+
# without this file present).
|
|
9
|
+
packages: []
|
|
10
|
+
minimumReleaseAgeExclude:
|
|
11
|
+
- '@ddtcorex/dsh-maestro-review@0.3.1'
|
|
12
|
+
- '@ddtcorex/maestro-skills@2.11.0'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import { promisify } from 'node:util'
|
|
3
|
+
import { gitlabAuthHeaders } from './gitlab-auth.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Source checkout for CI deep reviews (spec §3): clone the source project at
|
|
7
|
+
* the MR head SHA with the PAT over oauth2. The token lives only in the clone
|
|
8
|
+
* URL passed to git — errors rethrown here are redacted, and the clone dir is
|
|
9
|
+
* ephemeral (fresh container per job; the orchestrator removes it in `finally`).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type RunFn = (cmd: string, args: string[], cwd?: string) => Promise<void>
|
|
13
|
+
|
|
14
|
+
const execFileAsync = promisify(execFile)
|
|
15
|
+
|
|
16
|
+
/** Production runner (execFile — no shell, argv cannot leak into shell history). */
|
|
17
|
+
export const defaultRun: RunFn = async (cmd, args, cwd) => {
|
|
18
|
+
await execFileAsync(cmd, args, { cwd, timeout: 120_000 })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function redactCloneUrl(url: string): string {
|
|
22
|
+
return url.replace(/oauth2:[^@]+@/, 'oauth2:***@')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function repoHttpUrl(fetcher: typeof fetch, host: string, projectId: number, token: string): Promise<string> {
|
|
26
|
+
const res = await fetcher(`https://${host}/api/v4/projects/${projectId}`, { headers: gitlabAuthHeaders(token) })
|
|
27
|
+
if (!res.ok) throw new Error(`GitLab API error ${res.status}: ${(await res.text()).slice(0, 200)}`)
|
|
28
|
+
const body = (await res.json()) as { http_url_to_repo?: string }
|
|
29
|
+
if (!body.http_url_to_repo) throw new Error('project has no http_url_to_repo')
|
|
30
|
+
return body.http_url_to_repo
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function cloneSourceRepo(opts: {
|
|
34
|
+
fetcher: typeof fetch
|
|
35
|
+
run: RunFn
|
|
36
|
+
host: string
|
|
37
|
+
projectId: number
|
|
38
|
+
sourceBranch: string
|
|
39
|
+
headSha: string
|
|
40
|
+
token: string
|
|
41
|
+
dir: string
|
|
42
|
+
}): Promise<string> {
|
|
43
|
+
const httpUrl = await repoHttpUrl(opts.fetcher, opts.host, opts.projectId, opts.token)
|
|
44
|
+
const authed = httpUrl.replace('https://', `https://oauth2:${opts.token}@`)
|
|
45
|
+
try {
|
|
46
|
+
await opts.run('git', ['clone', '--depth', '50', '--branch', opts.sourceBranch, '--', authed, opts.dir])
|
|
47
|
+
await opts.run('git', ['fetch', '--depth', '50', 'origin', opts.headSha], opts.dir)
|
|
48
|
+
await opts.run('git', ['checkout', '--detach', opts.headSha], opts.dir)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
51
|
+
throw new Error(redactCloneUrl(message))
|
|
52
|
+
}
|
|
53
|
+
return opts.dir
|
|
54
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { gitlabAuthHeaders } from './gitlab-auth.js'
|
|
2
|
+
import { parseReviewMarker } from './review-marker.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Coexistence checks (spec §4): the CI flow yields to a completed review of
|
|
6
|
+
* the same head SHA carrying a review marker (i.e. an earlier CI review —
|
|
7
|
+
* webhook reviews post marker-free and stay invisible here) and to an
|
|
8
|
+
* in-flight review (eyes running-marker). Fetch failures fail open toward
|
|
9
|
+
* running — a missing signal must never block a review, only a present one
|
|
10
|
+
* skips it.
|
|
11
|
+
*/
|
|
12
|
+
export async function hasCompletedReviewForSha(
|
|
13
|
+
fetcher: typeof fetch, baseUrl: string, token: string, projectId: number, mrIid: number, sha: string,
|
|
14
|
+
): Promise<boolean> {
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetcher(
|
|
17
|
+
`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/notes?per_page=100&sort=desc&order_by=created_at`,
|
|
18
|
+
{ headers: gitlabAuthHeaders(token) },
|
|
19
|
+
)
|
|
20
|
+
if (!res.ok) return false
|
|
21
|
+
const notes = (await res.json()) as Array<{ body?: string }>
|
|
22
|
+
return notes.some((n) => {
|
|
23
|
+
const m = typeof n.body === 'string' ? parseReviewMarker(n.body) : undefined
|
|
24
|
+
return m?.sha.toLowerCase() === sha.toLowerCase() && m?.status === 'completed'
|
|
25
|
+
})
|
|
26
|
+
} catch {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function hasRunningEyes(
|
|
32
|
+
fetcher: typeof fetch, baseUrl: string, token: string, projectId: number, mrIid: number,
|
|
33
|
+
): Promise<boolean> {
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetcher(
|
|
36
|
+
`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/award_emoji`,
|
|
37
|
+
{ headers: gitlabAuthHeaders(token) },
|
|
38
|
+
)
|
|
39
|
+
if (!res.ok) return false
|
|
40
|
+
const awards = (await res.json()) as Array<{ name?: string }>
|
|
41
|
+
return awards.some((a) => a.name === 'eyes')
|
|
42
|
+
} catch {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/host/config-store.ts
CHANGED
|
@@ -14,17 +14,30 @@ export interface ReviewModelSelection {
|
|
|
14
14
|
reasoningEffort?: string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface ProjectMapping {
|
|
18
|
+
projectPath: string
|
|
19
|
+
localRepoPath: string
|
|
20
|
+
reviewProfile?: ReviewSkillProfile
|
|
21
|
+
reviewModel?: ReviewModelSelection
|
|
22
|
+
/** Per-project override of autoRereviewOnPush (undefined = inherit global). */
|
|
23
|
+
rereviewOnPush?: boolean
|
|
24
|
+
/** Per-project override of autoReviewOnAssign (undefined = inherit global). */
|
|
25
|
+
reviewOnAssign?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
export interface MaestroUserConfig {
|
|
18
29
|
gitlabBaseUrl?: string
|
|
19
30
|
gitlabToken?: string
|
|
20
31
|
botUsername?: string
|
|
21
32
|
webhookSecret?: string
|
|
22
33
|
webhookPort?: number
|
|
23
|
-
projectMappings?: Array<
|
|
34
|
+
projectMappings?: Array<ProjectMapping>
|
|
24
35
|
/** Override the DSH default model for automated reviews. When absent the global DSH default is used. */
|
|
25
36
|
reviewModel?: ReviewModelSelection
|
|
26
37
|
/** Re-run a quick review whenever new commits land on a previously reviewed MR. */
|
|
27
38
|
autoRereviewOnPush?: boolean
|
|
39
|
+
/** Trigger an automatic quick review when the bot is assigned as MR reviewer (default true). */
|
|
40
|
+
autoReviewOnAssign?: boolean
|
|
28
41
|
/** Bound one automated review agent's turn. */
|
|
29
42
|
agentTimeoutMs?: number
|
|
30
43
|
/** Prune Maestro's own review records older than this many days (0 = keep forever). */
|
package/src/host/events.ts
CHANGED
|
@@ -11,15 +11,44 @@ export type ReviewScope =
|
|
|
11
11
|
| { kind: 'mr' }
|
|
12
12
|
| { kind: 'discussion'; discussionId: string; path: string; line: number }
|
|
13
13
|
|
|
14
|
+
import type { ReviewSkillProfile } from './skills-tool.js'
|
|
15
|
+
|
|
14
16
|
export interface ReviewRequest extends MrOpenedPayload {
|
|
15
17
|
/** `push` arrives only when auto re-review on push is enabled and passes the completed-history gate. */
|
|
16
18
|
trigger: 'reviewer-assignment' | 'mention' | 'push'
|
|
17
19
|
mode: ReviewMode
|
|
18
20
|
scope: ReviewScope
|
|
21
|
+
/**
|
|
22
|
+
* CI-only override (ci-trigger sets it from REVIEW_PROFILE). The GitLab webhook
|
|
23
|
+
* producer never sets it — mapped projects resolve their profile from the mapping.
|
|
24
|
+
* Undefined preserves the legacy diff-only generic review.
|
|
25
|
+
*/
|
|
26
|
+
reviewProfile?: ReviewSkillProfile
|
|
27
|
+
/**
|
|
28
|
+
* CI-only: MR head SHA from fetchMrDetail (ci-trigger sets it). Feeds the
|
|
29
|
+
* comment marker so later CI runs can yield to this review. The GitLab
|
|
30
|
+
* webhook producer never sets it — webhook comments stay marker-free.
|
|
31
|
+
*/
|
|
32
|
+
headSha?: string
|
|
33
|
+
/** Short (8-char) head-sha hint for `push` triggers, taken from the webhook
|
|
34
|
+
* body — feeds the in-flight key so two concurrent pushes stop deduping
|
|
35
|
+
* each other. Absent when the body carries no sha. */
|
|
36
|
+
pushSha?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ReviewResult {
|
|
40
|
+
/** false = the review could not run or failed outright (infra error) — a CI caller should exit non-zero. */
|
|
41
|
+
ok: boolean
|
|
42
|
+
summary?: string
|
|
43
|
+
failures: string[]
|
|
44
|
+
durationMs: number
|
|
19
45
|
}
|
|
20
46
|
|
|
21
47
|
declare module '@deepseek-ai/cordis' {
|
|
22
48
|
interface Events {
|
|
23
49
|
'maestro/review-request': (payload: ReviewRequest) => void
|
|
24
50
|
}
|
|
51
|
+
interface Context {
|
|
52
|
+
reviewRunner: (payload: ReviewRequest) => Promise<ReviewResult>
|
|
53
|
+
}
|
|
25
54
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized GitLab API auth header (spec: token-kind aware).
|
|
3
|
+
*
|
|
4
|
+
* Personal / project / group tokens authenticate with `PRIVATE-TOKEN`.
|
|
5
|
+
* A CI job token (`CI_JOB_TOKEN`, cross-project via the target's job-token
|
|
6
|
+
* allowlist) authenticates with `JOB-TOKEN` instead — `PRIVATE-TOKEN` 401s.
|
|
7
|
+
* The kind comes from `GITLAB_TOKEN_KIND=job` (set by docker/entrypoint.sh
|
|
8
|
+
* when it falls back to `CI_JOB_TOKEN`); anything else means PRIVATE-TOKEN.
|
|
9
|
+
*/
|
|
10
|
+
export function gitlabAuthHeaders(token: string): Record<string, string> {
|
|
11
|
+
if (process.env.GITLAB_TOKEN_KIND === 'job') return { 'JOB-TOKEN': token }
|
|
12
|
+
return { 'PRIVATE-TOKEN': token }
|
|
13
|
+
}
|
|
@@ -3,6 +3,7 @@ import z from '@deepseek-ai/schemastery'
|
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
4
4
|
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
5
5
|
import { join, resolve } from 'node:path'
|
|
6
|
+
import { gitlabAuthHeaders } from './gitlab-auth.js'
|
|
6
7
|
|
|
7
8
|
export const name = 'maestro-gitlab-client'
|
|
8
9
|
export const inject = ['tools']
|
|
@@ -46,6 +47,27 @@ export function selectFileDiff(diffs: GitlabDiff[], path: string): string | unde
|
|
|
46
47
|
return `--- ${d.old_path}\n+++ ${d.new_path}\n${d.diff}`
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* `fetch` with a hard ceiling: aborts the request after `ms` (default 15s)
|
|
52
|
+
* and throws `GitLab API timeout after <ms>ms` on abort. The timer is always
|
|
53
|
+
* cleared on settle. Pre-agent lookups degrade the throw to `undefined` at
|
|
54
|
+
* their own call site; post-agent callers let it reach `writeFailedReport`.
|
|
55
|
+
*/
|
|
56
|
+
export async function fetchWithTimeout(
|
|
57
|
+
url: string, init: RequestInit | undefined, ms = 15_000,
|
|
58
|
+
): Promise<Response> {
|
|
59
|
+
const controller = new AbortController()
|
|
60
|
+
const timer = setTimeout(() => controller.abort(), ms)
|
|
61
|
+
try {
|
|
62
|
+
return await fetch(url, { ...init, signal: controller.signal })
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if ((err as Error)?.name === 'AbortError') throw new Error(`GitLab API timeout after ${ms}ms`)
|
|
65
|
+
throw err
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timer)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
interface GitlabDiffRefs {
|
|
50
72
|
base_sha: string
|
|
51
73
|
start_sha: string
|
|
@@ -119,7 +141,7 @@ export function selectOwnThreads(
|
|
|
119
141
|
|
|
120
142
|
export function apply(ctx: Context, config: Config): void {
|
|
121
143
|
const apiBase = `${config.baseUrl}/api/v4/projects/${config.projectId}/merge_requests/${config.mrIid}`
|
|
122
|
-
const headers =
|
|
144
|
+
const headers = gitlabAuthHeaders(config.token)
|
|
123
145
|
let cachedDiffRefs: GitlabDiffRefs | undefined
|
|
124
146
|
let cachedDiffs: GitlabDiff[] | undefined
|
|
125
147
|
|
package/src/host/incremental.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { gitlabAuthHeaders } from './gitlab-auth.js'
|
|
2
|
+
import { fetchWithTimeout } from './gitlab-client.js'
|
|
1
3
|
export interface CompareCommit {
|
|
2
4
|
short_id?: string
|
|
3
5
|
title?: string
|
|
@@ -55,9 +57,9 @@ export async function fetchMrDetailHeadSha(
|
|
|
55
57
|
baseUrl: string, token: string, projectId: number, mrIid: number,
|
|
56
58
|
): Promise<string | undefined> {
|
|
57
59
|
try {
|
|
58
|
-
const response = await
|
|
60
|
+
const response = await fetchWithTimeout(
|
|
59
61
|
`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}`,
|
|
60
|
-
{ headers:
|
|
62
|
+
{ headers: gitlabAuthHeaders(token) },
|
|
61
63
|
)
|
|
62
64
|
if (!response.ok) return undefined
|
|
63
65
|
const body = await response.json() as { diff_refs?: { head_sha?: string } }
|
|
@@ -72,9 +74,9 @@ export async function fetchCompare(
|
|
|
72
74
|
baseUrl: string, token: string, projectId: number, from: string, to: string,
|
|
73
75
|
): Promise<CompareResult | undefined> {
|
|
74
76
|
try {
|
|
75
|
-
const response = await
|
|
77
|
+
const response = await fetchWithTimeout(
|
|
76
78
|
`${baseUrl}/api/v4/projects/${projectId}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`,
|
|
77
|
-
{ headers:
|
|
79
|
+
{ headers: gitlabAuthHeaders(token) },
|
|
78
80
|
)
|
|
79
81
|
if (!response.ok) return undefined
|
|
80
82
|
return await response.json() as CompareResult
|