-
Notifications
You must be signed in to change notification settings - Fork 0
334 lines (297 loc) · 15.3 KB
/
Copy pathpr-auto-cleanup.yml
File metadata and controls
334 lines (297 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# Automatic PR Code Cleanup using Sandbox Cleanup Application
# This workflow automatically applies Eclipse JDT cleanup transformations to pull requests
# Uses the composite action that wraps the sandbox cleanup application
#
# LIMITATIONS:
# - This workflow has limited support for pull requests from forked repositories.
# The default GITHUB_TOKEN does not have write permissions to push branches or create PRs in forks.
# - For PRs from the main repository: Full functionality (cleanup PR created automatically)
# - For PRs from forked repositories: Fallback mode (review comments + diff in PR comment)
# The workflow will post review comments and provide a full diff for manual review.
name: Auto PR Cleanup
on:
pull_request:
types: [opened, synchronize]
branches: [main]
paths:
- '**.java'
workflow_dispatch: # Keep manual triggering as fallback
permissions:
contents: write
pull-requests: write
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Run Sandbox Cleanup
uses: ./.github/actions/cleanup-action
with:
config-file: '.github/cleanup-profiles/standard.properties'
source-dir: '.'
verbose: 'true'
- name: Check for changes
id: check_changes
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
git status --short
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Collect change details
id: changes
if: steps.check_changes.outputs.has_changes == 'true'
run: |
# Anzahl geänderter Dateien
CHANGED_FILES=$(git status --porcelain | wc -l)
echo "changed_count=$CHANGED_FILES" >> $GITHUB_OUTPUT
# Liste der geänderten Dateien (erste 20)
CHANGED_LIST=$(git status --porcelain | awk '{print $2}' | head -20)
echo "changed_list<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_LIST" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Diff-Statistik
git add -A
DIFF_STAT=$(git diff --cached --stat)
echo "diff_stat<<EOF" >> $GITHUB_OUTPUT
echo "$DIFF_STAT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Kurze Diff-Vorschau (erste 100 Zeilen)
DIFF_PREVIEW=$(git diff --cached --no-color | head -100)
echo "diff_preview<<EOF" >> $GITHUB_OUTPUT
echo "$DIFF_PREVIEW" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create cleanup branch and commit changes
id: create_branch
if: steps.check_changes.outputs.has_changes == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Create a unique cleanup branch name, sanitizing slashes in the base branch
BASE_BRANCH="${{ github.event.pull_request.head.ref }}"
SANITIZED_BASE_BRANCH="${BASE_BRANCH//\//-}"
CLEANUP_BRANCH="cleanup/${SANITIZED_BASE_BRANCH}-${{ github.run_number }}"
echo "cleanup_branch=$CLEANUP_BRANCH" >> $GITHUB_OUTPUT
# Create and switch to cleanup branch
git checkout -b "$CLEANUP_BRANCH"
# Commit changes
git add -A
git commit -m "🧹 Cleanup: Apply Eclipse JDT transformations (${{ steps.changes.outputs.changed_count }} files)" \
-m "Changed files:" \
-m "${{ steps.changes.outputs.changed_list }}"
# Push to remote
git push origin "$CLEANUP_BRANCH"
- name: Create cleanup PR
id: create_pr
if: steps.check_changes.outputs.has_changes == 'true'
uses: actions/github-script@v8
with:
script: |
const cleanupBranch = ${{ toJSON(steps.create_branch.outputs.cleanup_branch) }};
const baseBranch = ${{ toJSON(github.event.pull_request.head.ref) }};
const changedCount = ${{ toJSON(steps.changes.outputs.changed_count) }};
const diffStat = ${{ toJSON(steps.changes.outputs.diff_stat) }};
const changedList = ${{ toJSON(steps.changes.outputs.changed_list) }};
const diffPreview = ${{ toJSON(steps.changes.outputs.diff_preview) }};
const changedFiles = changedList.split('\n')
.filter(f => f.trim())
.map(f => `- \`${f}\``)
.join('\n');
// Create PR
const prBody = '## 🧹 Sandbox Cleanup Proposal\n\n' +
'**This is an automatically generated cleanup pull request.** You can:\n' +
'- ✅ **Accept**: Merge this pull request\n' +
'- ❌ **Decline**: Close this pull request without merging\n' +
'- ✏️ **Modify**: Edit the files in this pull request before merging\n\n' +
'### Summary\n' +
`- **Changed files:** ${changedCount}\n` +
`- **Diff stats:** ${diffStat}\n\n` +
'### Changed files\n' +
changedFiles + '\n\n' +
'### Applied transformations\n' +
'- ✨ Code formatting and import organization\n' +
'- 📝 Added missing annotations (@Override, @Deprecated)\n' +
'- 🧹 Removed unnecessary code (casts, unused variables)\n' +
'- 🚀 Modernized code (lambdas, functional interfaces)\n' +
'- 🔧 Applied sandbox-specific cleanups\n\n' +
'### Diff preview\n' +
'<details>\n' +
'<summary>Click to show diff</summary>\n\n' +
'```diff\n' +
diffPreview + '\n' +
'```\n' +
'</details>\n\n' +
'**Profile:** standard\n\n' +
`**Target PR:** #${{ github.event.pull_request.number }}`;
try {
const { data: newPR } = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🧹 Cleanup for PR #${{ github.event.pull_request.number }}: ${changedCount} files`,
head: cleanupBranch,
base: baseBranch,
body: prBody
});
core.setOutput('pr_number', newPR.number);
core.setOutput('pr_url', newPR.html_url);
core.setOutput('pr_created', 'true');
return newPR.number;
} catch (error) {
core.setOutput('pr_created', 'false');
core.setOutput('error_message', error.message);
core.warning(`Failed to create cleanup PR: ${error.message}`);
// Note: This workflow does not support forked repositories due to GITHUB_TOKEN permissions.
// For forked PRs, the cleanup changes are available in the branch but no PR can be created.
return null;
}
- name: Create review comments for fork fallback
id: create_review
if: steps.check_changes.outputs.has_changes == 'true' && steps.create_pr.outputs.pr_created != 'true'
uses: actions/github-script@v8
with:
script: |
const changedList = ${{ toJSON(steps.changes.outputs.changed_list) }};
const diffPreview = ${{ toJSON(steps.changes.outputs.diff_preview) }};
// Get the list of files that were changed
const changedFiles = changedList.split('\n').filter(f => f.trim());
// Parse diff to extract file changes and create review comments
const diffLines = diffPreview.split('\n');
const comments = [];
let currentFile = null;
let currentHunk = null;
let lineNumber = 0;
for (const line of diffLines) {
// Match file headers like "diff --git a/file b/file"
const fileMatch = line.match(/^diff --git a\/(.*) b\/(.*)/);
if (fileMatch) {
currentFile = fileMatch[2];
continue;
}
// Match hunk headers like "@@ -1,2 +3,4 @@"
const hunkMatch = line.match(/^@@ -(\d+),?\d* \+(\d+),?\d* @@/);
if (hunkMatch && currentFile) {
lineNumber = parseInt(hunkMatch[2]);
continue;
}
// Track line numbers for additions (lines starting with +)
if (line.startsWith('+') && !line.startsWith('+++') && currentFile && lineNumber > 0) {
// Limit to first 10 files and 3 comments per file to avoid spam
const fileComments = comments.filter(c => c.path === currentFile);
if (comments.length < 10 && fileComments.length < 3) {
comments.push({
path: currentFile,
line: lineNumber,
body: `🧹 Cleanup suggestion: This line was modified by the automatic cleanup process.`
});
}
}
// Increment line number for context and additions
if (!line.startsWith('-') || line.startsWith('---')) {
lineNumber++;
}
}
// Create a review with comments if we have any
if (comments.length > 0) {
try {
const review = await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
event: 'COMMENT',
body: '🧹 **Automatic cleanup suggestions**\n\n' +
'The cleanup process identified improvements to your code. ' +
'Since this is from a forked repository, I cannot create a cleanup PR, ' +
'but I\'ve added review comments showing the suggested changes.\n\n' +
'**Note**: Due to diff preview limitations, only a sample of changes are shown. ' +
'For the complete diff, see the comment below.',
comments: comments
});
core.setOutput('review_created', 'true');
core.setOutput('review_id', review.data.id);
return review.data.id;
} catch (error) {
core.warning(`Failed to create review: ${error.message}`);
core.setOutput('review_created', 'false');
return null;
}
} else {
core.info('No suitable locations found for review comments');
core.setOutput('review_created', 'false');
return null;
}
- name: Comment on original PR
if: steps.check_changes.outputs.has_changes == 'true'
uses: actions/github-script@v8
with:
script: |
const prCreated = '${{ steps.create_pr.outputs.pr_created }}';
const cleanupPrNumber = '${{ steps.create_pr.outputs.pr_number }}';
const cleanupPrUrl = '${{ steps.create_pr.outputs.pr_url }}';
const changedCount = '${{ steps.changes.outputs.changed_count }}';
const errorMessage = '${{ steps.create_pr.outputs.error_message }}';
const cleanupBranch = '${{ steps.create_branch.outputs.cleanup_branch }}';
const reviewCreated = '${{ steps.create_review.outputs.review_created }}';
const diffStat = ${{ toJSON(steps.changes.outputs.diff_stat) }};
const changedList = ${{ toJSON(steps.changes.outputs.changed_list) }};
const diffPreview = ${{ toJSON(steps.changes.outputs.diff_preview) }};
let commentBody;
if (prCreated === 'true' && cleanupPrNumber) {
// Success case: PR was created
commentBody = `## 🧹 Cleanup suggestions available\n\n` +
`The automatic cleanup process analyzed **${changedCount} files** and found improvements.\n\n` +
`### 📋 Cleanup PR: #${cleanupPrNumber}\n` +
`🔗 ${cleanupPrUrl}\n\n` +
`### Your options:\n` +
`1. ✅ **Accept all changes**: Merge PR #${cleanupPrNumber}\n` +
`2. ✏️ **Adjust the changes**: Edit the files in PR #${cleanupPrNumber} and then merge\n` +
`3. ❌ **Reject the changes**: Close PR #${cleanupPrNumber} and continue with your original PR\n\n` +
`The cleanup PR contains detailed information about all proposed changes.`;
} else {
// Failure case: PR creation failed (likely due to fork permissions)
const changedFiles = changedList.split('\n')
.filter(f => f.trim())
.map(f => `- \`${f}\``)
.join('\n');
commentBody = `## 🧹 Cleanup suggestions available\n\n` +
`The automatic cleanup process analyzed **${changedCount} files** and found improvements.\n\n` +
`⚠️ **Note**: A cleanup PR could not be automatically created. This typically occurs when:\n` +
`- The original PR is from a forked repository (GITHUB_TOKEN lacks write permissions to forks)\n` +
`- There are API permission or rate limit issues\n\n`;
if (reviewCreated === 'true') {
commentBody += `### ✅ Review comments created\n` +
`I've added review comments showing suggested changes on specific lines. ` +
`Check the "Files changed" tab to see the suggestions.\n\n`;
}
commentBody += `### Summary\n` +
`- **Changed files:** ${changedCount}\n` +
`- **Diff stats:** ${diffStat}\n\n` +
`### Changed files\n` +
changedFiles + '\n\n' +
`### Diff preview\n` +
`<details>\n` +
`<summary>Click to show diff</summary>\n\n` +
`\`\`\`diff\n` +
diffPreview + `\n` +
`\`\`\`\n` +
`</details>\n\n` +
`### Manual review options:\n` +
`1. **Review the diff above** and manually apply changes you want\n` +
`2. **Check the cleanup branch**: \`${cleanupBranch}\`\n` +
` - Fetch: \`git fetch origin ${cleanupBranch}\`\n` +
` - Compare: \`git diff ${cleanupBranch}\`\n` +
` - Cherry-pick specific changes if desired\n\n` +
`**Error details**: ${errorMessage || 'Unknown error'}`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});