@ddtcorex/dsh-maestro-review 0.4.0 → 0.5.0
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 +12 -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 +9 -0
- package/lib/ci-coexist.d.ts.map +1 -0
- package/lib/ci-coexist.js +36 -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 +23 -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.map +1 -1
- package/lib/gitlab-client.js +2 -1
- package/lib/gitlab-client.js.map +1 -1
- package/lib/incremental.d.ts.map +1 -1
- package/lib/incremental.js +3 -2
- package/lib/incremental.js.map +1 -1
- package/lib/orchestrator.d.ts +51 -4
- package/lib/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator.js +201 -59
- package/lib/orchestrator.js.map +1 -1
- package/lib/providers/ci-trigger.d.ts +43 -0
- package/lib/providers/ci-trigger.d.ts.map +1 -0
- package/lib/providers/ci-trigger.js +146 -0
- package/lib/providers/ci-trigger.js.map +1 -0
- package/lib/providers/gitlab.d.ts.map +1 -1
- package/lib/providers/gitlab.js +2 -1
- package/lib/providers/gitlab.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 +4 -8
- 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 +43 -0
- package/src/host/config-store.ts +14 -1
- package/src/host/events.ts +25 -0
- package/src/host/gitlab-auth.ts +13 -0
- package/src/host/gitlab-client.ts +2 -1
- package/src/host/incremental.ts +3 -2
- package/src/host/orchestrator.ts +202 -37
- package/src/host/providers/ci-trigger.ts +164 -0
- package/src/host/providers/gitlab.ts +2 -1
- package/src/host/review-marker.ts +25 -0
- package/src/host/review-signals.ts +4 -3
- package/src/host/settings-rpc.ts +1 -1
- package/templates/reviewer-project.gitlab-ci.yml +90 -0
- package/templates/source-project.gitlab-ci.yml +42 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine-readable review comment identity (spec: coexistence §4-5).
|
|
3
|
+
*
|
|
4
|
+
* Every posted review comment (webhook + CI, quick + deep) ends with an
|
|
5
|
+
* invisible marker so the CI flow can detect "this head SHA already has a
|
|
6
|
+
* completed review" without access to the webhook's history store.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type ReviewFlow = 'ci-quick' | 'ci-deep' | 'webhook'
|
|
10
|
+
|
|
11
|
+
export function reviewMarker(sha: string, flow: ReviewFlow): string {
|
|
12
|
+
return `<!-- maestro-review sha=${sha} flow=${flow} status=completed -->`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function parseReviewMarker(body: string): { sha: string; flow: string; status: string } | undefined {
|
|
16
|
+
const m = /<!-- maestro-review sha=([0-9a-fA-F]+) flow=([a-z-]+) status=([a-z]+) -->/.exec(body)
|
|
17
|
+
if (!m) return undefined
|
|
18
|
+
return { sha: m[1], flow: m[2], status: m[3] }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** CI detection is env-based (established pattern): the reviewer job always sets these. */
|
|
22
|
+
export function resolveFlow(mode: 'quick' | 'deep'): ReviewFlow {
|
|
23
|
+
if (process.env.SOURCE_PROJECT_ID === undefined) return 'webhook'
|
|
24
|
+
return mode === 'deep' ? 'ci-deep' : 'ci-quick'
|
|
25
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { gitlabAuthHeaders } from './gitlab-auth.js'
|
|
1
2
|
/**
|
|
2
3
|
* MR award-emoji acknowledgements: the MR shows "eyes" while a review runs and
|
|
3
4
|
* a final marker when it completes or fails. Signalling is best-effort — any
|
|
@@ -12,7 +13,7 @@ export interface ReviewSignals {
|
|
|
12
13
|
async function award(baseUrl: string, token: string, projectId: number, mrIid: number, name: string): Promise<void> {
|
|
13
14
|
await fetch(`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/award_emoji`, {
|
|
14
15
|
method: 'POST',
|
|
15
|
-
headers: {
|
|
16
|
+
headers: { ...gitlabAuthHeaders(token), 'Content-Type': 'application/json' },
|
|
16
17
|
body: JSON.stringify({ name }),
|
|
17
18
|
})
|
|
18
19
|
}
|
|
@@ -20,7 +21,7 @@ async function award(baseUrl: string, token: string, projectId: number, mrIid: n
|
|
|
20
21
|
/** Remove only this bot's stale running markers; other users' awards stay untouched. */
|
|
21
22
|
async function unawardOwn(baseUrl: string, token: string, projectId: number, mrIid: number, botUsername: string): Promise<void> {
|
|
22
23
|
const response = await fetch(`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/award_emoji`, {
|
|
23
|
-
headers:
|
|
24
|
+
headers: gitlabAuthHeaders(token),
|
|
24
25
|
})
|
|
25
26
|
if (!response.ok) return
|
|
26
27
|
const awards = (await response.json()) as Array<{ id?: number; name?: string; user?: { username?: string } }>
|
|
@@ -28,7 +29,7 @@ async function unawardOwn(baseUrl: string, token: string, projectId: number, mrI
|
|
|
28
29
|
if (awardItem.name !== 'eyes' || awardItem.user?.username !== botUsername || typeof awardItem.id !== 'number') continue
|
|
29
30
|
await fetch(`${baseUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/award_emoji/${awardItem.id}`, {
|
|
30
31
|
method: 'DELETE',
|
|
31
|
-
headers:
|
|
32
|
+
headers: gitlabAuthHeaders(token),
|
|
32
33
|
}).catch(() => {})
|
|
33
34
|
}
|
|
34
35
|
}
|
package/src/host/settings-rpc.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const inject = ['connection', 'maestroTunnel']
|
|
|
12
12
|
/** Keys the Settings card may persist; anything else is a rejected save. */
|
|
13
13
|
const SAVABLE_KEYS = new Set<keyof MaestroUserConfig>([
|
|
14
14
|
'gitlabBaseUrl', 'gitlabToken', 'botUsername', 'webhookSecret', 'webhookPort',
|
|
15
|
-
'projectMappings', 'reviewModel', 'autoRereviewOnPush', 'agentTimeoutMs', 'reviewSessionRetentionDays',
|
|
15
|
+
'projectMappings', 'reviewModel', 'autoRereviewOnPush', 'autoReviewOnAssign', 'agentTimeoutMs', 'reviewSessionRetentionDays',
|
|
16
16
|
'tunnelMode', 'quickTarget', 'tunnelId', 'tunnelCredentialsFile', 'tunnelHostname',
|
|
17
17
|
'proxyPort', 'proxyHost', 'lanPinEnabled', 'telegramBotToken', 'telegramChatId',
|
|
18
18
|
'telegramReviewNotifications',
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ddtcorex/maestro-reviewer — dedicated reviewer project
|
|
2
|
+
stages: [review]
|
|
3
|
+
|
|
4
|
+
variables:
|
|
5
|
+
# Image registry — pin the SHA for reproducibility
|
|
6
|
+
REVIEWER_IMAGE: "ddtcorex/maestro-reviewer:latest"
|
|
7
|
+
# Or use $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA when building the image in the same project
|
|
8
|
+
|
|
9
|
+
review:
|
|
10
|
+
stage: review
|
|
11
|
+
image: $REVIEWER_IMAGE
|
|
12
|
+
# Only runs when triggered from the source project (or manually)
|
|
13
|
+
rules:
|
|
14
|
+
- if: $CI_PIPELINE_SOURCE == "pipeline" # bridge trigger
|
|
15
|
+
- if: $CI_PIPELINE_SOURCE == "trigger" # token trigger
|
|
16
|
+
- if: $CI_PIPELINE_SOURCE == "web" # manual retry
|
|
17
|
+
when: manual
|
|
18
|
+
variables:
|
|
19
|
+
GIT_STRATEGY: none # no need to check out the reviewer repo
|
|
20
|
+
# Every parameter comes from env (SOURCE_PROJECT_ID, MR_IID, GITLAB_HOST, REVIEW_MODE) — no CLI args.
|
|
21
|
+
script:
|
|
22
|
+
- /entrypoint.sh
|
|
23
|
+
# History cache — the push-gate (skip already-reviewed head SHAs) and incremental
|
|
24
|
+
# context read $DSH_HOME/dsh-maestro-review/reviews.json, which is ephemeral in a
|
|
25
|
+
# fresh container. entrypoint.sh symlinks it at this cached path (one store, many
|
|
26
|
+
# MRs — entries are keyed per MR inside). Cache miss fails open toward reviewing.
|
|
27
|
+
cache:
|
|
28
|
+
key: maestro-review-history
|
|
29
|
+
paths:
|
|
30
|
+
- .maestro-history/
|
|
31
|
+
artifacts:
|
|
32
|
+
when: always
|
|
33
|
+
expire_in: 1 week
|
|
34
|
+
paths:
|
|
35
|
+
- review-report.json
|
|
36
|
+
- review-report.md
|
|
37
|
+
# Secrets — Protected + Masked, only available on the reviewer project's protected branches.
|
|
38
|
+
# MAESTRO_GITLAB_TOKEN is REQUIRED and must be a user PAT or Group/Project
|
|
39
|
+
# Access Token (api scope). CI_JOB_TOKEN is NOT enough: proven 2026-09-04 on
|
|
40
|
+
# GitLab 18.11 that reads work (GET=200) but posting the review comment fails
|
|
41
|
+
# (POST /notes=401) even with the source project allowlisting this reviewer —
|
|
42
|
+
# the run would "complete" without ever posting. A human-tied PAT needs yearly
|
|
43
|
+
# rotation; prefer a bot/group token where the instance allows creating one.
|
|
44
|
+
# MAESTRO_GITLAB_TOKEN (api scope) — required
|
|
45
|
+
# Route selection is key-based (entrypoint.sh swaps the baked settings variant):
|
|
46
|
+
# OPENCODE_GO_API_KEY → opencode profile (default, host mirror; serves opencode-go
|
|
47
|
+
# AND deepseek-official-via-zen). Set alone, or with DEEPSEEK_API_KEY (opencode wins).
|
|
48
|
+
# DEEPSEEK_API_KEY → deepseek profile ONLY when OPENCODE_GO_API_KEY is absent
|
|
49
|
+
# (deepseek-official straight from api.deepseek.com — needs a LIVE deepseek.com key).
|
|
50
|
+
# TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID (optional)
|
|
51
|
+
# Opencode model (optional, one variable — default muse-spark-1.3-contributor):
|
|
52
|
+
# OPENCODE_MODEL: "muse-spark-1.3-contributor"
|
|
53
|
+
# Model pin (optional, as a pair — unset both for the host-mirrored default
|
|
54
|
+
# = opencode-go/muse-spark-1.3-contributor, baked in docker/ci-settings.yaml):
|
|
55
|
+
# REVIEW_MODEL_PROVIDER: "opencode-go" # or "deepseek-official"
|
|
56
|
+
# REVIEW_MODEL: "muse-spark-1.3-contributor" # e.g. deepseek-v4-flash (deepseek-official via zen)
|
|
57
|
+
# Only the provider's key is needed — no settings mount required. ID without
|
|
58
|
+
# provider fails the review with a provider error (not silently the wrong
|
|
59
|
+
# model); provider without ID is ignored.
|
|
60
|
+
# Review skill profile (optional — unset for the legacy diff-only generic review):
|
|
61
|
+
# REVIEW_PROFILE: "magento2" # magento2 | laravel | symfony | wordpress | generic
|
|
62
|
+
# The reviewer must load the full skill set or the review fails closed
|
|
63
|
+
# (no half-skilled review). Runtime-backed checks still degrade without a
|
|
64
|
+
# checkout — the profile adds framework checklists, not a govard run.
|
|
65
|
+
# Review mode (default quick; deep runs on demand — no new variables):
|
|
66
|
+
# REVIEW_MODE: "deep" # manual downstream run with the same
|
|
67
|
+
# bridge variables (SOURCE_PROJECT_ID, MR_IID, GITLAB_HOST) + REVIEW_ON_PUSH=1
|
|
68
|
+
# to re-review pushes. The job clones the source at the MR head SHA with
|
|
69
|
+
# MAESTRO_GITLAB_TOKEN (api scope, oauth2) and runs reviewer (+auditor
|
|
70
|
+
# degraded to reviewer-only without a runtime). No govard in the container.
|
|
71
|
+
# CI yields to webhook: if the head SHA already has a completed review
|
|
72
|
+
# comment (either flow), the run skips instead of double-posting.
|
|
73
|
+
# Re-review pushes (optional — mirrors Settings UI autoRereviewOnPush, default off):
|
|
74
|
+
# REVIEW_ON_PUSH: "1" # re-review new pushes with incremental context;
|
|
75
|
+
# unset = only the first (opened) review runs; same head SHA always skips.
|
|
76
|
+
|
|
77
|
+
# Build the image inside the same reviewer project (optional):
|
|
78
|
+
#
|
|
79
|
+
# build:image:
|
|
80
|
+
# stage: .pre
|
|
81
|
+
# image: docker:27
|
|
82
|
+
# services: [docker:27-dind]
|
|
83
|
+
# rules:
|
|
84
|
+
# - if: $CI_COMMIT_BRANCH == "main"
|
|
85
|
+
# script:
|
|
86
|
+
# - docker build -f packages/dsh-maestro-review/docker/Dockerfile -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -t $CI_REGISTRY_IMAGE:latest .
|
|
87
|
+
# - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
88
|
+
# - docker push $CI_REGISTRY_IMAGE:latest
|
|
89
|
+
# # Build context must be the maestro-harness workspace root (contains deepseek-harness/) —
|
|
90
|
+
# # the trailing "." above assumes this job checks out maestro-harness, not this repo alone.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# example-group/example-project — source project (copy this template into YOUR source project)
|
|
2
|
+
stages: [review]
|
|
3
|
+
|
|
4
|
+
# Only runs when there is an MR (fork MRs run fine too — this job has no secrets)
|
|
5
|
+
maestro:trigger-review:
|
|
6
|
+
stage: review
|
|
7
|
+
rules:
|
|
8
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
9
|
+
# No image needed, no secrets needed
|
|
10
|
+
trigger:
|
|
11
|
+
project: ddtcorex/maestro-reviewer # ← reviewer project path
|
|
12
|
+
branch: main # ← the reviewer project's default branch (master on some instances)
|
|
13
|
+
strategy: depend # optional: wait for the reviewer pipeline, surface its status on the MR
|
|
14
|
+
variables:
|
|
15
|
+
SOURCE_PROJECT_ID: $CI_PROJECT_ID
|
|
16
|
+
MR_IID: $CI_MERGE_REQUEST_IID
|
|
17
|
+
GITLAB_HOST: $CI_SERVER_HOST
|
|
18
|
+
SOURCE_SHA: $CI_COMMIT_SHA
|
|
19
|
+
REVIEW_MODE: "quick" # or "deep" for a govard audit
|
|
20
|
+
REVIEW_PROFILE: "generic" # per-project skill profile: magento2 | laravel | symfony | wordpress | generic
|
|
21
|
+
# REVIEW_ON_PUSH: "1" # uncomment to re-review every push (default: only the opened review runs)
|
|
22
|
+
|
|
23
|
+
# Trigger Token fallback (when not using `trigger: project:`):
|
|
24
|
+
#
|
|
25
|
+
# maestro:trigger-review:token:
|
|
26
|
+
# stage: review
|
|
27
|
+
# rules:
|
|
28
|
+
# - if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
29
|
+
# image: curlimages/curl:8
|
|
30
|
+
# variables:
|
|
31
|
+
# REVIEWER_PROJECT_ID: "123" # id of the maestro-reviewer project
|
|
32
|
+
# script:
|
|
33
|
+
# - |
|
|
34
|
+
# curl --fail --request POST \
|
|
35
|
+
# --form token=$MAESTRO_TRIGGER_TOKEN \
|
|
36
|
+
# --form ref=main \
|
|
37
|
+
# --form variables[SOURCE_PROJECT_ID]=$CI_PROJECT_ID \
|
|
38
|
+
# --form variables[MR_IID]=$CI_MERGE_REQUEST_IID \
|
|
39
|
+
# --form variables[GITLAB_HOST]=$CI_SERVER_HOST \
|
|
40
|
+
# "https://${CI_SERVER_HOST}/api/v4/projects/${REVIEWER_PROJECT_ID}/trigger/pipeline"
|
|
41
|
+
# # MAESTRO_TRIGGER_TOKEN is the reviewer project's Trigger Token, stored in the source project
|
|
42
|
+
# # (can only trigger, never read/write code — safer than GITLAB_TOKEN)
|