Skip to content

step-security/auto-unapprove

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

StepSecurity Maintained Action

🚫 Auto Unapprove Reviews Action

GitHub repo size GitHub all releases License

GitHub Action for smart dismissal of pull request reviews when code owners modify files after approval or when reviewers approve their own changes.

Problem & Solution

Dismiss stale pull request approvals option

When working with large monorepos, there is a frustrating limitation in GitHub's PR review system. When new commits are pushed to a PR, GitHub only offers two options:

  1. keep all approvals
  2. dismiss all approvals. This binary choice becomes particularly problematic in monorepos where multiple teams own different parts of the codebase.

The Auto Unapprove Reviews GitHub Action solves this by providing granular control over review dismissals. It follows a simple but effective flow:

  1. Get PR information
  2. Check changed files
  3. Check team ownership
  4. Analyze review status
  5. Take appropriate action

Dismiss in PR

πŸš€ Main Script Features

auto-unapprove.js - Smart dismissal with advanced features:

  • βœ… Stale approval detection - Dismisses approvals when files are modified after approval
  • βœ… Team membership validation - Supports GitHub team-based code ownership
  • βœ… Precise file matching - Only dismisses when owned files are actually modified
  • βœ… Performance optimized - Parallel API calls and efficient caching
  • βœ… Comprehensive logging - Detailed analysis and reasoning for each decision

πŸš€ Quick Start

Option 1: GitHub Action (Recommended)

- name: Smart dismiss reviews
  uses: step-security/auto-unapprove@v1
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    pr-number: ${{ github.event.number }}
    dry-run: 'false'
    code-owners-file: 'CODEOWNERS'  # Optional: custom path
    target-branch: ${{ github.event.pull_request.base.ref }}  # Optional: target branch
    team-start-with: '@'  # Optional: team prefix

🧠 How It Works

Action Flow

  1. Get all changed files from the entire PR (not just latest commit)
  2. Parse CODEOWNERS from the PR target branch (not default branch) using hierarchical path matching (most specific wins)
  3. Check team memberships via GitHub API for relevant teams only
  4. Analyze approval timeline - detect commits made after approval
  5. Smart dismissal logic:
    • Dismiss code owners who authored commits
    • Dismiss stale approvals (post-approval commits to owned files)
    • Preserve legitimate approvals from non-owners

πŸ“ Example Workflow

name: Auto Unapprove

on:
  pull_request:
    types: [synchronize]

jobs:
  auto-unapprove:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/create-github-app-token@v3
        id: app-token
        with:
          app-id: ${{ vars.REVIEWS_APP_ID }}
          private-key: ${{ secrets.REVIEWS_APP_PRIVATE_KEY }}

      - name: Dismiss Stale Reviews
        uses: step-security/auto-unapprove@v1
        with:
          github-token: ${{ steps.app-token.outputs.token }}
          pr-number: ${{ github.event.number }}
          dry-run: 'false'
          target-branch: ${{ github.event.pull_request.base.ref }}
          team-start-with: '@your-org/'

For more workflow examples, see example-workflow.yml


Required Permissions: for more info on how to use GitHub App token check https://github.com/actions/create-github-app-token

  • Repository permissions:

    • Administration: Repository creation, deletion, settings, teams, and collaborators. (READ ONLY)
    • Contents: Repository contents, commits, branches, downloads, releases, and merges. (READ ONLY)
    • Pull requests: Pull requests and related comments, assignees, labels, milestones, and merges. (READ AND WRITE)
  • Organization permissions:

    • Members: Organization members and teams

βš™οΈ Key Features

  • βœ… Target branch CODEOWNERS: Reads ownership rules from PR target branch, not default branch
  • βœ… Stale approval detection: Automatically detects and dismisses approvals made stale by subsequent commits
  • βœ… Surgical precision: Only dismisses when owned files are actually modified
  • βœ… Team support: Full GitHub team membership validation via API
  • βœ… Timeline analysis: Compares approval timestamps with commit timestamps
  • βœ… Hierarchical CODEOWNERS: Proper path matching with most-specific-wins logic
  • βœ… Performance optimized: Parallel API calls and efficient team checking
  • βœ… Comprehensive logging: Detailed reasoning for every dismissal decision
  • βœ… Dry-run mode: Safe testing without actual dismissals

πŸ“Š Example Output

πŸš€ Smart Review Dismissal
   Repository: myorg/myrepo
   PR: #123
   Mode: πŸ§ͺ DRY RUN

πŸ“ All changed files in PR (3):
   πŸ“ src/gui/components/Button.tsx
   πŸ“ src/gui/styles/theme.css  
   πŸ“ src/gui/utils/helpers.ts

🎯 Target branch: main
πŸ‘‘ Parsed 15 CODEOWNERS rules

🎯 DISMISSAL ANALYSIS:
   πŸ• Checking commits after jane-smith's approval (2025-06-04T12:49:59.000Z)...
     πŸ“… Commit 55a4fc5 at 2025-06-04T12:53:44Z
       🎯 Modified owned files: src/gui/components/Button.tsx
   
   🚫 DISMISS @jane-smith
      πŸ“ Files: src/gui/components/Button.tsx, src/gui/styles/theme.css, src/gui/utils/helpers.ts
      πŸ‘‘ Owner Via: @myorg/frontend-team
      πŸ’‘ Reason: Approval became stale - commits modified owned files
   
   βœ… KEEP @bob-jones
      πŸ“„ Not owner of changed files

πŸ“Š EXECUTION PLAN:
   β€’ Changed files: 3
   β€’ Total approvals: 2
   β€’ Dismissals needed: 1
   β€’ Approvals preserved: 1

πŸ”§ Inputs & Environment Variables

GitHub Action Inputs

Input Required Default Description
github-token βœ… - GitHub API token with repo access
pr-number βœ… - Pull request number to analyze
dry-run - true Set to 'false' for actual dismissals
code-owners-file - CODEOWNERS Path to CODEOWNERS file
target-branch - main Target branch to read CODEOWNERS from
team-start-with - @ Team prefix for organization

Environment Variables (Direct Script Usage)

Variable Required Default Description
GITHUB_TOKEN βœ… - GitHub API token with repo access
PR_NUMBER βœ… - Pull request number to analyze
GITHUB_REPOSITORY βœ… - Repository in owner/repo format
TEAM_START_WITH - @ Team prefix for organization
DRY_RUN - true Set to 'false' for actual dismissals
CODEOWNERS_FILE - CODEOWNERS Path to CODEOWNERS file
TARGET_BRANCH - main Target branch to read CODEOWNERS from
CHANGED_FILES - - Newline-separated files (webhook optimization)

πŸ§ͺ Testing

The project includes comprehensive tests for the pagination implementation:

Quick Test (No API calls needed):

./tests/run-tests.sh

Test with Real Data:

export GITHUB_TOKEN='your_token'
export GITHUB_REPOSITORY='owner/repo'
export PR_NUMBER='123'
export DRY_RUN='true'
./tests/test-real-pagination.sh

Test Files:

  • tests/test-pagination.js - Logic tests with simulated data
  • tests/test-mock-pagination.js - Mock API tests
  • tests/test-real-pagination.sh - Real GitHub API tests
  • tests/TESTING.md - Comprehensive testing guide

For detailed testing instructions, see tests/README.md.

About

Secure drop-in replacement for RotemK1/auto-unapprove.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors