Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b45a902
security review github action
shashank-factory Dec 29, 2025
7da0757
chore: remove formatting-only changes
shashank-factory Dec 29, 2025
f346d30
security skills scanning
shashank-factory Jan 2, 2026
105eba0
fix: remove invalid git tool identifiers from security review
shashank-factory Jan 2, 2026
752a9c2
chore: remove formatting-only changes from security review feature
shashank-factory Jan 2, 2026
5f6d3c5
fix: address review feedback for security scan
shashank-factory Jan 3, 2026
409e980
fix: correct skills paths from security/ to skills/ in Factory-AI/ski…
shashank-factory Jan 3, 2026
bd513b4
feat: change @droid security-review command to @droid security
shashank-factory Jan 3, 2026
f248d11
feat: show 'running a security check' message for security commands
shashank-factory Jan 3, 2026
9b65808
feat: consolidate review output to single tracking comment
shashank-factory Jan 3, 2026
a8ef7db
feat: add modular composite actions for parallel review workflow
shashank-factory Jan 3, 2026
a6b7426
fix: allow parallel automatic code and security reviews
shashank-factory Jan 3, 2026
91d0aa3
fix: simplify detect step to use prepare outputs
shashank-factory Jan 3, 2026
cd4e10a
fix: each job obtains its own GitHub token via OIDC
shashank-factory Jan 3, 2026
e239e45
fix: use heredoc format for multi-line GITHUB_OUTPUT
shashank-factory Jan 3, 2026
91ebe00
fix: use core.setOutput instead of manual GITHUB_OUTPUT writing
shashank-factory Jan 3, 2026
3cd3339
fix: exclude MCP tools from --enabled-tools flag
shashank-factory Jan 3, 2026
e4fa2be
feat: separate inline comments to finalize step to avoid overlaps
shashank-factory Jan 3, 2026
93491a2
fix: remove github_pr___submit_review from combine step to prevent du…
shashank-factory Jan 3, 2026
eb47cb5
fix: use 'Code review completed' title for combined summary
shashank-factory Jan 3, 2026
7422343
feat: add @droid review/security command triggers for parallel workflow
shashank-factory Jan 3, 2026
57c8669
updated prompt
shashank-factory Jan 7, 2026
8ca3f06
formatted
shashank-factory Jan 7, 2026
f73f7ea
added github workflow
shashank-factory Jan 7, 2026
c10a055
merged dev
shashank-factory Jan 13, 2026
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
118 changes: 114 additions & 4 deletions .github/workflows/droid-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,133 @@ on:
types: [opened, ready_for_review, reopened]

jobs:
droid-review:
prepare:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
outputs:
comment_id: ${{ steps.prepare.outputs.comment_id }}
run_code_review: ${{ steps.prepare.outputs.run_code_review }}
run_security_review: ${{ steps.prepare.outputs.run_security_review }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Run Droid Auto Review
uses: Factory-AI/droid-action@v1
- name: Prepare
id: prepare
uses: Factory-AI/droid-action/prepare@v1
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
automatic_review: true
automatic_security_review: true

code-review:
needs: prepare
if: needs.prepare.outputs.run_code_review == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Run Code Review
uses: Factory-AI/droid-action/review@v1
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
output_file: ${{ runner.temp }}/code-review-results.json

- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: code-review-results
path: ${{ runner.temp }}/code-review-results.json
if-no-files-found: ignore

security-review:
needs: prepare
if: needs.prepare.outputs.run_security_review == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Run Security Review
uses: Factory-AI/droid-action/security@v1
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
security_severity_threshold: medium
output_file: ${{ runner.temp }}/security-results.json

- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: security-results
path: ${{ runner.temp }}/security-results.json
if-no-files-found: ignore

combine:
needs: [prepare, code-review, security-review]
# Only run combine when BOTH code review AND security review were executed
if: |
always() &&
needs.prepare.outputs.run_code_review == 'true' &&
needs.prepare.outputs.run_security_review == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Download Code Review Results
uses: actions/download-artifact@v4
with:
name: code-review-results
path: ${{ runner.temp }}
continue-on-error: true

- name: Download Security Results
uses: actions/download-artifact@v4
with:
name: security-results
path: ${{ runner.temp }}
continue-on-error: true

- name: Combine Results
uses: Factory-AI/droid-action/combine@v1
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
code_review_results: ${{ runner.temp }}/code-review-results.json
security_results: ${{ runner.temp }}/security-results.json
code_review_status: ${{ needs.code-review.result }}
security_review_status: ${{ needs.security-review.result }}
42 changes: 22 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

This GitHub Action powers the Factory **Droid** app. It watches your pull requests for the two supported commands and runs a full Droid Exec session to help you ship faster:

* `@droid fill` — turns a bare pull request into a polished description that matches your template or our opinionated fallback.
* `@droid review` — performs an automated code review, surfaces potential bugs, and leaves inline comments directly on the diff.
- `@droid fill` — turns a bare pull request into a polished description that matches your template or our opinionated fallback.
- `@droid review` — performs an automated code review, surfaces potential bugs, and leaves inline comments directly on the diff.

Everything runs inside GitHub Actions using your Factory API key, so the bot never leaves your repository and operates with the permissions you grant.

Expand All @@ -18,11 +18,11 @@ Everything runs inside GitHub Actions using your Factory API key, so the bot nev
## Installation

1. **Install the Droid GitHub App**
* Install from the Factory dashboard and grant it access to the repositories where you want Droid to operate.
- Install from the Factory dashboard and grant it access to the repositories where you want Droid to operate.
2. **Create a Factory API Key**
* Generate a token at [https://app.factory.ai/settings/api-keys](https://app.factory.ai/settings/api-keys) and save it as `FACTORY_API_KEY` in your repository or organization secrets.
- Generate a token at [https://app.factory.ai/settings/api-keys](https://app.factory.ai/settings/api-keys) and save it as `FACTORY_API_KEY` in your repository or organization secrets.
3. **Add the Action Workflows**
* Create two workflow files under `.github/workflows/` to separate on-demand tagging from automatic PR reviews.
- Create two workflow files under `.github/workflows/` to separate on-demand tagging from automatic PR reviews.

`droid.yml` (responds to explicit `@droid` mentions):

Expand Down Expand Up @@ -105,26 +105,28 @@ Once committed, tagging `@droid fill` or `@droid review` on an open PR will trig
## Using the Commands

### `@droid fill`
* Place the command in the PR description or in a top-level comment.
* Droid searches for common PR template locations (`.github/pull_request_template.md`, etc.). When a template exists, it fills the sections; otherwise it writes a structured summary (overview, changes, testing, rollout).
* The original request is replaced with the generated description so reviewers can merge immediately.

- Place the command in the PR description or in a top-level comment.
- Droid searches for common PR template locations (`.github/pull_request_template.md`, etc.). When a template exists, it fills the sections; otherwise it writes a structured summary (overview, changes, testing, rollout).
- The original request is replaced with the generated description so reviewers can merge immediately.

### `@droid review`
* Mention `@droid review` in a PR comment.
* Droid inspects the diff, prioritizes potential bugs or high-impact issues, and leaves inline comments directly on the changed lines.
* A short summary comment is posted in the original thread highlighting the findings and linking to any inline feedback.

- Mention `@droid review` in a PR comment.
- Droid inspects the diff, prioritizes potential bugs or high-impact issues, and leaves inline comments directly on the changed lines.
- A short summary comment is posted in the original thread highlighting the findings and linking to any inline feedback.

## Configuration Essentials

| Input | Purpose |
| --- | --- |
| `factory_api_key` | **Required.** Grants Droid Exec permission to run via Factory. |
| `github_token` | Optional override if you prefer a custom GitHub App/token. By default the installed app token is used. |
| `review_model` | Optional. Override the model used for code review (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to review flows. |
| `fill_model` | Optional. Override the model used for PR description fill (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to fill flows. |
| Input | Purpose |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `factory_api_key` | **Required.** Grants Droid Exec permission to run via Factory. |
| `github_token` | Optional override if you prefer a custom GitHub App/token. By default the installed app token is used. |
| `review_model` | Optional. Override the model used for code review (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to review flows. |
| `fill_model` | Optional. Override the model used for PR description fill (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to fill flows. |

## Troubleshooting & Support

* Check the workflow run linked from the Droid tracking comment for execution logs.
* Verify that the workflow file and repository allow the GitHub App to run (branch protections can block bots).
* Need more detail? Start with the [Setup Guide](./docs/setup.md) or [FAQ](./docs/faq.md).
- Check the workflow run linked from the Droid tracking comment for execution logs.
- Verify that the workflow file and repository allow the GitHub App to run (branch protections can block bots).
- Need more detail? Start with the [Setup Guide](./docs/setup.md) or [FAQ](./docs/faq.md).
101 changes: 101 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@ inputs:
description: "Automatically run the review flow for pull request contexts without requiring an explicit @droid review command. Only supported for PR-related events."
required: false
default: "false"
automatic_security_review:
description: "Automatically run the security review flow for pull request contexts without requiring an explicit @droid security-review command. Only supported for PR-related events."
required: false
default: "false"
security_model:
description: "Override the model used for security review (e.g., 'claude-sonnet-4-5-20250929', 'gpt-5.1-codex'). Only applies to security review flows."
required: false
default: ""
security_severity_threshold:
description: "Minimum severity to report in security reviews (critical, high, medium, low). Findings below this threshold will be filtered out."
required: false
default: "medium"
security_block_on_critical:
description: "Submit REQUEST_CHANGES review when critical severity findings are detected."
required: false
default: "true"
security_block_on_high:
description: "Submit REQUEST_CHANGES review when high severity findings are detected."
required: false
default: "false"
security_notify_team:
description: "GitHub team to @mention on critical findings (e.g., '@org/security-team')."
required: false
default: ""
security_scan_schedule:
description: "Enable scheduled security scans. Set to 'true' for schedule events to trigger full repository scans."
required: false
default: "false"
security_scan_days:
description: "Number of days of commits to scan for scheduled security scans. Only applies when security_scan_schedule is enabled."
required: false
default: "7"
review_model:
description: "Override the model used for code review (e.g., 'claude-sonnet-4-5-20250929', 'gpt-5.1-codex'). Only applies to review flows."
required: false
Expand Down Expand Up @@ -135,6 +167,14 @@ runs:
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
TRACK_PROGRESS: ${{ inputs.track_progress }}
AUTOMATIC_REVIEW: ${{ inputs.automatic_review }}
AUTOMATIC_SECURITY_REVIEW: ${{ inputs.automatic_security_review }}
SECURITY_MODEL: ${{ inputs.security_model }}
SECURITY_SEVERITY_THRESHOLD: ${{ inputs.security_severity_threshold }}
SECURITY_BLOCK_ON_CRITICAL: ${{ inputs.security_block_on_critical }}
SECURITY_BLOCK_ON_HIGH: ${{ inputs.security_block_on_high }}
SECURITY_NOTIFY_TEAM: ${{ inputs.security_notify_team }}
SECURITY_SCAN_SCHEDULE: ${{ inputs.security_scan_schedule }}
SECURITY_SCAN_DAYS: ${{ inputs.security_scan_days }}
REVIEW_MODEL: ${{ inputs.review_model }}
REASONING_EFFORT: ${{ inputs.reasoning_effort }}
FILL_MODEL: ${{ inputs.fill_model }}
Expand Down Expand Up @@ -178,6 +218,67 @@ runs:
env:
EXPERIMENTAL_ALLOWED_DOMAINS: ${{ inputs.experimental_allowed_domains }}

- name: Install Security Skills
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.install_security_skills == 'true'
shell: bash
run: |
echo "Installing security skills from Factory-AI/skills..."
SKILLS_DIR="$HOME/.factory/skills"
mkdir -p "$SKILLS_DIR"

# Clone public skills repo (sparse checkout for efficiency)
TEMP_DIR=$(mktemp -d)
git clone --filter=blob:none --sparse \
"https://github.com/Factory-AI/skills.git" \
"$TEMP_DIR" 2>/dev/null || {
echo "Warning: Could not clone skills repo. Security skills will not be available."
exit 0
}

cd "$TEMP_DIR"
git sparse-checkout set \
skills/threat-model-generation \
skills/commit-security-scan \
skills/vulnerability-validation \
skills/security-review 2>/dev/null || true

# Copy skills to ~/.factory/skills/ and track installed count
INSTALLED_COUNT=0
for skill in threat-model-generation commit-security-scan vulnerability-validation security-review; do
if [ -d "skills/$skill" ]; then
cp -r "skills/$skill" "$SKILLS_DIR/"
echo " Installed skill: $skill"
INSTALLED_COUNT=$((INSTALLED_COUNT + 1))
else
echo " Warning: Skill not found in repo: $skill"
fi
done

# Cleanup
rm -rf "$TEMP_DIR"

# Verify at least one skill was installed
if [ "$INSTALLED_COUNT" -eq 0 ]; then
echo "Warning: No security skills were installed. The skills may not exist in the Factory-AI/skills repository."
echo "Security review will proceed but may have limited functionality."
else
echo "Security skills installation complete ($INSTALLED_COUNT skills installed)"
fi

# Verify skills exist in the target directory
echo "Verifying installed skills in $SKILLS_DIR..."
VERIFIED_COUNT=0
for skill in threat-model-generation commit-security-scan vulnerability-validation security-review; do
if [ -d "$SKILLS_DIR/$skill" ]; then
echo " Verified: $skill"
VERIFIED_COUNT=$((VERIFIED_COUNT + 1))
fi
done

if [ "$VERIFIED_COUNT" -ne "$INSTALLED_COUNT" ]; then
echo "Warning: Skill verification mismatch. Expected $INSTALLED_COUNT, found $VERIFIED_COUNT in $SKILLS_DIR"
fi

- name: Checkout PR branch for review
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.review_pr_number != ''
shell: bash
Expand Down
3 changes: 1 addition & 2 deletions base-action/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ async function run() {
mcpTools: process.env.INPUT_MCP_TOOLS,
systemPrompt: process.env.INPUT_SYSTEM_PROMPT,
appendSystemPrompt: process.env.INPUT_APPEND_SYSTEM_PROMPT,
pathToDroidExecutable:
process.env.INPUT_PATH_TO_DROID_EXECUTABLE,
pathToDroidExecutable: process.env.INPUT_PATH_TO_DROID_EXECUTABLE,
showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT,
});
} catch (error) {
Expand Down
Loading