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
224 changes: 224 additions & 0 deletions .github/AUTOMERGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Automerge and Branch Cleanup Documentation

This document describes the automatic PR approval, merge, and branch cleanup workflows in the Scripts repository.

## Overview

The repository includes two workflows that automate the lifecycle of Pull Requests created by trusted automation:

1. **Automerge Workflow** (`.github/workflows/automerge.yml`) - Automatically approves and enables automerge for PRs
2. **Auto Delete Branch Workflow** (`.github/workflows/auto-delete-branch.yml`) - Automatically deletes branches after PRs are merged

## Automerge Workflow

### Purpose

Automatically approves and enables automerge for Pull Requests created by trusted automation sources, reducing manual overhead while maintaining quality control.

### Triggers

- **Automatic**: When a PR is opened, reopened, or marked ready for review
- **Manual**: Via workflow dispatch with a PR number input

### Eligible PRs

A PR is eligible for automerge if it meets ALL of the following criteria:

1. **Created by trusted automation**:
- Author is `Claude` (Anthropic AI agent)
- Author is `github-actions[bot]`
- Branch name starts with `automated-update/`
- Branch name starts with `claude/`

2. **Not a draft PR**: Draft PRs are skipped

3. **All checks passed**: All required status checks must pass

### Behavior

1. **Check Eligibility**: Verifies the PR meets automerge criteria
2. **Approve PR**: Automatically approves the PR with a standardized message
3. **Enable Automerge**: Uses GitHub's automerge feature with squash merge method
4. **Error Handling**: Comments on the PR if automerge fails

### Configuration

The workflow uses the following merge method:
- **Default**: `SQUASH` - Combines all commits into a single commit

To change the merge method, edit line 124 in `.github/workflows/automerge.yml`:
```yaml
mergeMethod: 'SQUASH' # Options: MERGE, SQUASH, REBASE
```

### Permissions Required

- `contents: write` - To enable automerge
- `pull-requests: write` - To approve PRs and add comments

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Permissions Required" section for the automerge workflow doesn’t mention the additional permissions needed for the current implementation (e.g., checks: read for checks.listForRef and issues: write for PR failure comments). After updating the workflow permissions, please update this section so operators can correctly configure least-privilege.

Suggested change
- `pull-requests: write` - To approve PRs and add comments
- `pull-requests: write` - To approve PRs
- `issues: write` - To post PR comments when automerge or checks fail
- `checks: read` - To read check statuses (via `checks.listForRef`) for eligibility

Copilot uses AI. Check for mistakes.

## Auto Delete Branch Workflow

### Purpose

Automatically cleans up branches after their Pull Requests are merged, keeping the repository tidy and preventing branch accumulation.

### Triggers

- **Automatic**: When a PR is closed (only deletes if merged)
- **Manual**: Via workflow dispatch with a branch name input

### Protected Branches

The following branches are NEVER deleted:
- `main`
- `master`
- `development`
- `staging`
- `production`

### Behavior

1. **Verify Merge**: Confirms the PR was actually merged (not just closed)
2. **Check Protection**: Ensures the branch is not in the protected list
3. **Delete Branch**: Removes the branch from the repository
4. **Add Comment**: Posts a comment on the PR confirming deletion
5. **Error Handling**: Gracefully handles cases where the branch doesn't exist

### Fork Handling

Branches from forked repositories are NOT deleted, as the workflow only has permissions in the main repository.

### Permissions Required

- `contents: write` - To delete branches

Comment on lines +91 to +94

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow’s documentation says only contents: write is required, but the workflow also posts a PR comment via issues.createComment, which typically requires issues: write. Update the permissions section to reflect the actual required permissions (or remove the comment behavior).

Copilot uses AI. Check for mistakes.
## Integration with Existing Workflows

### Auto-Update Dependencies Workflow

The automerge workflow works seamlessly with the existing dependency update automation:

1. `check-dependencies.yml` creates an issue when a new version is detected
2. `auto-update-dependencies.yml` creates a PR to update the dependency
3. **NEW**: `automerge.yml` automatically approves and enables automerge
4. GitHub merges the PR when all checks pass
5. **NEW**: `auto-delete-branch.yml` deletes the branch after merge
6. The original issue is automatically closed via `Closes #XX` in PR body

### Dependabot PRs

Dependabot PRs are also eligible for automerge if:
- They pass all status checks
- The workflow approves them automatically

To disable automerge for Dependabot PRs, you can modify the eligibility check in `automerge.yml`.

Comment on lines +110 to +115

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs state Dependabot PRs are eligible for automerge, but the workflow eligibility check only allows Claude, github-actions[bot], or branch prefixes automated-update/ and claude/—it does not include dependabot[bot]. Either update the workflow to explicitly include Dependabot or adjust this documentation section to match current behavior.

Suggested change
Dependabot PRs are also eligible for automerge if:
- They pass all status checks
- The workflow approves them automatically
To disable automerge for Dependabot PRs, you can modify the eligibility check in `automerge.yml`.
Dependabot PRs follow the same eligibility rules as other PRs.
With the current configuration, Dependabot PRs are not treated as trusted automation for automerge by default.
To enable automerge for Dependabot PRs, you can modify the eligibility check in `automerge.yml` to include `dependabot[bot]` (for example, by treating it as a trusted author or by matching Dependabot-specific branch name patterns).

Copilot uses AI. Check for mistakes.
## Manual Intervention

### When Manual Review is Required

Certain PRs require manual review and will NOT be automatically merged:

1. **NGINX Updates**: Marked as draft until SHA256 checksums are manually verified
2. **PRs from untrusted sources**: Only automation from trusted sources is auto-merged
3. **Failed checks**: PRs with failing status checks must be fixed before merge

### Manual Workflow Triggers

Both workflows support manual triggering:

#### Enable Automerge for a Specific PR
```bash
gh workflow run automerge.yml -f pr_number=123
```

#### Delete a Specific Branch
```bash
gh workflow run auto-delete-branch.yml -f branch_name=my-feature-branch
```

## Monitoring and Troubleshooting

### View Workflow Runs

Check workflow execution in the GitHub Actions tab:
```
https://github.com/Stensel8/Scripts/actions
```

### Common Issues

#### Automerge Not Enabled

**Possible causes**:
1. Repository settings don't allow automerge
2. Branch protection rules require additional approvals
3. PR is from an untrusted source
4. Status checks are failing

**Solution**: Check the workflow logs and verify repository settings.

#### Branch Not Deleted

**Possible causes**:
1. PR was closed without merging
2. Branch is in the protected list
3. Branch is from a fork
4. Branch was already deleted

**Solution**: These are expected behaviors. Check the workflow logs for details.

## Security Considerations

### Trusted Sources

The workflows only operate on PRs from:
- `Claude` (Anthropic AI agent)
- `github-actions[bot]`
- Branches matching specific patterns

This prevents unauthorized users from triggering automerge on malicious PRs.

### Required Checks

Automerge only enables if all required status checks pass, ensuring:
- Code validation (ShellCheck, PSScriptAnalyzer)
- Security scanning
- Any other configured checks

### Approval Trail

All auto-approved PRs include a comment indicating they were automatically approved, maintaining an audit trail.

## Disabling the Workflows

To temporarily disable automerge or branch cleanup:

1. **Via GitHub UI**: Go to Actions → Select workflow → Disable workflow
2. **Via Code**: Add `if: false` to the job in the workflow file

Example:
```yaml
jobs:
automerge:
name: Enable Automerge
runs-on: ubuntu-latest
if: false # Temporarily disable
```

## Future Enhancements

Potential improvements for consideration:

1. **Merge Method Selection**: Different merge methods based on PR type
2. **Approval Requirements**: Configurable approval count before automerge
3. **Label-Based Control**: Use labels to enable/disable automerge per PR
4. **Notification System**: Slack/Discord notifications for automated merges
5. **Rollback Mechanism**: Automatic revert if merged PR causes issues

## Related Documentation

- [Auto-Update Dependencies Workflow](../workflows/auto-update-dependencies.yml)
- [Check Dependencies Workflow](../workflows/check-dependencies.yml)
Comment on lines +221 to +222

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relative links in "Related Documentation" point to ../workflows/..., which resolves to a non-existent top-level workflows/ directory from .github/AUTOMERGE.md. Update the links to the actual paths under .github/workflows/ (e.g., workflows/auto-update-dependencies.yml from this file, or .github/workflows/... from repo root).

Suggested change
- [Auto-Update Dependencies Workflow](../workflows/auto-update-dependencies.yml)
- [Check Dependencies Workflow](../workflows/check-dependencies.yml)
- [Auto-Update Dependencies Workflow](workflows/auto-update-dependencies.yml)
- [Check Dependencies Workflow](workflows/check-dependencies.yml)

Copilot uses AI. Check for mistakes.
- [GitHub Automerge Documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/automatically-merging-a-pull-request)
- [GitHub Branch Protection Rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)
97 changes: 97 additions & 0 deletions .github/workflows/auto-delete-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Auto Delete Merged Branches

# This workflow automatically deletes branches after their PRs are merged

on:
pull_request:
types: [closed]
workflow_dispatch:
inputs:
branch_name:
description: 'Branch name to delete'
required: true
type: string

permissions:
contents: write

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow posts a PR comment via github.rest.issues.createComment, but permissions only include contents: write. With explicit permissions, commenting will likely fail without issues: write (or pull-requests: write if you switch to a PR-specific API). Add the needed permission or remove the comment step.

Suggested change
contents: write
contents: write
issues: write

Copilot uses AI. Check for mistakes.

jobs:
delete-branch:
name: Delete Merged Branch
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- name: Delete branch
uses: actions/github-script@v8
env:
BRANCH_NAME: ${{ github.event.inputs.branch_name }}
with:
script: |
let branchName;

if (context.payload.pull_request) {
// Get branch name from PR
branchName = context.payload.pull_request.head.ref;
const prNumber = context.payload.pull_request.number;
const merged = context.payload.pull_request.merged;

console.log(`PR #${prNumber} was closed`);
console.log(`Branch: ${branchName}`);
console.log(`Merged: ${merged}`);

if (!merged) {
console.log('PR was closed without merging, skipping branch deletion');
return;
}
} else {
// Get branch name from workflow input
branchName = process.env.BRANCH_NAME;
console.log(`Manual branch deletion requested for: ${branchName}`);
}

// Don't delete protected branches
const protectedBranches = ['main', 'master', 'development', 'staging', 'production'];
if (protectedBranches.includes(branchName)) {
console.log(`Branch ${branchName} is protected, skipping deletion`);
return;
}

// Check if it's a head branch from a fork
const isFork = context.payload.pull_request?.head.repo?.full_name !== context.payload.repository?.full_name;
if (isFork) {
console.log('Branch is from a fork, cannot delete from this repository');
return;
}
Comment on lines +59 to +64

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isFork is computed even when context.payload.pull_request is absent (workflow_dispatch). In that case the left side is undefined, so undefined !== <repo full_name> evaluates to true and the script returns early, meaning manual branch deletion will never work. Only perform the fork check when a PR payload exists (or set isFork to false for workflow_dispatch).

Copilot uses AI. Check for mistakes.

try {
// Delete the branch
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branchName}`
});

console.log(`✅ Successfully deleted branch: ${branchName}`);

// Add comment to the PR if available
if (context.payload.pull_request) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `🗑️ Branch \`${branchName}\` has been automatically deleted after merge.`
});
}

} catch (error) {
console.error(`Error deleting branch: ${error.message}`);

// Don't fail the workflow if branch doesn't exist or is already deleted
if (error.status === 404) {
console.log('Branch does not exist or was already deleted');
} else if (error.status === 422) {
console.log('Branch cannot be deleted (may be default branch)');
} else {
throw error;
}
}
Loading
Loading