From 00719fba937c3b59904d0a87add47c40ee38155f Mon Sep 17 00:00:00 2001 From: "Calvin A. Allen" Date: Fri, 30 Jan 2026 11:07:46 -0500 Subject: [PATCH] ci(workflow): add automatic cover image generation for PRs Add workflow that triggers on PRs modifying blog posts to automatically generate cover images for posts that don't have one. The generated image is committed back to the PR branch and a comment is posted showing the preview. --- .github/workflows/generate-cover-image.yml | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 .github/workflows/generate-cover-image.yml diff --git a/.github/workflows/generate-cover-image.yml b/.github/workflows/generate-cover-image.yml new file mode 100644 index 0000000..f6742af --- /dev/null +++ b/.github/workflows/generate-cover-image.yml @@ -0,0 +1,124 @@ +name: Generate Cover Images + +on: + pull_request: + types: [opened, synchronize] + paths: + - 'src/content/blog/**/index.md' + +permissions: + contents: write + pull-requests: write + +jobs: + generate-covers: + name: Generate Missing Cover Images + runs-on: ubuntu-latest + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci + + - name: Find blog posts needing covers + id: find-posts + run: | + # Get list of changed files in this PR + CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + + # Find blog post directories with changes but no cover.png + POSTS_NEEDING_COVERS="" + + for file in $CHANGED_FILES; do + # Check if it's a blog post index.md + if [[ "$file" =~ ^src/content/blog/[0-9]+/.+/index\.md$ ]]; then + POST_DIR=$(dirname "$file") + COVER_PATH="$POST_DIR/cover.png" + + # Check if cover.png already exists + if [ ! -f "$COVER_PATH" ]; then + POSTS_NEEDING_COVERS="$POSTS_NEEDING_COVERS $POST_DIR" + echo "Post needs cover: $POST_DIR" + else + echo "Post already has cover: $POST_DIR" + fi + fi + done + + # Trim leading space and output + POSTS_NEEDING_COVERS=$(echo "$POSTS_NEEDING_COVERS" | xargs) + echo "posts=$POSTS_NEEDING_COVERS" >> $GITHUB_OUTPUT + + if [ -z "$POSTS_NEEDING_COVERS" ]; then + echo "No posts need cover images" + echo "has_posts=false" >> $GITHUB_OUTPUT + else + echo "has_posts=true" >> $GITHUB_OUTPUT + fi + + - name: Generate cover images + if: steps.find-posts.outputs.has_posts == 'true' + run: | + for post_dir in ${{ steps.find-posts.outputs.posts }}; do + echo "Generating cover for: $post_dir" + node scripts/generate-cover.js "$post_dir" + done + + - name: Commit and push cover images + if: steps.find-posts.outputs.has_posts == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Add only the generated cover images + for post_dir in ${{ steps.find-posts.outputs.posts }}; do + git add "$post_dir/cover.png" + done + + # Check if there are changes to commit + if git diff --staged --quiet; then + echo "No new cover images to commit" + echo "committed=false" >> $GITHUB_OUTPUT + else + git commit -m "ci(blog): generate cover images for new posts [skip ci]" + git push + echo "committed=true" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR with cover images + if: steps.find-posts.outputs.has_posts == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get the commit SHA after push + COMMIT_SHA=$(git rev-parse HEAD) + + # Build comment body with all generated covers + COMMENT="## 🖼️ Generated Cover Images\n\n" + + for post_dir in ${{ steps.find-posts.outputs.posts }}; do + # Extract post title from directory name + POST_SLUG=$(basename "$post_dir") + POST_YEAR=$(basename $(dirname "$post_dir")) + + # Build raw GitHub URL for the image + IMAGE_URL="https://raw.githubusercontent.com/${{ github.repository }}/${COMMIT_SHA}/${post_dir}/cover.png" + + COMMENT="${COMMENT}### ${POST_YEAR}/${POST_SLUG}\n\n" + COMMENT="${COMMENT}![Cover Image](${IMAGE_URL})\n\n" + done + + COMMENT="${COMMENT}---\n*If you'd like a different style, delete the cover image and push again, or generate one locally with \`npm run cover\`.*" + + # Post comment to PR + echo -e "$COMMENT" | gh pr comment ${{ github.event.pull_request.number }} --body-file -