From 461baa98100265c6966b0aa775f78263a5874be2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Oct 2025 18:42:48 +0000 Subject: [PATCH 1/3] Initial plan From 46c3f2ec4062cf607b0e05d36d5e04b8189ee5a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Oct 2025 18:49:27 +0000 Subject: [PATCH 2/3] Add corrected check-media-attachments.yml workflow with all fixes Co-authored-by: A1L13N <193832434+A1L13N@users.noreply.github.com> --- .github/workflows/check-media-attachments.yml | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 .github/workflows/check-media-attachments.yml diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml new file mode 100644 index 000000000..50a2e54cf --- /dev/null +++ b/.github/workflows/check-media-attachments.yml @@ -0,0 +1,189 @@ +name: Check PR for media attachments when HTML files change + +"on": + pull_request: + types: [opened, synchronize, reopened] + paths: + - '**/*.html' + - '**/*.htm' + - '**/*.xhtml' + +concurrency: + group: check-media-attachments-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check-media-attachments: + runs-on: ubuntu-latest + name: Check media attachments in HTML files + permissions: + contents: read + pull-requests: write + issues: write + steps: + - name: Check for media attachments in HTML files + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + const { number } = context.payload.pull_request; + + console.log(`Checking PR #${number} for media attachments in HTML files`); + + // Get all changed files across all pages + const files = await github.paginate( + github.rest.pulls.listFiles, + { owner, repo, pull_number: number, per_page: 100 } + ); + + // Filter for HTML files that were added, modified, or renamed + const htmlFiles = files.filter(file => + /\.(?:html?|xhtml)$/i.test(file.filename) && + ['added', 'modified', 'renamed'].includes(file.status) + ); + + if (htmlFiles.length === 0) { + console.log('No HTML files were changed in this PR.'); + return; + } + + console.log(`Found ${htmlFiles.length} HTML file(s) to check:`); + htmlFiles.forEach(file => console.log(`- ${file.filename}`)); + + let hasIssues = false; + const issues = []; + const mediaReferences = []; + + for (const file of htmlFiles) { + console.log(`\nAnalyzing ${file.filename}...`); + + try { + // Get file content from the PR branch + const { data: fileData } = await github.rest.repos.getContent({ + owner, + repo, + path: file.filename, + ref: context.payload.pull_request.head.sha + }); + + // Handle truncated content or non-file responses + if (Array.isArray(fileData) || fileData.type !== 'file') { + throw new Error('Not a file content response'); + } + + let content; + if (fileData.encoding === 'base64' && fileData.content) { + content = Buffer.from(fileData.content, 'base64').toString('utf8'); + } else if (fileData.sha) { + const { data: blob } = await github.rest.git.getBlob({ + owner, + repo, + file_sha: fileData.sha + }); + content = Buffer.from(blob.content, 'base64').toString('utf8'); + } else { + throw new Error('Unable to retrieve file content'); + } + + // Check for images without alt attributes + const imgWithoutAlt = content.match(/]*\balt=)[^>]*>/gi); + if (imgWithoutAlt && imgWithoutAlt.length > 0) { + hasIssues = true; + issues.push(`**${file.filename}**: Found ${imgWithoutAlt.length} image(s) without alt attributes`); + console.log(` - Found ${imgWithoutAlt.length} img tag(s) without alt attributes`); + } + + // Check for media files referenced (including poster, src, href, and srcset) + const direct = [...content.matchAll(/\b(?:src|href|poster)\s*=\s*["']([^"']+\.(?:jpg|jpeg|png|gif|webp|svg|mp4|avi|mov|pdf))(?:\?[^"']*)?["']/gi)] + .map(m => m[1]) + .filter(u => !/^data:/i.test(u)); + const srcsetUrls = [...content.matchAll(/\bsrcset\s*=\s*["']([^"']+)["']/gi)] + .flatMap(m => m[1].split(',').map(s => s.trim().split(/\s+/)[0])) + .filter(u => /\.(?:jpg|jpeg|png|gif|webp|svg)$/i.test(u) && !/^data:/i.test(u)); + const allMedia = [...new Set([...direct, ...srcsetUrls])]; + + if (allMedia.length > 0) { + mediaReferences.push(`**${file.filename}**: References ${allMedia.length} media file(s)`); + console.log(` - Found ${allMedia.length} media reference(s)`); + + // Check for large image formats that could be optimized + const unoptimizedImages = allMedia.filter(u => /\.(jpg|jpeg|png)$/i.test(u)); + if (unoptimizedImages.length > 0) { + issues.push(`**${file.filename}**: Consider using WebP format for ${unoptimizedImages.length} image(s) for better performance`); + } + } + + // Check for missing figure captions for accessibility + const figureBlocks = Array.from(content.matchAll(/]*>([\s\S]*?)<\/figure>/gi)); + const figuresWithoutCaption = figureBlocks.filter(match => !/]/i.test(match[1])); + if (figuresWithoutCaption.length > 0) { + issues.push(`**${file.filename}**: Found ${figuresWithoutCaption.length} figure(s) without figcaption for accessibility`); + } + + } catch (error) { + console.error(`Error processing ${file.filename}: ${error.message}`); + issues.push(`**${file.filename}**: Could not analyze file - ${error.message}`); + } + } + + // Create summary comment + let commentBody = '## 📸 Media Attachments Analysis\n\n'; + + if (mediaReferences.length > 0) { + commentBody += '### Media Files Found\n'; + commentBody += mediaReferences.map(ref => `- ${ref}`).join('\n') + '\n\n'; + } + + if (hasIssues) { + commentBody += '### ⚠️ Issues Found\n'; + commentBody += issues.map(issue => `- ${issue}`).join('\n') + '\n\n'; + commentBody += '**Recommendations:**\n'; + commentBody += '- Add `alt` attributes to all images for accessibility\n'; + commentBody += '- Consider using WebP format for better performance\n'; + commentBody += '- Add `
` to `
` elements for screen readers\n\n'; + } else { + commentBody += '### ✅ All Checks Passed\n'; + commentBody += 'No media-related issues found in the HTML files.\n\n'; + } + + commentBody += '*This check was automatically performed when HTML files were modified.*'; + + // Check if we already commented on this PR + const { data: comments } = await github.rest.issues.listComments({ + owner, + repo, + issue_number: number, + }); + + const botComment = comments.find(comment => + comment.user.login === 'github-actions[bot]' && + comment.body.includes('📸 Media Attachments Analysis') + ); + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: botComment.id, + body: commentBody + }); + console.log('Updated existing media attachments comment'); + } else { + // Create new comment + await github.rest.issues.createComment({ + owner, + repo, + issue_number: number, + body: commentBody + }); + console.log('Created new media attachments comment'); + } + + // Don't fail the workflow, just inform + if (hasIssues) { + console.log('⚠️ Media attachment issues found, but not failing the workflow'); + } else { + console.log('✅ All media attachment checks passed!'); + } From e0869f4e552f264e2b736b6fe1877996effe855c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:13:09 +0000 Subject: [PATCH 3/3] Add linked issue media detection to workflow Co-authored-by: A1L13N <193832434+A1L13N@users.noreply.github.com> --- .github/workflows/check-media-attachments.yml | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index 50a2e54cf..39b7b9479 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -127,14 +127,60 @@ jobs: } } + // Check linked issues for media attachments + const issueMediaReferences = []; + const prBody = context.payload.pull_request.body || ''; + + // Extract issue numbers from PR body (e.g., "Fixes #123", "Closes #456", etc.) + const issueMatches = prBody.matchAll(/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi); + const linkedIssues = [...new Set([...issueMatches].map(match => parseInt(match[1])))]; + + for (const issueNumber of linkedIssues) { + try { + const { data: issue } = await github.rest.issues.get({ + owner, + repo, + issue_number: issueNumber + }); + + const issueBody = issue.body || ''; + + // Check for media in issue body using same detection logic + const issueDirect = [...issueBody.matchAll(/\b(?:src|href|poster)\s*=\s*["']([^"']+\.(?:jpg|jpeg|png|gif|webp|svg|mp4|avi|mov|pdf))(?:\?[^"']*)?["']/gi)] + .map(m => m[1]) + .filter(u => !/^data:/i.test(u)); + const issueSrcset = [...issueBody.matchAll(/\bsrcset\s*=\s*["']([^"']+)["']/gi)] + .flatMap(m => m[1].split(',').map(s => s.trim().split(/\s+/)[0])) + .filter(u => /\.(?:jpg|jpeg|png|gif|webp|svg)$/i.test(u) && !/^data:/i.test(u)); + const issueMedia = [...new Set([...issueDirect, ...issueSrcset])]; + + // Also check for direct image/video URLs in markdown format + const markdownMedia = [...issueBody.matchAll(/!\[([^\]]*)\]\(([^)]+\.(?:jpg|jpeg|png|gif|webp|svg|mp4))\)/gi)] + .map(m => m[2]); + const allIssueMedia = [...new Set([...issueMedia, ...markdownMedia])]; + + if (allIssueMedia.length > 0) { + issueMediaReferences.push(`**Issue #${issueNumber}** ([${issue.title}](${issue.html_url})): ${allIssueMedia.length} media file(s) found`); + console.log(` - Found ${allIssueMedia.length} media reference(s) in issue #${issueNumber}`); + } + } catch (error) { + console.log(`Could not check issue #${issueNumber}: ${error.message}`); + } + } + // Create summary comment let commentBody = '## 📸 Media Attachments Analysis\n\n'; if (mediaReferences.length > 0) { - commentBody += '### Media Files Found\n'; + commentBody += '### Media Files Found in Changed HTML Files\n'; commentBody += mediaReferences.map(ref => `- ${ref}`).join('\n') + '\n\n'; } + if (issueMediaReferences.length > 0) { + commentBody += '### Media Files Found in Linked Issues\n'; + commentBody += issueMediaReferences.map(ref => `- ${ref}`).join('\n') + '\n\n'; + } + if (hasIssues) { commentBody += '### ⚠️ Issues Found\n'; commentBody += issues.map(issue => `- ${issue}`).join('\n') + '\n\n'; @@ -142,7 +188,7 @@ jobs: commentBody += '- Add `alt` attributes to all images for accessibility\n'; commentBody += '- Consider using WebP format for better performance\n'; commentBody += '- Add `
` to `
` elements for screen readers\n\n'; - } else { + } else if (mediaReferences.length === 0 && issueMediaReferences.length === 0) { commentBody += '### ✅ All Checks Passed\n'; commentBody += 'No media-related issues found in the HTML files.\n\n'; }