feat(gitlab): add @droid fill mode (native parity, no webhook receiver)#94
feat(gitlab): add @droid fill mode (native parity, no webhook receiver)#94factory-nizar wants to merge 29 commits into
Conversation
|
Droid finished @factory-nizar's task —— View job Main correctness issue: the fill GitLab CI rules prevent the advertised description-only trigger path from ever starting when |
|
Droid finished @factory-nizar's task —— View job |
Adds a minimal gitlab/templates/review.yml job that prints a hello-world message. Used to verify that a GitLab CI pipeline can pull the template over HTTPS from raw.githubusercontent.com before we layer in the Docker image and real review entrypoints. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Pure-addition first slice of GitLab support. Mirrors the shape of src/github/ so subsequent entrypoints can swap adapter by PLATFORM env. - src/gitlab/context.ts parses CI_* env vars into a normalized ParsedGitlabContext with MR + commit + user + inputs - src/gitlab/token.ts reads GITLAB_TOKEN with OVERRIDE/CI_JOB_TOKEN fallbacks; throws a clear error when missing - src/gitlab/api/client.ts is a thin fetch wrapper around GitLab v4 exposing getMr/getMrChanges/listNotes/createNote/updateNote/ createDiscussionOnDiff/updateMrDescription - src/gitlab/types.ts defines GitlabMr, GitlabNote, GitlabDiscussion, GitlabPosition - 16 new unit tests; full existing suite (381 -> 397) still passes No GitHub code paths touched. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
src/mcp/gitlab-mr-server.ts mirrors src/mcp/github-pr-server.ts in shape so the review prompt can stay platform-agnostic at the tool layer. Tools registered: - get_mr / get_mr_changes / list_mr_notes - create_mr_note / update_mr_note (sticky tracking comment lifecycle) - update_mr_description (for @droid fill) - submit_review: posts an optional summary note plus N inline discussions anchored to diff positions; per-comment errors are collected so one bad position doesn't abort the batch. Position objects are built from the MR's diff_refs (base/head/start SHA) and switch new_line vs old_line based on RIGHT vs LEFT side, matching the GitHub PR review tool's contract. 5 new unit tests cover summary, inline anchoring, LEFT-side mapping, and partial-failure batching. Full suite: 402/402. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…y-note logic Implements the sticky tracking note lifecycle for GitLab MR pipelines: prepare creates (or reuses) the running-state note before droid exec runs; update-comment-link rewrites it with success/failure + pipeline links after droid exec completes. - src/gitlab/operations/tracking-note.ts builds the note body with a hidden <!-- droid-tracking-note --> marker so retries find and update the same note instead of creating duplicates. Renders pipeline/job links, security badge (when automatic_security_review is on), and an error-details <details> block on failure. - src/entrypoints/gitlab-prepare.ts parses CI env, gates on merge_request_event + automatic_review, writes a JSON state file (DROID_STATE_FILE) for downstream steps. - src/entrypoints/gitlab-update-comment-link.ts reads the state file and PUTs the final body. Skips cleanly when no review ran. 7 new tracking-note tests; full suite 402 -> 409. Also tightened two gitlab-mr-server tests to satisfy noUncheckedIndexedAccess. Cross-SCM include:remote was validated end-to-end against gitlab.com pipeline #2568334623 (passed) before this commit. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Replaces the hello-world include-test template with a real droid-review job. No Docker image dependency for v1; instead the job: 1. Uses the public oven/bun:1.2.11 image (Bun preinstalled) 2. apt-get installs git/curl/ca-certificates on top 3. Shallow-clones Factory-AI/droid-action at $DROID_ACTION_REF 4. bun install --frozen-lockfile 5. Runs src/entrypoints/gitlab-prepare.ts (creates sticky tracking note) 6. droid exec placeholder (real prompt + MCP wiring next commit) 7. after_script always runs src/entrypoints/gitlab-update-comment-link.ts so failures still rewrite the note to the failure state Template uses GitLab's spec.inputs so it works equally well via `include: component:` (post-Catalog publish) and `include: remote:` (today). Inputs: automatic_review, review_depth, review_model, reasoning_effort, droid_action_repo, droid_action_ref, stage. Required CI variables on the consumer side: FACTORY_API_KEY, GITLAB_TOKEN. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The template now installs the Droid CLI, registers the gitlab-mr MCP server with the runtime CI env, and runs `droid exec` against a review prompt that gitlab-prepare writes to /tmp/droid-prompts/droid-prompt.txt. - src/gitlab/review-prompt.ts: minimal v1 review prompt builder embedding the MR diff and instructions to call submit_review with a single batched set of inline comments (max 8). Returns LGTM body when no issues found. - src/entrypoints/gitlab-prepare.ts: after creating the sticky note, fetches MR + diff via the GitLab API and writes the prompt file; exposes promptPath on the state JSON. - gitlab/templates/review.yml: * Installs Droid CLI via curl https://app.factory.ai/cli | sh * Skips droid exec if prepare set shouldRunReview=false * Registers MCP server: droid mcp add gitlab_mr <bun run server> --env ... * Runs: droid exec -f $DROID_PROMPT_FILE --output-format stream-json --skip-permissions-unsafe (plus optional --model/--reasoning-effort) * after_script reads /tmp/droid-error.txt for failure details - 3 new prompt-builder tests; suite at 412 passing. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Previously, review_depth was a declared spec.input that was parsed and
threaded through gitlab-prepare but never consumed by the prompt
builder or the template -- so customers passing review_depth: shallow
silently still got Droid's default model.
This wires it end-to-end:
- gitlab-prepare now calls resolveReviewConfig({reviewModel,
reasoningEffort, reviewDepth}) from src/utils/review-depth.ts (which
already had the preset table: shallow -> kimi-k2-0711, deep -> gpt-5.2
+ high reasoning), reusing the same logic the GitHub flow uses.
- Explicit review_model / reasoning_effort still beat the depth preset
(resolveReviewConfig handles priority).
- Resolved values are written to /tmp/droid-prompts/resolved-env.sh as
shell exports (RESOLVED_MODEL / RESOLVED_REASONING_EFFORT), plus
echoed into the state.json for visibility.
- Template sources the shim before constructing droid exec args and
uses the RESOLVED_* values for --model / --reasoning-effort.
Adds 6 unit tests covering: default deep preset, shallow preset,
explicit model override, explicit effort override, both overrides
simultaneously, unknown depth fallback. Suite at 418 passing.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Replaces the v1 single-pass bespoke prompt with the same two-pass flow the GitHub Action uses: Pass 1 generates review_candidates.json without posting; Pass 2 validates each candidate, writes review_validated.json, and submits approved findings in a single batched call. The /review skill (loaded by Droid at runtime) drives both passes; the prompts here are the runtime harness that selects which pass + which tools. Why two droid exec calls rather than one: 1. Tool gating — Pass 1 has NO access to gitlab_mr___submit_review so the model can't shortcut by posting raw candidates. --enabled-tools is fixed at process start, so a single exec can't switch mid-run. 2. Fresh-eyes context — Pass 2 re-reads the diff without memory of why each candidate was generated, dropping ~30-60% false positives. 3. Hard output checkpoint on disk between passes (resumable, debuggable). 4. Clean failure semantics — half-baked Pass 1 won't pollute Pass 2. New files: * src/gitlab/data/review-artifacts.ts — fetch + write mr.diff, existing_comments.json, mr_description.txt (mirror of GitHub's precomputed PR artifacts). * src/gitlab/prompts/types.ts — shared prompt-context shape. * src/gitlab/prompts/candidates.ts — Pass 1 prompt, ported from GitHub's review-candidates-prompt.ts with MR/project terminology. * src/gitlab/prompts/validator.ts — Pass 2 prompt, ported from GitHub's review-validator-prompt.ts; uses gitlab_mr___submit_review and the new gitlab_mr___update_tracking_note tool. * src/entrypoints/gitlab-prepare-validator.ts — overwrites the prompt file with Pass 2 between the two droid exec calls. * test/gitlab/prompts.test.ts + test/gitlab/review-artifacts.test.ts — 14 new tests covering the two prompts and artifact computation. MCP additions: * src/mcp/gitlab-mr-server.ts — new update_tracking_note tool that reads DROID_MR_IID + DROID_TRACKING_NOTE_ID from env, symmetric with GitHub's github_comment___update_droid_comment. Template changes (gitlab/templates/review.yml): * Sources resolved-env.sh once up front (now includes DROID_MR_IID + DROID_TRACKING_NOTE_ID). * Registers the MCP server with those env vars exposed. * Pass 1 droid exec: --enabled-tools excludes submit_review. * Runs gitlab-prepare-validator between the two execs. * Pass 2 droid exec: --enabled-tools includes gitlab_mr___submit_review and gitlab_mr___update_tracking_note. * Artifacts archive bumped to include review_candidates.json, review_validated.json, droid-prompt.txt for debugging. Removed: * src/gitlab/review-prompt.ts (v1 single-pass bespoke prompt). * test/gitlab/review-prompt.test.ts. Refactoring src/gitlab/prompts/ + src/create-prompt/ into a shared src/core/review/ tree is queued as a follow-up PR. All 432 tests pass; tsc + prettier clean. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds the same `settings` input the GitHub action exposes: * spec.input on the CI/CD Component (JSON string OR path to JSON file). * Plumbed via DROID_SETTINGS env var to gitlab-prepare. * gitlab-prepare reuses base-action/src/setup-droid-settings.ts (zero GitHub deps, fully portable) to write ~/.factory/droid/settings.json before either droid exec call. Always sets enableAllProjectMcpServers=true to match the GitHub flow. * context.inputs.settings surfaces the raw value. * 2 new context tests; 434 tests pass. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…etry, draft skip, stderr capture - 1.6b Copy bundled .factory/droids from droid-action checkout into runner ~/.factory/droids before droid exec, mirroring the GitHub action's Setup Custom Droids step. - 1.6c Stage debug artifacts (/tmp/droid-prompts/*, droid-error.txt) into .droid-debug/ inside CI_PROJECT_DIR in after_script so GitLab can upload them on failure (GitLab artifacts cannot reach outside CI_PROJECT_DIR). - 1.6d Capture droid exec stream-json output to per-pass JSONL log files via tee + pipefail; parse session_id / numTurns / durationMs (+ legacy cost_usd) from the last completion event of each pass; render a <sub> summary line (44 turns • 11m 40s • $0.42) and a collapsible session-IDs block in the sticky tracking note. - C7 Skip Draft: / WIP: MRs at the rules: level (GitLab-native equivalent of GitHub's draft == false workflow if:). - C9 On droid exec failure, tail 60 lines of the per-pass log into /tmp/droid-error.txt so the existing error-details block in the sticky note surfaces real failure context instead of a generic message. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds the include_suggestions spec input to the GitLab review template. Threading was already in place: gitlab-prepare reads the INCLUDE_SUGGESTIONS env var, stores it in the on-disk state, gitlab-prepare-validator restores it, and both candidate + validator prompts already consume includeSuggestions to gate suggestion-block guidance. This commit just exposes the toggle as a customer-facing input. Mirrors the GitHub action's include_suggestions input. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds a cache: block keyed on droid_action_ref so bun's package cache survives across MR pipelines. BUN_INSTALL_CACHE_DIR is pinned to CI_PROJECT_DIR/.bun-cache because GitLab's cache mechanism can only persist paths inside the project directory. policy: pull-push lets every job benefit from prior downloads and contribute newly-fetched packages back to the cache. Mirrors the actions/cache pattern in the GitHub action. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Wraps every GitlabClient.request call with retry-on-transient-failure logic, mirroring the implicit retry behaviour the GitHub action gets from Octokit. Retries are limited to a configurable maxRetries (default 5) for: 408, 429, 500, 502, 503, 504, and raw fetch network errors. On 429 the client honours the Retry-After header when present (seconds or HTTP-date), and otherwise uses exponential backoff with jitter clamped to maxDelayMs. Non-retryable 4xx errors (401, 403, 404, etc.) surface immediately as GitlabApiError. The retry knobs are exposed via a third constructor argument so tests can use sub-millisecond delays without needing fake timers. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds a private paginate<T>() helper that follows GitLab's standard pagination either via the X-Next-Page header (newer GitLab) or Link rel="next" (older GitLab + GitLab.com responses) with per_page=100 to minimise round-trips. listNotes now uses it and a new listDiscussions method is exposed for use cases that need to inspect existing diff discussions. Mirrors Octokit's implicit paginate-everything behaviour that the GitHub action gets for free. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This reverts commit 7c721d1.
…t (C1)" This reverts commit d70dbd7.
This reverts commit 0d6c73b.
Removes two GitLab-only behaviours that don't exist in the GitHub action: - Draft/WIP `rules: when: never` block. GitHub leaves draft handling to the user's workflow `if: ... draft == false` condition and doesn't bake it into the action. Customers who want to skip drafts can add the same condition to their own .gitlab-ci.yml. - Stderr tail-60 capture into droid-error.txt. GitHub uses core.setFailed(error.message) which surfaces in the Actions run summary, not in the PR comment body — the comment just shows an actionFailed flag. Revert to the plain "droid exec pass N failed" message for parity. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Wires the automatic_security_review toggle through the GitLab CI/CD Component. When set to "true", the security-reviewer subagent already bundled in .factory/droids/security-reviewer.md is spawned in parallel with the code-review subagents during Pass 1 (the conditional block in src/gitlab/prompts/candidates.ts already handles this via the securityReviewEnabled flag). Security findings are prefixed with [security] and flow through the same Pass-2 validator path as code-review findings, ultimately posted via a single batched submit_review call. No plugin install is required: the security-review skill that the subagent invokes as its first action is built into the Droid CLI binary. Mirrors the GitHub action's automatic_security_review input for the per-MR flow only; the scheduled full-repo scan (which additionally uses threat-model-generation / commit-security-scan / vulnerability-validation skills from the security-engineer plugin) is a separate feature. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Matches GitHub action's input surface for security_block_on_critical (default "true") and security_block_on_high (default "false"). Like the GitHub action, the inputs are parsed into context but not currently consumed by production code — they exist for surface-level parity and forward compatibility when blocking logic lands. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… Component Documents the manual installation flow for the new GitLab CI/CD Component (`gitlab/templates/review.yml`): prerequisites, full inputs table, what the pipeline produces, how the two-pass review works, what is intentionally not yet supported (comment triggers, fill mode, scheduled scans), and troubleshooting steps. README now has a short GitLab section that points to the full guide. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
9ef6f30 to
6f05311
Compare
4c96254 to
0cac9e6
Compare
| rules: | ||
| # Always-on mode: fire on every MR event and decide via description content. | ||
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $AUTOMATIC_FILL == "true"' | ||
| # Explicit trigger in MR title. | ||
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE =~ /@droid\s+fill/i' | ||
| # Explicit trigger via label. | ||
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /(^|,)\s*droid:fill\s*(,|$)/i' |
There was a problem hiding this comment.
[P0] Make description-triggered fill runnable via rules
The PR description claims @droid fill in the MR description should trigger fill by fetching description via API in gitlab-fill-prepare, but the job rules: currently only allow automatic-fill, title, or label matches; a description-only trigger will never start the job when automatic_fill=false (and the title rule is also hard-coded to @droid, ignoring trigger_phrase). Since GitLab CI rules can’t inspect MR description content, the job needs to run on merge_request_event and let gitlab-fill-prepare decide whether to execute.
| rules: | |
| # Always-on mode: fire on every MR event and decide via description content. | |
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $AUTOMATIC_FILL == "true"' | |
| # Explicit trigger in MR title. | |
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE =~ /@droid\s+fill/i' | |
| # Explicit trigger via label. | |
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /(^|,)\s*droid:fill\s*(,|$)/i' | |
| rules: | |
| - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' |
| return description | ||
| .replace(regex, " ") | ||
| .replace(/\s{2,}/g, " ") | ||
| .trim(); |
There was a problem hiding this comment.
[P1] Don’t collapse MR Markdown when stripping the fill trigger
stripFillTrigger() collapses \s{2,} across the entire description after removing the token; because \s includes newlines, this can remove paragraph breaks and indentation in normal Markdown descriptions (headings/lists/code blocks).
| return description | |
| .replace(regex, " ") | |
| .replace(/\s{2,}/g, " ") | |
| .trim(); | |
| return description | |
| .replace(regex, " ") | |
| .replace(/[ \t]{2,}/g, " ") | |
| .trim(); |
| export function buildFillRegex(triggerPhrase: string): RegExp { | ||
| const escaped = escapeRegExp(triggerPhrase); | ||
| return new RegExp(`(^|\\s)${escaped}\\s+fill([\\s.,!?;:]|$)`, "i"); | ||
| } |
There was a problem hiding this comment.
[P1] Match @droid fill when wrapped in common punctuation
buildFillRegex() currently requires whitespace or start-of-string before the trigger phrase and only allows [\s.,!?;:] after fill, so realistic Markdown like (@droid fill) or `@droid fill` won’t trigger fill even though it contains the literal phrase.
| export function buildFillRegex(triggerPhrase: string): RegExp { | |
| const escaped = escapeRegExp(triggerPhrase); | |
| return new RegExp(`(^|\\s)${escaped}\\s+fill([\\s.,!?;:]|$)`, "i"); | |
| } | |
| export function buildFillRegex(triggerPhrase: string): RegExp { | |
| const escaped = escapeRegExp(triggerPhrase); | |
| return new RegExp( | |
| `(^|[\\s\\(\\[\\{\\x60])${escaped}\\s+fill([\\s\\).,!?;:\\]\\}\\x60]|$)`, | |
| "i", | |
| ); | |
| } |
| --env DROID_MR_IID="$DROID_MR_IID" | ||
| - | | ||
| set -o pipefail | ||
| FILL_TOOLS="Read,Grep,Glob,LS,Execute,Skill,gitlab_mr___update_mr_description" |
There was a problem hiding this comment.
[P1] [security] Remove Execute (and Skill) from fill tool allowlist
Fill mode only needs repository read/search plus gitlab_mr___update_mr_description, but FILL_TOOLS currently includes Execute (and Skill), which materially increases the impact of prompt-injection from MR-controlled inputs (title/description/diff) by enabling arbitrary shell execution and easier secret exfiltration into the updated MR description and/or job artifacts.
| FILL_TOOLS="Read,Grep,Glob,LS,Execute,Skill,gitlab_mr___update_mr_description" | |
| FILL_TOOLS="Read,Grep,Glob,LS,gitlab_mr___update_mr_description" |
Adds two consumer-facing sample files plus a README so users have a
canonical reference for what a Component-consuming .gitlab-ci.yml looks
like:
* gitlab/examples/.gitlab-ci.minimal.yml — shortest possible, every
default accepted, only the two required CI/CD variables wired.
* gitlab/examples/.gitlab-ci.example.yml — annotated, every input
spelled out with safe-default guidance, plus an optional
@droid fill block (commented out) showing how to add fill.
* gitlab/examples/README.md — table mapping each file to its use case
and the variable-setup story.
Also updates docs/gitlab-setup.md:
* Points readers at gitlab/examples/ instead of just inlining the
snippet, so customers can clone the project, copy the file, done.
* Drops the stale 'service account provisioning' reference now that
the install-code-review skill no longer provisions SAs.
* Tightens the GITLAB_TOKEN row to make the poster-identity story
explicit: the token owner IS the poster, no API impersonation.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
0cac9e6 to
e999fd2
Compare
Customer-facing surface cleanup before GA:
* gitlab/templates/review.yml: change droid_action_ref default from
"dev" to "main" so customers who don't set it track stable.
Description annotated 'Internal: most users leave at the default'.
* docs/gitlab-setup.md: drop droid_action_ref, droid_action_repo,
stage from the Inputs table — they're for self-hosted GitLab
mirrors / advanced overrides, not standard customer config.
* docs/gitlab-setup.md: drop everything from 'How it works' onward
(How it works, What's not yet supported, Troubleshooting,
Self-hosted GitLab). Those belong in deeper docs / runbooks; the
setup page should be exactly setup.
* docs/gitlab-setup.md: drop the 'pin to a release tag (e.g. v1)'
advice — droid-action doesn't tag yet, and the @main URL pin is
the canonical pattern.
* gitlab/examples/.gitlab-ci.example.yml: drop droid_action_ref from
the inputs block + drop the same line from the optional fill
snippet. Update the comment to just explain the @main pin.
* gitlab/examples/README.md: drop the 'custom stage' reference now
that stage isn't documented as a user-facing knob.
The hidden inputs still exist in the template with sensible defaults,
so air-gapped customers mirroring droid-action can still override
droid_action_repo or pin droid_action_ref to a private SHA — they just
aren't part of the documented surface.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
e999fd2 to
b5d731e
Compare
Customer-facing surface should describe what is supported, not what isn't. Caveats belong in deeper docs / runbooks if anywhere, not in the README quick-start. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
b5d731e to
f98fdb9
Compare
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
f98fdb9 to
2cfcc20
Compare
…le pattern Component template renamed from gitlab/templates/review.yml to gitlab/templates/droid-review.yml. Customer drops a self-contained factory/droid-review.yml in their project and adds a single `include: - local:` line to their .gitlab-ci.yml, mirroring the GitHub action's `.github/workflows/droid-review.yml` model. Examples restructured to reflect the two-file layout. Docs + README snippets updated accordingly. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2cfcc20 to
6d2d69d
Compare
…compatibility GitLab CI/CD Catalog requires components to live at top-level templates/. Move gitlab/templates/droid-review.yml to templates/droid-review.yml so the repo is ready for Catalog publish (gitlab.com/factory-ai/droid-action) without future renames. Add templates/README.md explaining the directory ownership (mandated by Catalog, parallel to action.yml + .github/workflows/ for GitHub) and a headline comment in droid-review.yml. Update all existing include URLs (README, docs, examples) to the new path. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
6d2d69d to
4e981ba
Compare
Switch the customer-facing include form from a raw GitHub URL to GitLab's native include: project: form pointing at the gitlab.com pull-mirror of this repo. Customers no longer need github.com egress to fetch the template — gitlab.com is sufficient. Also flip the runtime droid_action_repo default from github.com/Factory-AI/droid-action.git to gitlab.com/factory-components/droid-action.git so the at-job clone of the runtime source also goes through gitlab.com. Customers behind a firewall that only permits gitlab.com + app.factory.ai can now run the full flow. Updated: - README.md GitLab quick-start snippet (include: project: form) - docs/gitlab-setup.md Step 2 snippet - gitlab/examples/factory/droid-review.yml - templates/README.md — documents include: project: (default), future include: component: form once Catalog-tagged, and a raw GitHub URL fallback for projects that can't reach gitlab.com - templates/droid-review.yml headline comment + droid_action_repo default Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds GitLab support for `@droid fill` via the three native pipeline-
firing surfaces that don't require a webhook receiver: MR description,
MR title, and labels. Plus an `automatic_fill` always-on mode.
How the trigger works:
* `automatic_fill: "true"` -> droid-fill runs on every MR event
and decides to fill based on description content.
* `@droid fill` in MR title -> matched at rule level via
$CI_MERGE_REQUEST_TITLE.
* `droid:fill` label -> matched at rule level via
$CI_MERGE_REQUEST_LABELS.
* `@droid fill` in MR description -> not matchable at rule level
(description isn't in env), so the job runs and exits early via
state file if no match found in title/labels and the description
fetched from the API doesn't contain the phrase.
After fill completes the prompt instructs the model to strip the
`@droid fill` token from the new description so the next
`merge_request_event` (fired by our own update) does not re-fire fill.
Discussion-comment triggers (`@droid fill` posted as a note on the
MR) still require a webhook receiver because GitLab does not fire CI
on note events. That subset is deliberately deferred.
Files:
* `gitlab/templates/fill.yml` — new GitLab CI/CD Component with
one `droid-fill` job, three trigger rules, MCP registration,
`.droid-debug/` artifact staging.
* `src/gitlab/validation/trigger.ts` — port of GitHub's
`checkContainsTrigger` for the fill path with
`checkContainsFillTrigger` + `stripFillTrigger`.
* `src/gitlab/prompts/fill.ts` — fill prompt mirroring the GitHub
`fill-prompt.ts` but writing back via
`gitlab_mr___update_mr_description` instead of
`github_pr___update_pr_description`.
* `src/entrypoints/gitlab-fill-prepare.ts` — single-pass prepare
that reads the MR description via API, checks the trigger, and
writes the fill prompt + state file.
* `src/gitlab/context.ts` — adds `automaticFill` input and
`mr.labels` parsed from `CI_MERGE_REQUEST_LABELS`.
Tests: 24 new tests across `fill-trigger.test.ts` and
`fill-prompt.test.ts`. 469 pass, typecheck clean.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
4e981ba to
459080a
Compare
Summary
Adds GitLab support for
using GitLab's **native pipeline-firing surfaces**, so no external webhook receiver is required. A new `droid-fill` CI/CD component job fires on `merge_request_event` and decides whether to fill the MR description based on the trigger phrase, a label, or an always-on input. After fill completes, the prompt strips thetoken from the new description so that the nextmerge_request_event(caused by our own update) does not re-trigger fill.Trigger Surfaces
merge_request_eventon title editrules:via$CI_MERGE_REQUEST_TITLE =~ /@droid\s+fill/idroid:filllabelmerge_request_eventon label changerules:via$CI_MERGE_REQUEST_LABELSmerge_request_eventon description editgitlab-fill-prepare.tsautomatic_fill: "true"always-onmerge_request_eventrules:via$AUTOMATIC_FILLChanges
gitlab/templates/fill.yml(new, +125)droid-filljob: threerules:trigger conditions, runtime clone/install of droid-action, Droid CLI install,gitlab_mrMCP registration, state-gateddroid exec, and.droid-debug/+.droid-state.jsonartifact stagingsrc/entrypoints/gitlab-fill-prepare.ts(new, +225)FillPrepareStateconsumed by the CI scriptsrc/gitlab/validation/trigger.ts(new, +92)checkContainsTriggerfor fill:checkContainsFillTrigger(title/description/label/automatic),buildFillRegex,stripFillTrigger, andescapeRegExpsrc/gitlab/prompts/fill.ts(new, +61)gitlab_mr___update_mr_description(description-only; single mutation)src/gitlab/context.ts(+9)automaticFillinput and parsesmr.labelsfromCI_MERGE_REQUEST_LABELStest/gitlab/fill-trigger.test.ts(new, +131)stripFillTriggertest/gitlab/fill-prompt.test.ts(new, +60)test/gitlab/context.test.ts(+2)Implementation Details
gitlab_mr___update_mr_descriptionMCP tool already exists insrc/mcp/gitlab-mr-server.ts; no new MCP tools were added.droid execon"shouldRunFill": truein the state file, so jobs that fire on every MR event but don't actually need to fill exit early.update_mr_descriptioncall, with the enabled tool setRead,Grep,Glob,LS,Execute,Skill,gitlab_mr___update_mr_description.Testing
fill-trigger.test.tsandfill-prompt.test.ts(pluscontext.test.tscoverage).tsc --noEmitclean, Prettier formatted.Manual end-to-end on a real GitLab MR for each trigger surface:
droid-filljob fires → description filled → token stripped.droid:filllabel → same flow.automatic_fill: "true"→ fires on every MR open/push.Breaking Changes
None — all changes are additive (new GitLab files and additive context fields).
Dependencies
Branches off
feat/gitlab-support(PR #93). Once PR #93 merges todev, this PR's base should be retargeted todev.Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>