Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,39 @@ The guided flow will:

For GitHub-only setups you can also run `/install-github-app`. See the [Automated Code Review guide](https://docs.factory.ai/guides/droid-exec/code-review) and the [GitHub App installation guide](https://docs.factory.ai/cli/features/install-github-app) for full details.

### GitLab

GitLab support ships as a **GitLab CI/CD Component** that delivers automated code review — inline MR comments on every merge request, with optional security review.

Two files in your project:

`factory/droid-review.yml`:

```yaml
include:
- project: "factory-components/droid-action"
ref: main
file: "/templates/droid-review.yml"
inputs:
automatic_review: "true"
automatic_security_review: "false"
review_depth: "deep"

droid-review:
variables:
FACTORY_API_KEY: $FACTORY_API_KEY
GITLAB_TOKEN: $GITLAB_TOKEN
```

`.gitlab-ci.yml` (one include line, append to existing if present):

```yaml
include:
- local: "factory/droid-review.yml"
```

Full setup, available inputs, and troubleshooting live in [`docs/gitlab-setup.md`](docs/gitlab-setup.md).

### Manual Setup

If you prefer to wire things up by hand:
Expand Down
106 changes: 106 additions & 0 deletions docs/gitlab-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# GitLab Setup

This action ships a **GitLab CI/CD Component** that delivers the same
automated code-review experience as the GitHub action on GitLab merge
requests (MRs). The component runs on every `merge_request_event` pipeline,
posts inline comments on the diff, maintains a sticky tracking note, and
optionally runs a security-focused subagent in parallel.

## Quick start with `/install-code-review`

The fastest path is the guided installer built into the Droid CLI:

```bash
droid
> /install-code-review
```

It detects GitLab, asks which account should be the poster of review
comments (you supply its PAT as `GITLAB_TOKEN`), asks the configuration
questions below, drops `factory/droid-review.yml` in your project, wires
it into `.gitlab-ci.yml`, and opens an MR / direct-commits to the target
project(s).

## Manual installation

### 1. Prerequisites

| Requirement | How to get it |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GitLab Maintainer role on the project | Repo admin grants you Maintainer (40) |
| `FACTORY_API_KEY` CI/CD variable | Generate at <https://app.factory.ai/settings/api-keys>; add as **masked**, **unprotected** variable at the project, subgroup, or top-level group level |
| `GITLAB_TOKEN` CI/CD variable | A personal access token with the `api` scope, owned by whichever account should post review comments. The token owner is the poster — there is no API impersonation. Add as **masked**, **unprotected**. |

### 2. Add the CI/CD Component

Drop-in samples live in [`gitlab/examples/`](../gitlab/examples/). The
layout is two files:

- [`factory/droid-review.yml`](../gitlab/examples/factory/droid-review.yml) — self-contained config (include + inputs + variables). Drop verbatim.
- [`.gitlab-ci.yml`](../gitlab/examples/.gitlab-ci.yml) — project-root entry point. If you already have one, append the include line below to its `include:` block.

**`factory/droid-review.yml`** (drop into your project):

```yaml
include:
- project: "factory-components/droid-action"
ref: main
file: "/templates/droid-review.yml"
inputs:
automatic_review: "true"
automatic_security_review: "false"
review_depth: "deep"
include_suggestions: "true"
security_block_on_critical: "true"
security_block_on_high: "false"

droid-review:
variables:
FACTORY_API_KEY: $FACTORY_API_KEY
GITLAB_TOKEN: $GITLAB_TOKEN
```

**`.gitlab-ci.yml`** (project root, just needs the one include line):

```yaml
include:
- local: "factory/droid-review.yml"
```

> The remote `include:` URL is pinned to `@main`, which tracks the
> latest stable cut of droid-action.

### 3. Push an MR

Open or push to an MR. The next `merge_request_event` pipeline will run
the `droid-review` job. Expect ~5-10 minutes for a typical change.

## Inputs

| Input | Default | Description |
| ---------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `automatic_review` | `"true"` | Run code review automatically on every MR pipeline. |
| `automatic_security_review` | `"false"` | Run a parallel security-focused subagent on every MR pipeline. Findings are prefixed `[security]` and posted alongside code-review comments. |
| `review_depth` | `"deep"` | `"deep"` (thorough) or `"shallow"` (fast). |
| `review_model` | `""` | Override the model. Empty = use depth preset. |
| `reasoning_effort` | `""` | Override reasoning effort. Empty = use depth preset. |
| `include_suggestions` | `"true"` | Include code suggestion blocks in review comments when the fix is high-confidence. |
| `security_block_on_critical` | `"true"` | Block merge on CRITICAL security findings. (Mirrors GitHub action; surface-level parity.) |
| `security_block_on_high` | `"false"` | Block merge on HIGH security findings. (Mirrors GitHub action; surface-level parity.) |
| `settings` | `""` | Droid Exec settings as a JSON string or a path to a JSON file. Merged into `~/.factory/droid/settings.json` before each `droid exec` call. |

## What you get

Each MR pipeline produces:

- **Inline review comments** anchored to the relevant diff lines, posted in a
single batched `submit_review` call. Findings are prefixed with priority
tags (`P0`, `P1`, `P2`, `P3`) and `[security]` for security findings.
- **A sticky tracking note** on the MR with pipeline + job links, telemetry
(`N turns • Xm Ys`), session IDs, and a security badge when
`automatic_security_review` is enabled.
- **Debug artifacts** at `.droid-debug/` (prompts, candidate JSON, raw
stream-json logs) retained for 1 week.
- **A custom droid library** copied from
`$DROID_ACTION_DIR/.factory/droids` into `~/.factory/droids` on the
runner, so subagents like `security-reviewer` are reachable.
9 changes: 9 additions & 0 deletions gitlab/examples/.gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Example project-root .gitlab-ci.yml.
#
# If the project doesn't have a .gitlab-ci.yml yet, create one with at
# minimum the include line below. If it already has one, just append the
# `- local: "factory/droid-review.yml"` entry to its existing `include:`
# block (or add a new `include:` block if there isn't one).

include:
- local: "factory/droid-review.yml"
21 changes: 21 additions & 0 deletions gitlab/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# GitLab examples

Two-file layout for consuming the droid-action GitLab CI/CD Component:

```
your-project/
├── .gitlab-ci.yml # gains one `include:` line
└── factory/
└── droid-review.yml # self-contained droid-review config
```

| File | Where it lives in your project | Purpose |
| ----------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `factory/droid-review.yml` | drop verbatim | Self-contained config: includes the remote Component, sets inputs, wires CI/CD variables. |
| `.gitlab-ci.yml` | append one `include:` line if the file exists | Project-root entry point. Just needs to include `factory/droid-review.yml`. |

The two required CI/CD variables (`FACTORY_API_KEY`, `GITLAB_TOKEN`) are set
in the GitLab UI under **Project → Settings → CI/CD → Variables** (or at
the group level for org-wide rollout), masked and unprotected.

For the full input reference see the docs at [`docs/gitlab-setup.md`](../../docs/gitlab-setup.md).
31 changes: 31 additions & 0 deletions gitlab/examples/factory/droid-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# factory/droid-review.yml
#
# Drop this file at `factory/droid-review.yml` in your project, then
# add a single `include: - local: "factory/droid-review.yml"` line to
# your `.gitlab-ci.yml` (see ../.gitlab-ci.yml for the entry-point
# example).
#
# Prerequisites (one-time, set in Project / Group → Settings → CI/CD):
#
# * FACTORY_API_KEY — masked variable. Get one at
# https://app.factory.ai/settings/api-keys
# * GITLAB_TOKEN — masked variable. A personal access token with
# `api` scope, owned by whichever GitLab account should post review
# comments. The token owner IS the poster.

include:
- project: "factory-components/droid-action"
ref: main
file: "/templates/droid-review.yml"
inputs:
automatic_review: "true" # run on every MR event; "false" disables
review_depth: "deep" # "deep" (thorough) or "shallow" (fast)
include_suggestions: "true" # post code-suggestion blocks for high-confidence fixes
automatic_security_review: "false" # enable to flag vulns alongside the regular review
security_block_on_critical: "true" # block merge on CRITICAL findings (when security review is on)
security_block_on_high: "false" # block merge on HIGH findings

droid-review:
variables:
FACTORY_API_KEY: $FACTORY_API_KEY
GITLAB_TOKEN: $GITLAB_TOKEN
25 changes: 25 additions & 0 deletions src/core/review/artifacts/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Shared shape for the three on-disk review artifacts. Both GitHub and
* GitLab pipelines pre-compute the same trio (diff, existing comments,
* description) and write them under `${tempDir}/droid-prompts/`, then
* pass the resulting paths into the Pass 1 / Pass 2 prompts.
*
* The fetch mechanics differ substantially per platform (git+gh CLI vs
* REST API), so we don't try to share the fetchers — only the path
* shape and the disk-write helper.
*/
export type ReviewArtifactPaths = {
diffPath: string;
commentsPath: string;
descriptionPath: string;
};

/**
* Raw content for each of the three artifacts, before they're written
* to disk by `writeReviewArtifacts`.
*/
export type ReviewArtifactContents = {
diff: string;
comments: unknown; // JSON-serializable
description: string;
};
41 changes: 41 additions & 0 deletions src/core/review/artifacts/write.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as fs from "fs/promises";
import * as path from "path";
import type { ReviewArtifactContents, ReviewArtifactPaths } from "./types";

/**
* Naming convention for review-artifact files on disk. Each platform
* gets its own basename for the diff and description (pr.diff /
* mr.diff, pr_description.txt / mr_description.txt) but the existing
* comments file is platform-neutral.
*/
export type ReviewArtifactNames = {
diff: string;
comments: string;
description: string;
};

/**
* Write the three review-artifact files into `outDir` in parallel,
* creating `outDir` if it doesn't yet exist. Returns the resolved
* on-disk paths so the caller can hand them straight to the prompt
* builder.
*/
export async function writeReviewArtifacts(
outDir: string,
contents: ReviewArtifactContents,
names: ReviewArtifactNames,
): Promise<ReviewArtifactPaths> {
await fs.mkdir(outDir, { recursive: true });

const diffPath = path.join(outDir, names.diff);
const commentsPath = path.join(outDir, names.comments);
const descriptionPath = path.join(outDir, names.description);

await Promise.all([
fs.writeFile(diffPath, contents.diff),
fs.writeFile(commentsPath, JSON.stringify(contents.comments, null, 2)),
fs.writeFile(descriptionPath, contents.description),
]);

return { diffPath, commentsPath, descriptionPath };
}
Loading