Files
Dress/.github/workflows/auto_fix.yml
Asahina Mafuyu 16c356e6a9
All checks were successful
Mark stale issues and pull requests / stale (push) Successful in 4s
[Enhancement] 添加自动修复工作流 (#337)
* feat: add GitHub Action to automatically strip EXIF data and compress images via PR comments

* fix: 提升尝试压缩后修复失败的用户体验

* feat: 现在修复结果会直接基于源 comment 做 update

* fix: 修复工作流仍然报告失败的问题

* doc: 添加自动修复引导

* ci: 改用 `github.rest.repos.compareCommitsWithBasehead`

* ci: 增强检查工作流
2026-04-08 00:31:01 +08:00

226 lines
8.8 KiB
YAML

name: Auto-fix Image Files
on:
issue_comment:
types: [created]
permissions:
contents: write
pull-requests: write
issues: write
jobs:
auto-fix:
name: Auto-fix Images (EXIF & Size)
if: >
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/auto-fix') &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.comment.user.login == github.event.issue.user.login)
runs-on: ubuntu-latest
steps:
- name: Acknowledge command and start processing
id: init
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: "rocket"
});
const response = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: "🚀 自动修复工作中...(Auto-fix initiated! Processing images...)"
});
core.setOutput("comment_id", response.data.id);
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Git identity and auth
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
gh auth setup-git
- name: Checkout PR branch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr checkout ${{ github.event.issue.number }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libimage-exiftool-perl imagemagick jpegoptim optipng pngquant
- name: Get list of changed image files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr diff ${{ github.event.issue.number }} --name-only | grep -iE '\.(jpe?g|png|webp|tiff?|hei[cf]|avif|gif)$' > changed_images.txt || true
if [ ! -s changed_images.txt ]; then
echo "No images changed in this PR."
exit 0
fi
echo "Changed images to process:"
cat changed_images.txt
- name: Process Phase 1 - Remove EXIF Data
run: |
if [ ! -s changed_images.txt ]; then exit 0; fi
exif_files_changed=0
while IFS= read -r file; do
if [ -f "$file" ]; then
count=$(exiftool -EXIF:all -S -q -- "$file" 2>/dev/null | wc -l || echo 0)
if [ "$count" -gt 0 ]; then
echo "Removing EXIF: $file"
exiftool -all= -overwrite_original "$file"
exif_files_changed=1
fi
fi
done < changed_images.txt
if [ "$exif_files_changed" -eq 1 ]; then
git add .
git commit -m "chore: auto-remove EXIF data from images" || true
fi
- name: Process Phase 2 - Compress Oversized Images
id: compress
continue-on-error: true
run: |
if [ ! -s changed_images.txt ]; then exit 0; fi
size_files_changed=0
failed_files=()
MAX_SIZE=$((1024 * 1024)) # 1MB Limit
while IFS= read -r file; do
if [ -f "$file" ]; then
size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo 0)
if [ "$size" -gt "$MAX_SIZE" ]; then
echo "Compressing: $file ($size bytes)"
ext="${file##*.}"
ext_lower=$(echo "$ext" | tr '[:upper:]' '[:lower:]')
case "$ext_lower" in
jpg|jpeg)
jpegoptim --size=990k "$file" || mogrify -quality 75 "$file"
;;
png)
# Use pngquant for highly effective lossy PNG compression first
pngquant --quality=60-80 --ext .png --force "$file" || optipng -o2 "$file"
;;
*)
mogrify -quality 75 "$file"
;;
esac
# Check if file size is successfully reduced under 1MB
new_size=$(stat -c%s "$file" 2>/dev/null || echo 0)
if [ "$new_size" -gt "$MAX_SIZE" ]; then
echo "Failed to compress $file below 1MB (Current: $new_size bytes)"
failed_files+=("$file")
else
size_files_changed=1
fi
fi
fi
done < changed_images.txt
if [ ${#failed_files[@]} -gt 0 ]; then
echo "Some files failed to compress below 1MB."
{
echo "failed=true"
echo "failed_list<<EOF"
for f in "${failed_files[@]}"; do echo "- $f"; done
echo "EOF"
} >> $GITHUB_OUTPUT
# Revert changes to prevent pushing conflicted or partial states
git reset --hard
exit 1
fi
if [ "$size_files_changed" -eq 1 ]; then
git add .
git commit -m "chore: auto-compress oversized images" || true
fi
- name: Push Changes
id: push
if: steps.compress.outcome != 'failure'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if ! git diff --quiet @{u}...HEAD; then
echo "Pushing changes back to the PR branch..."
git push
else
echo "No changes made during processing."
fi
- name: Report Completion
if: always()
uses: actions/github-script@v7
with:
script: |
const compressOutcome = "${{ steps.compress.outcome }}";
const failedList = `${{ steps.compress.outputs.failed_list }}`;
const commentId = `${{ steps.init.outputs.comment_id }}`;
let message = "";
if (compressOutcome === 'failure' && failedList.trim().length > 0) {
// Compression failed because some files couldn't be brought under 1MB
message = `❌ **自动修复未完全成功 (Auto-fix failed to compress some files).**
自动修复算法无法在保持分辨率的前提下,将以下文件压缩至 1MB 以内:
(The automated algorithm could not compress the following files strictly under 1MB without reducing their resolution:)
${failedList}
**如何手动修复 (How to fix manually):**
为了满足通过条件,请您在本地手动将这些图片尺寸缩小(缩小分辨率/长宽),或者转换为更高压缩率的格式后再次提交。
(Please manually scale down the images' resolution or convert them to a more efficient format and commit again.)
*请放心,为了防止冲突,本次自动化修改已被拦截,没有发起代码推送。*
*(The auto-fix push was aborted to prevent conflicts.)*`;
} else if (compressOutcome === 'failure') {
// Unexpected failure in the compress step
message = "❌ **自动修复失败 (Auto-fix failed).**\n请查看 [Actions Log](" + context.serverUrl + "/" + context.repo.owner + "/" + context.repo.repo + "/actions/runs/" + context.runId + ") 获取详情。(Please check the Actions Log for details.)";
} else {
// success or skipped — everything went fine
message = "✅ **自动修复成功 (Auto-fix completed successfully)!**\nEXIF 信息已被移除且文件已被压缩(如需要)。(EXIF data removed and files compressed if needed. Check the latest commits.)";
}
if (commentId && commentId !== "undefined" && commentId !== "") {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: parseInt(commentId, 10),
body: message
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: message
});
}