Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
63474af
feat: Add automatic AI cover generation for new blog articles
hobbytp Nov 13, 2025
5e2f555
🤖 自动更新每日AI动态 V2 - 2025-11-13
actions-user Nov 13, 2025
76a439a
fix: Update image optimization workflow to push to current branch
hobbytp Nov 13, 2025
76ece2b
fix: Use proper refname in image optimization workflow
hobbytp Nov 13, 2025
8805f45
fix: Add intelligent push logic to image optimization workflow
hobbytp Nov 13, 2025
25e6331
fix: Handle PR events correctly in image optimization workflow
hobbytp Nov 13, 2025
a69cf5c
Merge 25e6331e7d59526bdc28710a4fc889d3020857e3 into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
07c8988
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
0dca57f
Merge 07c89887f1558ee34149ba54ebc2da3f803a4765 into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
93eee96
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
de64eeb
Merge 93eee96e2b8a8fe93bf7a7218ece662af9808878 into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
bb28606
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
ffd5ffa
Merge bb28606a4258d43798749d41f5bc37f452aee213 into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
8592052
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
5fd0f2c
Merge 859205281bf910d6f1bb30bd3b9a7df6e8d47c72 into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
288eb01
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
298ba38
Merge 288eb012c3281cf24a2abb21fbc34eb0f3d208bc into 5e2f555814d001f5d…
hobbytp Nov 13, 2025
e5d88f7
🔧 优化图片: 自动生成WebP格式和响应式尺寸
github-actions[bot] Nov 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
208 changes: 191 additions & 17 deletions .github/workflows/generate-blog-images.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
name: Generate Blog Images

on:
workflow_dispatch: # 只支持手动触发
push:
branches:
- main
paths:
- 'content/**/*.md'
workflow_dispatch:
inputs:
image_service:
description: 'AI service to use'
required: false
default: 'modelscope'
type: choice
options:
- modelscope
- gemini
- both
target_type:
description: 'Generation target'
required: false
default: 'both'
type: choice
options:
- covers
- articles
- both
force_regenerate:
description: 'Force regenerate existing images'
required: false
default: false
type: boolean
auto_generate:
description: 'Auto-generate for new/updated articles only'
required: false
default: true
type: boolean

permissions:
contents: write
Expand All @@ -26,27 +60,57 @@ jobs:
run: |
cd scripts
pip install -r requirements.txt
pip install requests pillow python-dotenv

- name: Detect changed blog files
id: detect-changes
run: |
# 获取本次提交修改的.md文件
if [ "${{ github.event_name }}" = "push" ]; then
if [ -z "${{ github.event.before }}" ]; then
# No previous commit to diff against (e.g., first commit), so no changed files
CHANGED_FILES=""
# First commit, process all content files
CHANGED_FILES=$(find content -name "*.md" -not -path "*/daily_ai/*" | tr '\n' ' ')
echo "First commit detected, processing all files"
else
CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.md$' | grep '^content/' || true)
# Get changed .md files, excluding daily_ai
CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.md$' | grep '^content/' | grep -v 'daily_ai' || true)
echo "Push detected, analyzing changed files"
fi
else
# 手动触发时处理所有没有图片的博客
CHANGED_FILES=""
echo "Manual workflow triggered"
fi

# Filter for files that need AI covers (have description but no ai_cover)
NEED_AI_COVER=""
if [ -n "$CHANGED_FILES" ]; then
for file in $CHANGED_FILES; do
if [ -f "$file" ]; then
# Check if file has description
if grep -q "^description:" "$file"; then
# Check if file doesn't have ai_cover or ai_generated: true
if ! grep -q "ai_cover:" "$file" || ! grep -q "ai_generated: true" "$file"; then
NEED_AI_COVER="$NEED_AI_COVER $file"
echo "Need AI cover: $file"
else
echo "Already has AI cover: $file"
fi
else
echo "No description, skipping: $file"
fi
fi
done
fi

echo "changed_files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

echo "need_ai_cover<<EOF" >> $GITHUB_OUTPUT
echo "$NEED_AI_COVER" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Generate images for new blogs
if: steps.detect-changes.outputs.changed_files != ''
env:
Expand All @@ -72,42 +136,152 @@ jobs:
cd scripts
python generate_image.py --backfill

- name: Auto-generate ModelScope AI Covers for new blogs
if: github.event_name == 'push' && steps.detect-changes.outputs.need_ai_cover != ''
env:
MODELSCOPE_API_KEY: ${{ secrets.MODELSCOPE_API_KEY }}
TEXT2IMAGE_PROVIDER: modelscope
WORKFLOW_MODE: true
FORCE_REGENERATE: false
run: |
cd scripts
echo "🤖 Auto-generating ModelScope AI covers for new blogs..."
echo "Files needing AI covers: ${{ steps.detect-changes.outputs.need_ai_cover }}"

# Process files that need AI covers (limit to avoid API overload)
NEED_COVER_FILES="${{ steps.detect-changes.outputs.need_ai_cover }}"
file_count=0
max_files=5 # Limit per push to avoid API overload

for file in $NEED_COVER_FILES; do
if [ -f "$file" ] && [ $file_count -lt $max_files ]; then
echo "Processing: $file"
python ai_cover_generator.py --workflow-mode --target=covers --specific-file="$file"
file_count=$((file_count + 1))
elif [ $file_count -ge $max_files ]; then
echo "Reached limit of $max_files files, skipping remaining files"
break
fi
done

echo "✅ Auto-generation completed: processed $file_count files"

- name: Generate ModelScope AI Covers (manual trigger)
if: github.event_name == 'workflow_dispatch' && (contains(github.event.inputs.image_service, 'modelscope') || github.event.inputs.image_service == 'both')
env:
MODELSCOPE_API_KEY: ${{ secrets.MODELSCOPE_API_KEY }}
TEXT2IMAGE_PROVIDER: modelscope
WORKFLOW_MODE: true
FORCE_REGENERATE: ${{ github.event.inputs.force_regenerate }}
run: |
cd scripts
echo "🤖 Generating ModelScope AI covers..."
echo "Target: ${{ github.event.inputs.target_type }}"
echo "Force regenerate: $FORCE_REGENERATE"

# Generate covers if target includes covers
if [[ "${{ github.event.inputs.target_type }}" == "covers" || "${{ github.event.inputs.target_type }}" == "both" ]]; then
python ai_cover_generator.py --workflow-mode --target=covers --force=${FORCE_REGENERATE}
else
echo "Skipping cover generation (target: articles only)"
fi

- name: Create images directory
run: |
mkdir -p static/images/articles
echo "Created static/images/articles directory"
mkdir -p static/images/generated-covers
echo "Created image directories"

- name: Commit generated images
if: always()
env:
GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}
run: |
# 检查是否有新的图片文件
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git

has_changes=false

# Check for article images
if [ -d "static/images/articles" ] && [ "$(ls -A static/images/articles 2>/dev/null)" ]; then
echo "Found new images to commit"
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git
echo "Found new article images to commit"
git add static/images/articles/
git commit -m "Auto-generate blog images [skip ci]" || echo "No changes to commit"
has_changes=true
fi

# Check for generated covers
if [ -d "static/images/generated-covers" ] && [ "$(ls -A static/images/generated-covers 2>/dev/null)" ]; then
echo "Found new AI-generated covers to commit"
git add static/images/generated-covers/
has_changes=true
fi

# Commit if there are changes
if [ "$has_changes" = true ]; then
git commit -m "🤖 Auto-generate blog images and AI covers [skip ci]" || echo "No new changes to commit"
git push
echo "✅ Images committed successfully"
else
echo "No new images to commit"
echo "ℹ️ No new images to commit"
fi

- name: Create summary
if: always()
run: |
echo "## Blog Image Generation Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📋 Configuration" >> $GITHUB_STEP_SUMMARY
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Image Service:** ${{ github.event.inputs.image_service || 'N/A' }}" >> $GITHUB_STEP_SUMMARY
echo "**Target Type:** ${{ github.event.inputs.target_type || 'N/A' }}" >> $GITHUB_STEP_SUMMARY
echo "**Force Regenerate:** ${{ github.event.inputs.force_regenerate || 'false' }}" >> $GITHUB_STEP_SUMMARY
echo "**Changed files:** ${{ steps.detect-changes.outputs.changed_files }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Generated Images:" >> $GITHUB_STEP_SUMMARY
if [ -d "static/images/articles" ]; then
ls -la static/images/articles/ | tail -n +2 | while read line; do
echo "- $line" >> $GITHUB_STEP_SUMMARY

echo "### 🖼️ Generated Article Images" >> $GITHUB_STEP_SUMMARY
if [ -d "static/images/articles" ] && [ "$(ls -A static/images/articles 2>/dev/null)" ]; then
echo "| Filename | Size | Type |" >> $GITHUB_STEP_SUMMARY
echo "|----------|------|------|" >> $GITHUB_STEP_SUMMARY
cd static/images/articles
ls -lh *.jpg *.png *.webp 2>/dev/null | while read -r line; do
filename=$(echo $line | awk '{print $9}')
size=$(echo $line | awk '{print $5}')
echo "| $filename | $size | Image |" >> $GITHUB_STEP_SUMMARY
done
else
echo "No article images generated" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY

echo "### 🤖 Generated AI Covers" >> $GITHUB_STEP_SUMMARY
if [ -d "static/images/generated-covers" ] && [ "$(ls -A static/images/generated-covers 2>/dev/null)" ]; then
echo "| Filename | Size | AI Service |" >> $GITHUB_STEP_SUMMARY
echo "|----------|------|-----------|" >> $GITHUB_STEP_SUMMARY
cd static/images/generated-covers
ls -lh *.jpg *.png *.webp 2>/dev/null | while read -r line; do
filename=$(echo $line | awk '{print $9}')
size=$(echo $line | awk '{print $5}')
echo "| $filename | $size | ModelScope |" >> $GITHUB_STEP_SUMMARY
done
else
echo "No images directory found" >> $GITHUB_STEP_SUMMARY
echo "No AI covers generated" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY

# Add generation statistics
article_count=$(find static/images/articles -name "*.jpg" -o -name "*.png" -o -name "*.webp" 2>/dev/null | wc -l)
cover_count=$(find static/images/generated-covers -name "*.jpg" -o -name "*.png" -o -name "*.webp" 2>/dev/null | wc -l)

echo "### 📊 Statistics" >> $GITHUB_STEP_SUMMARY
echo "**Article Images:** $article_count files" >> $GITHUB_STEP_SUMMARY
echo "**AI Covers:** $cover_count files" >> $GITHUB_STEP_SUMMARY
echo "**Total Images:** $((article_count + cover_count)) files" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

echo "### ✅ Status" >> $GITHUB_STEP_SUMMARY
if [ $((article_count + cover_count)) -gt 0 ]; then
echo "🎉 **Generation Successful**: Images were generated and committed" >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ **No Generation**: No new images were generated" >> $GITHUB_STEP_SUMMARY
fi
31 changes: 29 additions & 2 deletions .github/workflows/image-optimization.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
- "!static/images/optimized/**"
- "!static/images/backup/**"
pull_request:
branches:
- main
paths:
- "static/images/**"
- "!static/images/optimized/**"
Expand Down Expand Up @@ -45,8 +47,12 @@ jobs:
restore-keys: |
${{ runner.os }}-pip-image-optimization-

- name: Install Python dependencies
- name: Install dependencies
run: |
# Install jq for JSON parsing
sudo apt-get update && sudo apt-get install -y jq

# Install Python dependencies
python -m pip install --upgrade pip
pip install -r tools/image-optimization/requirements.txt

Expand Down Expand Up @@ -88,5 +94,26 @@ jobs:
GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}
run: |
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git
git push origin main

# 智能推送逻辑
if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "Pushing to main branch"
git push origin main
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "Pull request detected, pushing to source branch instead"
# 获取PR的源分支名
SOURCE_BRANCH=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }} | \
jq -r '.head.ref')
echo "Source branch: $SOURCE_BRANCH"
if [[ "$SOURCE_BRANCH" != "null" && "$SOURCE_BRANCH" != "" ]]; then
git push origin HEAD:$SOURCE_BRANCH
else
echo "Could not determine source branch, skipping push"
fi
else
echo "Pushing to current branch"
git push origin HEAD:${{ github.ref_name }}
fi

echo "推送优化后的图片到仓库"
32 changes: 32 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,42 @@ make full-build
# AI-enhanced build pipeline
make full-build-ai

# Generate AI covers for articles
make generate-ai-covers

# Clean build artifacts
make clean
```

### AI Cover Generation

The blog features automated AI cover generation using ModelScope Qwen-image:

**🆕 Automatic Generation (Recommended):**
```bash
# Simply commit new articles, AI covers are generated automatically
git add content/zh/category/new-article.md
git commit -m "Add new article"
git push origin main
```

**Manual Generation:**
```bash
# Generate AI covers for articles without covers
make generate-ai-covers

# Limited batch generation (recommended for API limits)
conda run -n news_collector python scripts/ai_cover_generator.py --workflow-mode --limit=10

# Force regenerate existing covers
conda run -n news_collector python scripts/ai_cover_generator.py --workflow-mode --force
```

**GitHub Actions:**
- Automatic: Triggered on push to main branch
- Manual: Use "Generate Blog Images" workflow in Actions tab
- Supports: ModelScope (default), Gemini, or both services

### Content Management

```bash
Expand Down
28 changes: 0 additions & 28 deletions content/papers/example.md

This file was deleted.

Loading