From b7e3440ef19210c286f15f9be6da56f09fd6f2c3 Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Fri, 5 Sep 2025 08:37:38 +0530 Subject: [PATCH 1/8] Fix: Add missing media attachments checker workflow Resolves issue #571 - SyntaxError: Unexpected token '|' - Adds comprehensive HTML media attachment validation - Includes accessibility and performance checks - Uses proper YAML and JavaScript syntax --- .github/workflows/check-media-attachments.yml | 161 ++++++++++++++++++ 1 file changed, 161 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..c0f5f8dcd --- /dev/null +++ b/.github/workflows/check-media-attachments.yml @@ -0,0 +1,161 @@ +name: Check PR for media attachments when HTML files change + +on: + pull_request: + types: [opened, synchronize] + # optionally: [opened, synchronize, reopened] + +jobs: + check-media-attachments: + runs-on: ubuntu-latest + name: Check media attachments in HTML files + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check for media attachments in HTML files + uses: actions/github-script@v6 + 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 the list of files changed in this PR + const { data: files } = await github.rest.pulls.listFiles({ + owner, + repo, + pull_number: number + }); + + // Filter for HTML files that were added or modified + const htmlFiles = files.filter(file => + file.filename.endsWith('.html') && + (file.status === 'added' || file.status === 'modified') + ); + + 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 + }); + + const content = Buffer.from(fileData.content, 'base64').toString('utf8'); + + // Check for images without alt attributes + const imgWithoutAlt = content.match(/]*\salt=)[^>]*>/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 + const mediaMatches = content.match(/(src|href)=["'][^"']*\.(jpg|jpeg|png|gif|webp|svg|mp4|avi|mov|pdf)["']/gi); + if (mediaMatches && mediaMatches.length > 0) { + mediaReferences.push(`**${file.filename}**: References ${mediaMatches.length} media file(s)`); + console.log(` - Found ${mediaMatches.length} media reference(s)`); + + // Check for large image formats that could be optimized + const unoptimizedImages = mediaMatches.filter(match => + /\.(jpg|jpeg|png)["']/i.test(match) + ); + 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 figuresWithoutCaption = content.match(/]*]*>[\s\S]*?<\/figure>/gi); + if (figuresWithoutCaption && 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!'); + } \ No newline at end of file From 77300b6fda0a2e4ae1dc24b9ac6e0ded8158c3b2 Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Mon, 6 Oct 2025 17:32:01 +0530 Subject: [PATCH 2/8] chore: requested changes are made --- .github/workflows/check-media-attachments.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index c0f5f8dcd..61b71d450 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -65,7 +65,7 @@ jobs: const content = Buffer.from(fileData.content, 'base64').toString('utf8'); // Check for images without alt attributes - const imgWithoutAlt = content.match(/]*\salt=)[^>]*>/gi); + 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`); @@ -87,9 +87,10 @@ jobs: } } - // Check for missing figure captions for accessibility - const figuresWithoutCaption = content.match(/]*]*>[\s\S]*?<\/figure>/gi); - if (figuresWithoutCaption && figuresWithoutCaption.length > 0) { + // Find all
...
blocks and check if they lack
+ 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`); } From 477980e8c3712916af347d4fdf1a4045f208047c Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Mon, 6 Oct 2025 21:51:26 +0530 Subject: [PATCH 3/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 181 ++++++------------ 1 file changed, 55 insertions(+), 126 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index 61b71d450..cbbd3de52 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -1,162 +1,91 @@ -name: Check PR for media attachments when HTML files change +name: Check PR for Screenshots when HTML changes on: pull_request: - types: [opened, synchronize] - # optionally: [opened, synchronize, reopened] + types: [opened, synchronize, reopened] + paths: + - '**/*.html' jobs: - check-media-attachments: + check-for-screenshots: runs-on: ubuntu-latest - name: Check media attachments in HTML files + name: Check for PR screenshots steps: - name: Checkout code uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check for media attachments in HTML files - uses: actions/github-script@v6 + - name: Check for screenshots in PR description or comments + 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 the list of files changed in this PR - const { data: files } = await github.rest.pulls.listFiles({ + const prNumber = context.payload.pull_request.number; + const botCommentIdentifier = ''; + + console.log(`Checking PR #${prNumber} for screenshots because HTML files were changed.`); + + // --- 1. Get PR body + comments --- + const prBody = context.payload.pull_request.body || ''; + + const { data: comments } = await github.rest.issues.listComments({ owner, repo, - pull_number: number + issue_number: prNumber, }); - - // Filter for HTML files that were added or modified - const htmlFiles = files.filter(file => - file.filename.endsWith('.html') && - (file.status === 'added' || file.status === 'modified') + + const allCommentsText = comments.map(comment => comment.body || '').join('\n'); + const combinedText = prBody + '\n' + allCommentsText; + + // --- 2. Look for actual media (image/video uploads) --- + const mediaRegex = /(https:\/\/user-images\.githubusercontent\.com\/\d+\/.+\.(png|jpe?g|gif|webp|mp4|mov))|(!\[.*\]\(.*\.(png|jpe?g|gif|webp)\))/i; + const mediaFound = mediaRegex.test(combinedText); + + // --- 3. Find previous bot comment --- + const botComment = comments.find(comment => + comment.user?.login === 'github-actions[bot]' && + comment.body?.includes(botCommentIdentifier) ); - - 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({ + + if (mediaFound) { + console.log('✅ Screenshot or media found in the PR.'); + if (botComment) { + await github.rest.issues.deleteComment({ owner, repo, - path: file.filename, - ref: context.payload.pull_request.head.sha + comment_id: botComment.id, }); - - const content = Buffer.from(fileData.content, 'base64').toString('utf8'); - - // 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 - const mediaMatches = content.match(/(src|href)=["'][^"']*\.(jpg|jpeg|png|gif|webp|svg|mp4|avi|mov|pdf)["']/gi); - if (mediaMatches && mediaMatches.length > 0) { - mediaReferences.push(`**${file.filename}**: References ${mediaMatches.length} media file(s)`); - console.log(` - Found ${mediaMatches.length} media reference(s)`); - - // Check for large image formats that could be optimized - const unoptimizedImages = mediaMatches.filter(match => - /\.(jpg|jpeg|png)["']/i.test(match) - ); - if (unoptimizedImages.length > 0) { - issues.push(`**${file.filename}**: Consider using WebP format for ${unoptimizedImages.length} image(s) for better performance`); - } - } - - // Find all
...
blocks and check if they lack
- 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}`); + console.log('Removed previous warning comment.'); } + return; } + + // --- 4. Post or update warning comment --- + const commentBody = `${botCommentIdentifier} - // 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') - ); - + ## Screenshot Missing + + You've modified **HTML files**, but no screenshots or recordings were found in the PR description or comments. + + *Please attach a screenshot or GIF demonstrating the change.*`; + if (botComment) { - // Update existing comment await github.rest.issues.updateComment({ owner, repo, comment_id: botComment.id, - body: commentBody + body: commentBody, }); - console.log('Updated existing media attachments comment'); + console.log('Updated existing warning comment.'); } else { - // Create new comment await github.rest.issues.createComment({ owner, repo, - issue_number: number, - body: commentBody + issue_number: prNumber, + body: commentBody, }); - console.log('Created new media attachments comment'); + console.log('Created a new warning 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!'); - } \ No newline at end of file + + // --- 5. Fail workflow to block merging --- + core.setFailed('Missing screenshots for HTML changes. Please add one to continue.'); From a2c5a6c417b8f944c76b13e515fa17120e49316d Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Mon, 6 Oct 2025 23:14:10 +0530 Subject: [PATCH 4/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index cbbd3de52..84f4ac5b5 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -62,7 +62,7 @@ jobs: // --- 4. Post or update warning comment --- const commentBody = `${botCommentIdentifier} - + ## Screenshot Missing You've modified **HTML files**, but no screenshots or recordings were found in the PR description or comments. From 4416eb229e523e61d49e271b4d7e7c0be602a2da Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Mon, 6 Oct 2025 23:26:11 +0530 Subject: [PATCH 5/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index 84f4ac5b5..21b5b822a 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -10,6 +10,10 @@ jobs: check-for-screenshots: runs-on: ubuntu-latest name: Check for PR screenshots + permissions: + contents: read + pull-requests: read + issues: write steps: - name: Checkout code uses: actions/checkout@v4 From 67f7b7ad175575da0d1647ed81a1bd76e42c617d Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Mon, 6 Oct 2025 23:41:43 +0530 Subject: [PATCH 6/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index 21b5b822a..afa8c7c20 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -42,8 +42,27 @@ jobs: const combinedText = prBody + '\n' + allCommentsText; // --- 2. Look for actual media (image/video uploads) --- - const mediaRegex = /(https:\/\/user-images\.githubusercontent\.com\/\d+\/.+\.(png|jpe?g|gif|webp|mp4|mov))|(!\[.*\]\(.*\.(png|jpe?g|gif|webp)\))/i; - const mediaFound = mediaRegex.test(combinedText); + + // Extract URLs from Markdown image syntax (![alt](url)) + const mdImageUrls = [...combinedText.matchAll(/!\[[^\]]*\]\(([^)]+)\)/gi)].map(m => m[1]); + + // Extract all plain URLs in the text + const allUrls = [...combinedText.matchAll(/\bhttps?:\/\/[^\s)>"']+/gi)].map(m => m[0]); + + // Merge and deduplicate all URLs + const candidates = [...new Set([...mdImageUrls, ...allUrls])]; + + // Define file extensions that count as media + const mediaExt = /\.(?:png|jpe?g|gif|webp|svg|apng|mp4|mov|webm)$/i; + + // Define known GitHub hosts for attachments (including new user-attachments paths) + const ghAssetsHost = /(?:^|\/\/)(?:user-images\.githubusercontent\.com|private-user-images\.githubusercontent\.com|github\.com\/user-attachments\/assets|github-production-user-asset-[^/]+\.s3\.amazonaws\.com)\b/i; + + // Filter candidate URLs: either matches media extensions OR GitHub attachment hosts + const mediaUrls = candidates.filter(u => mediaExt.test(u) || ghAssetsHost.test(u)); + + // Final flag: true if at least one media URL found + const mediaFound = mediaUrls.length > 0; // --- 3. Find previous bot comment --- const botComment = comments.find(comment => From 503ec6f9790072d63199e2aa480a36ae7168e26b Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Tue, 7 Oct 2025 00:11:46 +0530 Subject: [PATCH 7/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index afa8c7c20..4151ba3df 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -32,11 +32,10 @@ jobs: // --- 1. Get PR body + comments --- const prBody = context.payload.pull_request.body || ''; - const { data: comments } = await github.rest.issues.listComments({ - owner, - repo, - issue_number: prNumber, - }); + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number: prNumber } + ); const allCommentsText = comments.map(comment => comment.body || '').join('\n'); const combinedText = prBody + '\n' + allCommentsText; From f43167fca19c3cbafad3b4fa47b51d89ab53c4cf Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Tue, 7 Oct 2025 00:21:38 +0530 Subject: [PATCH 8/8] enforce screenshot requirement on HTML changes --- .github/workflows/check-media-attachments.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-media-attachments.yml b/.github/workflows/check-media-attachments.yml index 4151ba3df..ddea3904e 100644 --- a/.github/workflows/check-media-attachments.yml +++ b/.github/workflows/check-media-attachments.yml @@ -72,12 +72,16 @@ jobs: if (mediaFound) { console.log('✅ Screenshot or media found in the PR.'); if (botComment) { - await github.rest.issues.deleteComment({ - owner, - repo, - comment_id: botComment.id, - }); - console.log('Removed previous warning comment.'); + try { + await github.rest.issues.deleteComment({ + owner, + repo, + comment_id: botComment.id, + }); + console.log('Removed previous warning comment.'); + } catch (e) { + console.warn(`Could not delete previous comment (likely fork perms): ${e.status || ''}`); + } } return; }