GitHub Actions Adoption Guide

June 20, 2026 · View on GitHub

This guide shows how to integrate VibeGuard into your GitHub Actions PR workflow using SARIF code scanning, PR comments, baselines, and annotations.

0. One-Command Setup

The fastest path: let VibeGuard write the workflow for you.

pip install vibeguard-gate
vibeguard setup github-actions

This generates .github/workflows/vibeguard.yml — a single job that wires all three PR surfaces at once:

  1. SARIF upload → inline code-scanning annotations on the PR diff.
  2. PR comment → one summary comment, updated in place on every push (no spam).
  3. Gate → fails the PR on findings at/above --fail-on (default high).

Review the file, commit it, and open a PR. Options:

FlagEffect
--policy-pack <name>Also write a vibeguard.yaml for the pack and derive the gate threshold from it (e.g. web-appmedium).
--fail-on <severity>Override the gate threshold (infocritical).
--with-configAlso write a default vibeguard.yaml.
--dry-runPrint the generated file(s) to stdout without writing.
--forceOverwrite an existing workflow (refuses by default).

The generated workflow installs VibeGuard from PyPI (pip install vibeguard-gate) and posts the comment with actions/github-script, so it adds no networking to the VibeGuard CLI itself. The sections below document the same surfaces individually if you prefer to assemble the workflow by hand.

1. Minimal PR Gate Workflow

The simplest setup: fail PRs that introduce high/critical findings.

Diff base & fetch-depth. --diff compares base...HEAD. Pass --base "origin/${{ github.base_ref }}" so the diff is scoped to the PR's target branch (works for develop/trunk/release bases, not just main), and keep fetch-depth: 0 so that base is fetched. Without a detectable base VibeGuard degrades to git diff HEAD and prints a diagnostic — on a shallow clone it also reminds you to set fetch-depth: 0. You can also set the base in vibeguard.yaml under git.base_branch; the --base flag wins.

name: VibeGuard Gate
on:
  pull_request:

permissions:
  contents: read

jobs:
  vibeguard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # needed for --diff mode

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: pip

      - name: Install VibeGuard
        run: pip install vibeguard-gate

      - name: Run VibeGuard gate
        run: vibeguard gate --diff --base "origin/${{ github.base_ref }}" --fail-on high

For security-sensitive repositories, add --strict-errors so the gate fails closed when the scan itself ran degraded (a crashed rule, a failed plugin load, unavailable git context, or a failed registry lookup) rather than passing on a partial scan. Routine binary/oversize skips never trip it:

      - name: Run VibeGuard gate (fail-closed on degraded scans)
        run: vibeguard gate --diff --base "origin/${{ github.base_ref }}" --fail-on high --strict-errors

Or using the first-party action:

      - uses: dgenio/vibeguard@v0.8.0
        with:
          diff: 'true'
          fail-on: high

2. SARIF Upload (Code Scanning Annotations)

Upload SARIF to get inline annotations in PR diffs and findings in the Security tab.

name: VibeGuard Code Scanning
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write  # required for SARIF upload

jobs:
  vibeguard-sarif:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: pip

      - name: Install VibeGuard
        run: pip install vibeguard-gate

      - name: Run VibeGuard scan (SARIF)
        run: vibeguard scan --sarif --output vibeguard.sarif
        continue-on-error: true

      - name: Upload SARIF to Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: vibeguard.sarif
          category: vibeguard

File output. Use --output PATH (or -o) to write a report to a file instead of redirecting stdout (--sarif --output vibeguard.sarif); pass - for stdout. To emit several formats from a single scan, use the repeatable --report FORMAT=PATH, e.g. vibeguard gate --diff --report sarif=vibeguard.sarif --report pr-comment=comment.md.

Large repositories. GitHub Code Scanning caps SARIF at 5,000 results per run. VibeGuard caps its SARIF output at that limit (configurable via output.sarif_max_results), keeping the most severe findings and adding an overflow notice rather than failing the upload. For a large legacy repo, create a baseline first (section 4) and gate only new findings, so the diff stays well under the cap. The full set is always available via --json.

3. PR Comment Workflow

Post a single summary comment that updates in place on every push instead of adding a new comment each time. VibeGuard's --pr-comment output leads with a hidden marker (<!-- vibeguard-report -->); the step below finds the existing comment by that marker and edits it, falling back to creating one on the first run.

name: VibeGuard PR Comment
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write  # required for posting comments

jobs:
  vibeguard-comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: pip

      - name: Install VibeGuard
        run: pip install vibeguard-gate

      - name: Generate PR comment
        run: vibeguard gate --diff --fail-on high --pr-comment > vibeguard-comment.md
        continue-on-error: true

      - name: Post / update comment
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require("fs");
            const body = fs.readFileSync("vibeguard-comment.md", "utf8");
            const marker = "<!-- vibeguard-report -->";
            // Page through every comment (listComments caps at 30/page) so the
            // marker comment is still found on long PRs, and only match our own
            // bot comment so a user echoing the marker can't be overwritten.
            const comments = await github.paginate(github.rest.issues.listComments, {
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              per_page: 100,
            });
            const existing = comments.find(
              (c) => c.user?.login === "github-actions[bot]" && c.body && c.body.includes(marker)
            );
            if (existing) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existing.id,
                body,
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body,
              });
            }

Fork PRs: on pull requests from forks, GitHub restricts GITHUB_TOKEN to read-only regardless of the permissions: block, so the comment step can't post. Use the SARIF upload (Section 2) for fork coverage, or gate forks without the comment surface.

vibeguard setup github-actions (Section 0) generates a workflow that already includes this idempotent comment step alongside SARIF upload and the gate.

4. Baseline Workflow

Create a baseline on the default branch to suppress pre-existing findings, then use it in PRs.

Create baseline on main branch merges:

name: Update VibeGuard Baseline
on:
  push:
    branches: [main]

permissions:
  contents: write

jobs:
  update-baseline:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: pip

      - name: Install VibeGuard
        run: pip install vibeguard-gate

      - name: Create baseline
        run: vibeguard baseline create --output .vibeguard-baseline.json

      - name: Commit baseline
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add .vibeguard-baseline.json
          git diff --cached --quiet || git commit -m "chore: update vibeguard baseline"
          git push

Use baseline in PR gate:

      - name: Run gate with baseline
        run: vibeguard gate --diff --fail-on high --baseline .vibeguard-baseline.json
Risk Profilefail-onUse Case
StrictmediumRegulated environments, security-critical code
BalancedhighDefault for most teams
Audit-onlycriticalInitial adoption, observability mode
Info— (don't gate)Use scan instead of gate for monitoring

6. Caching

Speed up runs by caching pip installs:

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: pip
          cache-dependency-path: '**/pyproject.toml'

Problem Matcher

VibeGuard ships a problem matcher for console output. Enable it to get annotations even without --sarif:

      - name: Enable VibeGuard problem matcher
        run: echo "::add-matcher::.github/problem-matchers/vibeguard.json"

Annotations Mode

VibeGuard auto-emits ::error, ::warning, and ::notice annotations when running inside GitHub Actions. Disable with --no-annotations:

      - name: Run without annotations
        run: vibeguard gate --diff --fail-on high --no-annotations