name: RepoPilot
description: "Run RepoPilot static analysis on your repository"
author: "MykytaStel"
branding:
icon: shield
color: blue
inputs:
command:
description: "Command to run: scan | review | compare | doctor | ai-context | ai-plan | ai-prompt"
required: false
default: "scan"
format:
description: "Output format: auto | console | json | markdown | sarif. auto uses sarif for scan and markdown for review/compare/doctor; AI commands emit Markdown."
required: false
default: "auto"
path:
description: "Path passed to scan, review, doctor, or AI commands."
required: false
default: "."
config:
description: "Optional repopilot.toml path."
required: false
default: ""
baseline:
description: "Optional baseline path for scan/review."
required: false
default: ""
fail-on:
description: "Optional scan/review failure threshold, e.g. new-high."
required: false
default: ""
fail-on-priority:
description: "Optional scan/review failure threshold by risk priority: p0 | p1 | p2 | p3."
required: false
default: ""
min-severity:
description: "Optional minimum rendered severity, e.g. high."
required: false
default: ""
min-priority:
description: "Optional minimum rendered risk priority: p0 | p1 | p2 | p3."
required: false
default: ""
rule:
description: "Optional scan rule filter. For multiple rules, provide a space-separated list of rule IDs."
required: false
default: ""
timing:
description: "Print per-phase scan timing breakdown for scan commands."
required: false
default: "false"
base:
description: "Optional Git base ref for review."
required: false
default: ""
head:
description: "Optional Git head ref for review; requires base."
required: false
default: ""
focus:
description: "Optional AI focus: security | arch | architecture | quality | framework | all."
required: false
default: ""
budget:
description: "Optional AI token budget: 2k | 4k | 8k | 16k or a positive integer."
required: false
default: ""
output:
description: "Optional output path for commands that write reports."
required: false
default: ""
receipt:
description: "Optional audit receipt JSON path for scan."
required: false
default: ""
args:
description: "Advanced extra CLI arguments appended after typed inputs. Prefer typed inputs for CI workflows."
required: false
default: ""
version:
description: "npm version tag to install (e.g. latest, 0.11.0)"
required: false
default: "latest"
upload-sarif:
description: "Automatically upload SARIF output to GitHub Code Scanning"
required: false
default: "true"
outputs:
sarif-file:
description: "Path to the generated SARIF file (only set when format is sarif)"
value: ${{ steps.run.outputs.sarif_file }}
receipt-file:
description: "Path to the generated audit receipt JSON file (only set when receipt is provided for scan)"
value: ${{ steps.run.outputs.receipt_file }}
runs:
using: composite
steps:
- name: Install repopilot
shell: bash
run: npm install -g repopilot@${{ inputs.version }}
- name: Run repopilot
id: run
shell: bash
run: |
set -euo pipefail
COMMAND="${{ inputs.command }}"
FORMAT="${{ inputs.format }}"
PATH_INPUT="${{ inputs.path }}"
CONFIG="${{ inputs.config }}"
BASELINE="${{ inputs.baseline }}"
FAIL_ON="${{ inputs.fail-on }}"
FAIL_ON_PRIORITY="${{ inputs.fail-on-priority }}"
MIN_SEVERITY="${{ inputs.min-severity }}"
MIN_PRIORITY="${{ inputs.min-priority }}"
RULE_INPUT="${{ inputs.rule }}"
TIMING="${{ inputs.timing }}"
BASE="${{ inputs.base }}"
HEAD="${{ inputs.head }}"
FOCUS="${{ inputs.focus }}"
BUDGET="${{ inputs.budget }}"
OUTPUT="${{ inputs.output }}"
RECEIPT="${{ inputs.receipt }}"
ARGS="${{ inputs.args }}"
case "$COMMAND" in
scan) RUN_ARGS=(scan "$PATH_INPUT") ;;
review) RUN_ARGS=(review "$PATH_INPUT") ;;
compare) RUN_ARGS=(compare) ;;
doctor) RUN_ARGS=(doctor "$PATH_INPUT") ;;
ai-context|vibe) RUN_ARGS=(ai context "$PATH_INPUT") ;;
ai-plan|harden) RUN_ARGS=(ai plan "$PATH_INPUT") ;;
ai-prompt|prompt) RUN_ARGS=(ai prompt "$PATH_INPUT") ;;
*)
echo "::error::Unsupported command '$COMMAND'. Expected scan, review, compare, doctor, ai-context, ai-plan, or ai-prompt."
exit 1
;;
esac
IS_AI="false"
if [[ "$COMMAND" == "ai-context" || "$COMMAND" == "ai-plan" || "$COMMAND" == "ai-prompt" || "$COMMAND" == "vibe" || "$COMMAND" == "harden" || "$COMMAND" == "prompt" ]]; then
IS_AI="true"
fi
if [[ "$FORMAT" == "auto" ]]; then
if [[ "$COMMAND" == "scan" ]]; then
FORMAT="sarif"
else
FORMAT="markdown"
fi
fi
if [[ "$FORMAT" == "sarif" && "$COMMAND" != "scan" ]]; then
echo "::error::SARIF output and upload are only supported by 'scan'. Use format=markdown for '$COMMAND'."
exit 1
fi
if [[ -n "$RECEIPT" && "$COMMAND" != "scan" ]]; then
echo "::error::Receipt output is only supported by 'scan'. Remove receipt or use command=scan."
exit 1
fi
if [[ -n "$FAIL_ON_PRIORITY" && "$COMMAND" != "scan" && "$COMMAND" != "review" ]]; then
echo "::error::fail-on-priority is only supported by 'scan' and 'review'."
exit 1
fi
if [[ -n "$MIN_PRIORITY" && "$COMMAND" != "scan" && "$COMMAND" != "review" ]]; then
echo "::error::min-priority is only supported by 'scan' and 'review'."
exit 1
fi
if [[ -n "$RULE_INPUT" && "$COMMAND" != "scan" ]]; then
echo "::error::rule filtering is only supported by 'scan'."
exit 1
fi
if [[ "$TIMING" == "true" && "$COMMAND" != "scan" ]]; then
echo "::error::timing is only supported by 'scan'."
exit 1
fi
if [[ -n "$CONFIG" && "$COMMAND" != "compare" ]]; then RUN_ARGS+=(--config "$CONFIG"); fi
if [[ "$COMMAND" == "scan" || "$COMMAND" == "review" ]]; then
if [[ -n "$BASELINE" ]]; then RUN_ARGS+=(--baseline "$BASELINE"); fi
if [[ -n "$FAIL_ON" ]]; then RUN_ARGS+=(--fail-on "$FAIL_ON"); fi
if [[ -n "$FAIL_ON_PRIORITY" ]]; then RUN_ARGS+=(--fail-on-priority "$FAIL_ON_PRIORITY"); fi
if [[ -n "$MIN_SEVERITY" ]]; then RUN_ARGS+=(--min-severity "$MIN_SEVERITY"); fi
if [[ -n "$MIN_PRIORITY" ]]; then RUN_ARGS+=(--min-priority "$MIN_PRIORITY"); fi
fi
if [[ "$COMMAND" == "scan" && -n "$RULE_INPUT" ]]; then
read -r -a RULES <<< "$RULE_INPUT"
for RULE in "${RULES[@]}"; do
RUN_ARGS+=(--rule "$RULE")
done
fi
if [[ "$COMMAND" == "scan" && "$TIMING" == "true" ]]; then RUN_ARGS+=(--timing); fi
if [[ "$COMMAND" == "review" && -n "$BASE" ]]; then RUN_ARGS+=(--base "$BASE"); fi
if [[ "$COMMAND" == "review" && -n "$HEAD" ]]; then RUN_ARGS+=(--head "$HEAD"); fi
if [[ "$IS_AI" == "true" && -n "$FOCUS" ]]; then RUN_ARGS+=(--focus "$FOCUS"); fi
if [[ "$IS_AI" == "true" && -n "$BUDGET" ]]; then RUN_ARGS+=(--budget "$BUDGET"); fi
if [[ -n "$OUTPUT" && "$FORMAT" != "sarif" ]]; then RUN_ARGS+=(--output "$OUTPUT"); fi
if [[ -n "$RECEIPT" ]]; then RUN_ARGS+=(--receipt "$RECEIPT"); fi
if [[ -n "$ARGS" ]]; then
read -r -a EXTRA_ARGS <<< "$ARGS"
RUN_ARGS+=("${EXTRA_ARGS[@]}")
fi
OUTFILE="${OUTPUT:-repopilot-results.sarif}"
RUN_STATUS=0
if [[ "$IS_AI" == "true" ]]; then
if [[ "$FORMAT" != "markdown" ]]; then
echo "::error::'$COMMAND' emits Markdown and does not accept --format. Use format=auto or format=markdown."
exit 1
fi
set +e
repopilot "${RUN_ARGS[@]}"
RUN_STATUS=$?
set -e
elif [[ "$FORMAT" == "sarif" ]]; then
set +e
repopilot "${RUN_ARGS[@]}" --format sarif --output "$OUTFILE"
RUN_STATUS=$?
set -e
echo "sarif_file=$OUTFILE" >> "$GITHUB_OUTPUT"
else
set +e
repopilot "${RUN_ARGS[@]}" --format "$FORMAT"
RUN_STATUS=$?
set -e
fi
if [[ -n "$RECEIPT" ]]; then
echo "receipt_file=$RECEIPT" >> "$GITHUB_OUTPUT"
fi
exit "$RUN_STATUS"
- name: Upload SARIF to GitHub Code Scanning
if: inputs.upload-sarif == 'true' && steps.run.outputs.sarif_file != ''
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: ${{ steps.run.outputs.sarif_file }}