Skip to content

Feat: Add auto formatting bot #14927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
312 changes: 312 additions & 0 deletions .github/workflows/auto-format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
name: Auto Format Bot

on:
issue_comment:
types: [created]

env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
format-check:
# Only run on pull requests when the bot is mentioned
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/format-fix apply')
runs-on: ubuntu-latest
timeout-minutes: 30

permissions:
contents: write
pull-requests: write
issues: write

steps:
- name: Check permissions
run: |
PR_AUTHOR=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json author --jq '.author.login')

PERMISSION=$(gh api repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission --jq '.permission' 2>/dev/null || echo "none")

# only allow if user is PR author OR has admin/write repository access
if [[ "${{ github.actor }}" == "$PR_AUTHOR" ]] || [[ "$PERMISSION" == "admin" ]] || [[ "$PERMISSION" == "write" ]]; then
echo "Permission granted: User @${{ github.actor }} can run the format bot"
if [[ "${{ github.actor }}" == "$PR_AUTHOR" ]]; then
echo " - Reason: PR author"
fi
if [[ "$PERMISSION" == "admin" ]] || [[ "$PERMISSION" == "write" ]]; then
echo " - Reason: Repository $PERMISSION access"
fi
else
echo "Error: User @${{ github.actor }} does not have permission to run the format bot."
echo "Required: Be the PR author OR have admin/write repository access."
exit 1
fi

- name: Add workflow started reaction
run: |
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
--method POST \
--input - <<< '{"content":"eyes"}'

- name: Get PR details and checkout
run: |
# Get PR details
PR_DATA=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json headRefName,headRepository,headRepositoryOwner)
HEAD_REF=$(echo "$PR_DATA" | jq -r '.headRefName')
HEAD_REPO=$(echo "$PR_DATA" | jq -r '.headRepository.name')
HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login')

echo "head_ref=$HEAD_REF" >> $GITHUB_ENV
echo "head_repo_full=$HEAD_OWNER/$HEAD_REPO" >> $GITHUB_ENV

- name: Detect if PR is from fork
run: |
HEAD_OWNER=$(echo "${{ env.head_repo_full }}" | cut -d'/' -f1)
REPO_OWNER="${{ github.repository_owner }}"

if [[ "$HEAD_OWNER" != "$REPO_OWNER" ]]; then
echo "is_fork=true" >> $GITHUB_ENV
echo "PR is from fork: ${{ env.head_repo_full }}"
else
echo "is_fork=false" >> $GITHUB_ENV
echo "PR is from same repository"
fi

- name: Checkout PR code
uses: actions/checkout@v4
with:
repository: ${{ env.head_repo_full }}
ref: ${{ env.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- uses: ./.github/actions/prepare-for-build

#this replaces ana06/get-changed-files@v2.3.0 since it doesn't support issue comment triggers
- name: Get changed files
id: changed-files
run: |
echo "Getting changed files for PR #${{ github.event.issue.number }}..."

ALL_FILES=$(gh pr diff ${{ github.event.issue.number }} --name-only --repo ${{ github.repository }})

FILTERED_FILES=$(echo "$ALL_FILES" | grep -E '\.(java|gradle|groovy|md|properties|xml|py|sh|bat|cmd)$' || true)

echo "All changed files:"
echo "$ALL_FILES"
echo
echo "Filtered files for formatting check:"
echo "$FILTERED_FILES"

echo "all<<EOF" >> $GITHUB_OUTPUT
echo "$ALL_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

echo "filtered<<EOF" >> $GITHUB_OUTPUT
echo "$FILTERED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Initial validation
id: initial-validation
continue-on-error: true
run: |
echo "Running initial validation..."
./gradlew check -x test

- name: Fix formatting issues
if: steps.initial-validation.outcome == 'failure'
run: |
echo "Fixing formatting issues..."
./gradlew tidy

- name: Check if formatting fixes were made
if: steps.initial-validation.outcome == 'failure'
id: check_changes
run: |
git add .
if git diff --staged --quiet; then
echo "No formatting changes were made"
echo "changes_made=false" >> $GITHUB_OUTPUT
else
echo "Formatting changes were made"
echo "changes_made=true" >> $GITHUB_OUTPUT
echo "Changed files:"
git diff --staged --name-only
fi

- name: Create fix branch and commit (same-repo)
if: steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'true' && env.is_fork == 'false'
id: create_fix_branch
run: |
# Create a new branch for the fixes
fix_branch="format-fixes-${{ env.head_ref }}-$(date +%s)"
echo "fix_branch=$fix_branch" >> $GITHUB_OUTPUT
echo "fix_branch=$fix_branch" >> $GITHUB_ENV

git config --local user.email "action@github.com"
git config --local user.name "Auto Format Bot"

git checkout -b "$fix_branch"
git commit -m "Apply automatic formatting fixes

Fixes applied by @auto-format-bot in response to:
${{ github.event.comment.html_url }}

Original PR: #${{ github.event.issue.number }}

Changes applied:
$(git diff --name-only HEAD~1)"

git push origin "$fix_branch"

- name: Commit directly to fork PR
if: steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'true' && env.is_fork == 'true'
id: commit_to_fork
run: |
git config --local user.email "action@github.com"
git config --local user.name "Auto Format Bot"

git commit -m "Apply automatic formatting fixes

Fixes applied by @auto-format-bot in response to:
${{ github.event.comment.html_url }}

Original PR: #${{ github.event.issue.number }}

Changes applied:
$(git diff --name-only HEAD~1)"

git push origin "${{ env.head_ref }}"

- name: Final validation
run: |
echo "Running final validation..."
./gradlew check -x test

- name: Create PR for fixes (same-repo only)
if: steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'true' && env.is_fork == 'false'
id: create_pr
run: |
PR_BODY="## Automatic Formatting Fixes

This PR applies automatic formatting fixes to address validation failures in #${{ github.event.issue.number }}.

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}
- **Original PR**: #${{ github.event.issue.number }}
- **Fix branch**: \`${{ env.fix_branch }}\`

### 🚀 Next Steps
Review and merge this PR to apply the formatting fixes to the original branch.

---
*This PR was created automatically by the Auto Format Bot*"

PR_URL=$(gh pr create \
--repo ${{ github.repository }} \
--title "Apply formatting fixes to #${{ github.event.issue.number }}" \
--body "$PR_BODY" \
--base "${{ env.head_ref }}" \
--head "${{ env.fix_branch }}")

PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$')
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT

- name: Add success reaction
if: success()
run: |
# Add success reaction
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
--method POST \
--input - <<< '{"content":"+1"}'

- name: Comment on PR with success (fixes applied to same-repo)
if: success() && steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'true' && env.is_fork == 'false'
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "## Formatting Fixes Applied

The formatting bot has successfully created a PR with formatting fixes!

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}
- **Fix PR**: #${{ steps.create_pr.outputs.pr_number }}

### 🚀 Next Steps
Review and merge PR #${{ steps.create_pr.outputs.pr_number }} to apply the formatting fixes to this branch.

---
*This was performed automatically by the Auto Format Bot*"

- name: Comment on PR with success (fixes applied to fork)
if: success() && steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'true' && env.is_fork == 'true'
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "## Formatting Fixes Applied

The formatting bot has commited the formatting fixes to this PR!

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}
- **Action taken**: Direct commit to this PR branch

---
*This was performed automatically by the Auto Format Bot*"

- name: Comment on PR with success (no fixes needed)
if: success() && steps.initial-validation.outcome == 'success'
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "## No Formatting Issues Found

The formatting bot has validated this PR and found no issues!

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}

The PR is ready for review!

---
*This was performed automatically by the Auto Format Bot*"

- name: Comment on PR with success (fixes made but no changes)
if: success() && steps.initial-validation.outcome == 'failure' && steps.check_changes.outputs.changes_made == 'false'
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "## Formatting Issues Detected (No Auto-fixes Available)

The formatting bot found issues but was unable to automatically fix them.

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}

**Recommendation**: Review the validation failures manually and apply fixes as needed.

---
*This was performed automatically by the Auto Format Bot*"

- name: Add failure reaction
if: failure()
run: |
# Add failure reaction
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
--method POST \
--input - <<< '{"content":"-1"}'

- name: Comment on PR with failure
if: failure()
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "## Formatting Failed

The formatting bot encountered issues while processing this PR.

### Next Steps:
1. Check the workflow logs for specific error details
2. Fix any issues manually if needed
3. Re-trigger the bot with \`/format-fix apply\`

### 📝 Details
- **Triggered by**: ${{ github.event.comment.html_url }}
- **Files processed**: ${{ steps.changed-files.outputs.all || 'None detected' }}

---
*This was performed automatically by the Auto Format Bot*"
Loading