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`:

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 doc references "edit line 124" to change merge method, but in the current automerge.yml the mergeMethod: 'SQUASH' setting is at a different line. Consider referencing the key name/section instead of a hard-coded line number (or update the line number to match).

Suggested change
To change the merge method, edit line 124 in `.github/workflows/automerge.yml`:
To change the merge method, update the `mergeMethod` setting in `.github/workflows/automerge.yml`:

Copilot uses AI. Check for mistakes.
```yaml
mergeMethod: 'SQUASH' # Options: MERGE, SQUASH, REBASE
```

### Permissions Required

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

## 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

## 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.

This section claims Dependabot PRs are eligible for automerge, but automerge.yml currently doesn't include dependabot[bot] or dependabot/ branches in its eligibility check. Either update the workflow to include Dependabot explicitly, or adjust the documentation 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 are **not** currently included in the automerge eligibility criteria and will **not** be auto-approved or auto-merged by default.
To enable automerge for Dependabot PRs, update the eligibility check in `.github/workflows/automerge.yml` to treat Dependabot as a trusted automation source (for example, by allowing the `dependabot[bot]` author or `dependabot/` branch prefixes).
If you prefer to keep Dependabot PRs manual-only, no changes to the workflow are required.

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.

These links are broken: from .github/AUTOMERGE.md, ../workflows/... resolves to <repo>/workflows/... (which doesn't exist). Use ./workflows/... or an absolute repo path like .github/workflows/... so the links point to the actual workflow files.

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 the job permissions only grants contents: write. Add issues: write (or switch to an API that aligns with the granted scopes) so the comment step doesn't fail due to insufficient token permissions.

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.

For workflow_dispatch runs, context.payload.pull_request is undefined, so isFork becomes undefined !== <repoFullName> which evaluates to true. This makes manual branch deletions always skip. Compute the fork check only when running on a PR event (e.g., wrap it in if (context.payload.pull_request)), or default isFork to false for manual 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