Skip to content

feat: Add CLI arguments support for LaunchAndKill action #4

feat: Add CLI arguments support for LaunchAndKill action

feat: Add CLI arguments support for LaunchAndKill action #4

name: PR Screenshots
# Triggered when a maintainer adds the 'run-tests' label to a PR.
on:
pull_request:
types: [labeled]
jobs:
screenshots:
if: github.event.label.name == 'run-tests'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
actions: read
steps:
- name: Checkout PR
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Run screenshot tests
run: dotnet test tests/TimeToKill.App.Tests/ --verbosity normal
- name: Validate screenshot sizes
run: |
MAX_SIZE=$((5 * 1024 * 1024)) # 5MB per file
SCREENSHOTS_DIR="tests/TimeToKill.App.Tests/bin/Debug/net10.0/Screenshots"
if [ -d "$SCREENSHOTS_DIR" ]; then
for f in "$SCREENSHOTS_DIR"/*.png; do
size=$(stat -c%s "$f")
if [ "$size" -gt "$MAX_SIZE" ]; then
echo "::error::Screenshot $(basename $f) is ${size} bytes (max ${MAX_SIZE}). Aborting upload."
exit 1
fi
echo "$(basename $f): ${size} bytes - OK"
done
else
echo "::warning::No screenshots directory found"
fi
- name: Upload screenshots
uses: actions/upload-artifact@v4
if: always()
with:
name: screenshots
path: tests/TimeToKill.App.Tests/bin/Debug/net10.0/Screenshots/
if-no-files-found: warn
- name: Try uploading screenshots to GitHub CDN
id: upload-images
if: success()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const screenshotsDir = 'tests/TimeToKill.App.Tests/bin/Debug/net10.0/Screenshots';
const files = fs.existsSync(screenshotsDir)
? fs.readdirSync(screenshotsDir).filter(f => f.endsWith('.png')).sort()
: [];
if (files.length === 0) {
core.setOutput('result', JSON.stringify({ files: [], urls: {} }));
return;
}
const token = process.env.GITHUB_TOKEN;
const owner = context.repo.owner;
const repo = context.repo.repo;
const prNumber = context.issue.number;
const urls = {};
for (const file of files) {
const filePath = path.join(screenshotsDir, file);
const fileData = fs.readFileSync(filePath);
const blob = new Blob([fileData], { type: 'image/png' });
// S1: https://github.com/upload/assets
try {
const form1 = new FormData();
form1.append('file', blob, file);
const res1 = await fetch('https://github.com/upload/assets', {
method: 'POST',
headers: { 'Authorization': `token ${token}` },
body: form1
});
console.log(`S1 ${file}: status=${res1.status}`);
const text1 = await res1.text();
console.log(`S1 ${file}: body=${text1}`);
if (res1.ok) {
try {
const json1 = JSON.parse(text1);
if (json1.url) { urls[file] = json1.url; continue; }
} catch (e) { console.log(`S1 ${file}: not JSON`); }
}
} catch (e) { console.log(`S1 ${file}: error=${e.message}`); }
// S2: https://github.com/{owner}/{repo}/issues/{pr}/attachments
try {
const form2 = new FormData();
form2.append('file', blob, file);
const res2 = await fetch(
`https://github.com/${owner}/${repo}/issues/${prNumber}/attachments`,
{
method: 'POST',
headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' },
body: form2
}
);
console.log(`S2 ${file}: status=${res2.status}`);
const text2 = await res2.text();
console.log(`S2 ${file}: body=${text2}`);
if (res2.ok) {
try {
const json2 = JSON.parse(text2);
if (json2.url) { urls[file] = json2.url; continue; }
} catch (e) { console.log(`S2 ${file}: not JSON`); }
}
} catch (e) { console.log(`S2 ${file}: error=${e.message}`); }
console.log(`${file}: both methods failed, no inline URL`);
}
console.log('Upload results:', JSON.stringify(urls, null, 2));
core.setOutput('result', JSON.stringify({ files, urls }));
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Comment on PR with screenshots
if: success()
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const uploadResult = JSON.parse('${{ steps.upload-images.outputs.result }}' || '{"files":[],"urls":{}}');
const { files, urls } = uploadResult;
let body = `## Screenshot Results\n\n`;
if (files.length === 0) {
body += `No screenshots captured.\n`;
} else {
const hasInline = Object.keys(urls).length > 0;
if (hasInline) {
for (const file of files) {
if (urls[file]) {
body += `### ${file}\n![${file}](${urls[file]})\n\n`;
} else {
body += `### ${file}\n_Upload failed - see artifacts_\n\n`;
}
}
} else {
body += `| # | Screenshot |\n|---|---|\n`;
for (const f of files) {
body += `| ${f} | :white_check_mark: |\n`;
}
body += `\n_Inline image upload not available._\n`;
}
body += `\n[Download all screenshots](${runUrl}#artifacts)\n`;
}
body += `\n---\n_[PR Screenshots workflow](${runUrl})_`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
- name: Remove run-tests label
if: always()
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name: 'run-tests'
});
} catch (e) {
// Label may have already been removed
console.log('Could not remove label:', e.message);
}