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
189 changes: 189 additions & 0 deletions .github/workflows/auto-merge-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Reusable auto-merge workflow for ANcpLua framework repos.
#
# Why a GitHub App instead of GITHUB_TOKEN:
# When auto-merge fires under GITHUB_TOKEN identity, the resulting `push: main`
# event is silently filtered by GitHub (anti-loop protection), so downstream
# publish workflows never run. Using an App installation token makes the App
# the merge actor — push events fire normally and downstream workflows trigger.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication
#
# Tiers:
# 1a. Dependabot PRs: Auto-approve + auto-merge for patch/minor
# 1b. Renovate PRs: Auto-approve + auto-merge
# 2. AI Agent fix PRs (copilot/, jules/, claude/): Auto-merge when CI passes
# 3. CodeRabbit approved PRs: Auto-merge when CI passes
# 4. Owner PRs: Auto-merge when CI passes

name: Auto-merge (reusable)

on:
workflow_call:
secrets:
AUTOMERGE_APP_ID:
description: "GitHub App ID for the auto-merge App (required)"
required: true
AUTOMERGE_APP_PRIVATE_KEY:
description: "GitHub App private key (PEM contents) for the auto-merge App (required)"
required: true

permissions:
contents: write
pull-requests: write

jobs:
dependabot-auto-merge:
name: Dependabot auto-merge
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v2.1.4
id: app-token
with:
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }}

- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3
with:
github-token: ${{ steps.app-token.outputs.token }}

- name: Auto-approve patch and minor updates
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr review --approve "$PR_URL"

- name: Enable auto-merge for patch and minor
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --squash "$PR_URL"

- name: Request Claude review for major updates
if: steps.metadata.outputs.update-type == 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
DEP_NAMES: ${{ steps.metadata.outputs.dependency-names }}
PREV_VERSION: ${{ steps.metadata.outputs.previous-version }}
NEW_VERSION: ${{ steps.metadata.outputs.new-version }}
run: |
gh pr comment "$PR_URL" --body "## ⚠️ Major Version Update

@claude Please review this major version update for breaking changes and merge if safe.

| Dependency | Update |
|------------|--------|
| \`$DEP_NAMES\` | \`$PREV_VERSION\` → \`$NEW_VERSION\` |

Check the changelog and verify compatibility."

renovate-auto-merge:
name: Renovate auto-merge
runs-on: ubuntu-latest
if: github.actor == 'renovate[bot]'
steps:
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v2.1.4
id: app-token
with:
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }}

- name: Auto-approve Renovate PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr review --approve "$PR_URL"

- name: Enable auto-merge for Renovate
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --squash "$PR_URL"

ai-agent-auto-merge:
name: AI Agent auto-merge
runs-on: ubuntu-latest
if: |
(github.actor == 'copilot[bot]' || github.actor == 'jules[bot]' || github.actor == 'claude-code[bot]') &&
(
startsWith(github.event.pull_request.head.ref, 'copilot/') ||
startsWith(github.event.pull_request.head.ref, 'jules/') ||
startsWith(github.event.pull_request.head.ref, 'claude/')
)
steps:
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v2.1.4
id: app-token
with:
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }}

- name: Identify AI agent
id: agent
env:
BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
if [[ "$BRANCH" == copilot/* ]]; then
echo "agent=Copilot" >> "$GITHUB_OUTPUT"
elif [[ "$BRANCH" == jules/* ]]; then
echo "agent=Jules" >> "$GITHUB_OUTPUT"
elif [[ "$BRANCH" == claude/* ]]; then
echo "agent=Claude" >> "$GITHUB_OUTPUT"
fi

- name: Auto-approve AI agent PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
AGENT: ${{ steps.agent.outputs.agent }}
run: |
echo "Auto-approving $AGENT PR"
gh pr review --approve "$PR_URL" --body "✅ Auto-approved: $AGENT autonomous fix PR"

- name: Enable auto-merge for AI agent PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --squash "$PR_URL"

coderabbit-auto-merge:
name: CodeRabbit auto-merge
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request_review' &&
github.event.review.state == 'approved' &&
github.event.review.user.login == 'coderabbitai[bot]'
steps:
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v2.1.4
id: app-token
with:
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }}

- name: Enable auto-merge for CodeRabbit approved PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --squash "$PR_URL"

owner-auto-merge:
name: Owner auto-merge
runs-on: ubuntu-latest
if: |
github.event.pull_request.user.login == github.event.repository.owner.login &&
github.event.pull_request.draft == false
steps:
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v2.1.4
id: app-token
with:
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }}

- name: Enable auto-merge for owner PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --squash "$PR_URL"
146 changes: 7 additions & 139 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
# Auto-merge workflow for trusted PRs
#
# Tiers:
# 1a. Dependabot PRs: Auto-approve + auto-merge for patch/minor
# 1b. Renovate PRs: Auto-approve + auto-merge
# 2. AI Agent fix PRs (copilot/, jules/, claude/): Auto-merge when CI passes
# 3. CodeRabbit approved PRs: Auto-merge when CI passes
# 4. Owner PRs: Auto-merge when CI passes
# Thin caller — delegates to the reusable workflow in this same repo.
# The reusable workflow lives at .github/workflows/auto-merge-reusable.yml
# and is also consumed by ANcpLua.NET.Sdk, ANcpLua.Roslyn.Utilities,
# ANcpLua.Analyzers, ANcpLua.Agents, and qyl.

name: Auto-merge

Expand All @@ -20,134 +16,6 @@ permissions:
pull-requests: write

jobs:
dependabot-auto-merge:
name: Dependabot auto-merge
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'

steps:
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Auto-approve patch and minor updates
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr review --approve "$PR_URL"

- name: Enable auto-merge for patch and minor
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --squash "$PR_URL"

- name: Request Claude review for major updates
if: steps.metadata.outputs.update-type == 'version-update:semver-major'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEP_NAMES: ${{ steps.metadata.outputs.dependency-names }}
PREV_VERSION: ${{ steps.metadata.outputs.previous-version }}
NEW_VERSION: ${{ steps.metadata.outputs.new-version }}
run: |
gh pr comment "$PR_URL" --body "## ⚠️ Major Version Update

@claude Please review this major version update for breaking changes and merge if safe.

| Dependency | Update |
|------------|--------|
| \`$DEP_NAMES\` | \`$PREV_VERSION\` → \`$NEW_VERSION\` |

Check the changelog and verify compatibility."

renovate-auto-merge:
name: Renovate auto-merge
runs-on: ubuntu-latest
if: github.actor == 'renovate[bot]'

steps:
- name: Auto-approve Renovate PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr review --approve "$PR_URL"

- name: Enable auto-merge for Renovate
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --squash "$PR_URL"

ai-agent-auto-merge:
name: AI Agent auto-merge
runs-on: ubuntu-latest
if: |
(github.actor == 'copilot[bot]' || github.actor == 'jules[bot]' || github.actor == 'claude-code[bot]') &&
(
startsWith(github.event.pull_request.head.ref, 'copilot/') ||
startsWith(github.event.pull_request.head.ref, 'jules/') ||
startsWith(github.event.pull_request.head.ref, 'claude/')
)

steps:
- name: Identify AI agent
id: agent
env:
BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
if [[ "$BRANCH" == copilot/* ]]; then
echo "agent=Copilot" >> "$GITHUB_OUTPUT"
elif [[ "$BRANCH" == jules/* ]]; then
echo "agent=Jules" >> "$GITHUB_OUTPUT"
elif [[ "$BRANCH" == claude/* ]]; then
echo "agent=Claude" >> "$GITHUB_OUTPUT"
fi

- name: Auto-approve AI agent PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AGENT: ${{ steps.agent.outputs.agent }}
run: |
echo "Auto-approving $AGENT PR"
gh pr review --approve "$PR_URL" --body "✅ Auto-approved: $AGENT autonomous fix PR"

- name: Enable auto-merge for AI agent PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --squash "$PR_URL"

coderabbit-auto-merge:
name: CodeRabbit auto-merge
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request_review' &&
github.event.review.state == 'approved' &&
github.event.review.user.login == 'coderabbitai[bot]'

steps:
- name: Enable auto-merge for CodeRabbit approved PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --squash "$PR_URL"

owner-auto-merge:
name: Owner auto-merge
runs-on: ubuntu-latest
if: |
github.event.pull_request.user.login == github.event.repository.owner.login &&
github.event.pull_request.draft == false

steps:
- name: Enable auto-merge for owner PRs
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --squash "$PR_URL"
auto-merge:
uses: ./.github/workflows/auto-merge-reusable.yml
secrets: inherit
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Shared Renovate preset for `ANcpLua.NET.Sdk`, `ANcpLua.Roslyn.Utilities`,

## Usage

### 1. Renovate preset

```jsonc
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
Expand All @@ -21,6 +23,39 @@ Per-repo self-bump pattern (in the consumer's local `renovate.json`):
| `ANcpLua.Analyzers` | `/^ANcpLua\\.Analyzers/`, `/^Dummy/` |
| `ANcpLua.Agents` | `/^ANcpLua\\.Agents/` |

### 2. Auto-merge reusable workflow

`.github/workflows/auto-merge-reusable.yml` is a [reusable workflow](https://docs.github.com/en/actions/using-workflows/reusing-workflows) — single source of truth for auto-merge tiers across the framework. Each consumer repo has a thin caller:

```yaml
# .github/workflows/auto-merge.yml in each consumer repo
name: Auto-merge
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
pull_request_review:
types: [submitted]
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
uses: ANcpLua/renovate-config/.github/workflows/auto-merge-reusable.yml@main
secrets: inherit
```

**Required secrets** (per consumer repo, or org-level if available):
- `AUTOMERGE_APP_ID` — GitHub App ID
- `AUTOMERGE_APP_PRIVATE_KEY` — full PEM contents of the App's private key

**Why a GitHub App, not GITHUB_TOKEN?** When auto-merge fires under GITHUB_TOKEN identity, the resulting `push: main` event is silently filtered by GitHub's anti-loop protection — downstream publish workflows never run. Using an App installation token makes the App the merge actor, so push events fire normally. See [GitHub docs](https://docs.github.com/en/actions/security-guides/automatic-token-authentication).

**One-time App setup**:
1. Create a GitHub App at <https://github.com/settings/apps/new> with permissions: Repository → Contents (Write), Pull requests (Write).
2. Generate a private key, download the `.pem` file.
3. Install the App on each consumer repo (or org-wide).
4. In each repo's secrets, set `AUTOMERGE_APP_ID` (the numeric App ID) and `AUTOMERGE_APP_PRIVATE_KEY` (full PEM contents including BEGIN/END lines).

## Why customManagers exist

Renovate handles native NuGet `PackageReference` and CPM `PackageVersion`
Expand Down
Loading