Add CLI activation and single process IPC #7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| env: | |
| SCREENSHOTS_DIR: tests/TimeToKill.App.Tests/bin/Debug/net10.0/Screenshots | |
| B2_BUCKET: kjerk-github-artifacts | |
| B2_ENDPOINT: https://s3.us-west-004.backblazeb2.com | |
| B2_PUBLIC_BASE: https://kjerk-github-artifacts.s3.us-west-004.backblazeb2.com | |
| 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)) | |
| 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." | |
| exit 1 | |
| fi | |
| echo "$(basename $f): ${size} bytes - OK" | |
| done | |
| else | |
| echo "::warning::No screenshots directory found" | |
| fi | |
| - name: Upload screenshots to B2 | |
| run: | | |
| PR_NUM=${{ github.event.pull_request.number }} | |
| aws s3 sync "$SCREENSHOTS_DIR" "s3://${B2_BUCKET}/TimeToKill/pr/${PR_NUM}/" \ | |
| --endpoint-url "$B2_ENDPOINT" \ | |
| --delete | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.B2_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.B2_APP_KEY }} | |
| - name: Comment on PR with screenshots | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const dir = process.env.SCREENSHOTS_DIR; | |
| const prNum = context.payload.pull_request.number; | |
| const base = `${process.env.B2_PUBLIC_BASE}/TimeToKill/pr/${prNum}`; | |
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const sha = context.payload.pull_request.head.sha.substring(0, 7); | |
| const files = fs.existsSync(dir) | |
| ? fs.readdirSync(dir).filter(f => f.endsWith('.png')).sort() | |
| : []; | |
| const total = files.length; | |
| let body = `### Screenshot Tests: ${total} captured · \`${sha}\`\n\n`; | |
| if (total === 0) { | |
| body += `No screenshots captured.\n`; | |
| } else { | |
| const cols = 3; | |
| for (let i = 0; i < files.length; i += cols) { | |
| const row = files.slice(i, i + cols); | |
| const pad = cols - row.length; | |
| // Header row | |
| body += `| ${row.map(f => `\`${f}\``).join(' | ')}${pad > 0 ? ' |'.repeat(pad) : ''} |\n`; | |
| body += `|${row.map(() => ':-:').join('|')}${pad > 0 ? '|:-:'.repeat(pad) : ''}|\n`; | |
| // Image row | |
| const cells = row.map(f => { | |
| const url = `${base}/${f}`; | |
| return `<a href="${url}"><img src="${url}" width="320" alt="${f}"></a>`; | |
| }); | |
| body += `| ${cells.join(' | ')}${pad > 0 ? ' |'.repeat(pad) : ''} |\n\n`; | |
| } | |
| } | |
| body += `\n<sub><a href="${runUrl}">workflow run</a></sub>`; | |
| 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) { | |
| console.log('Could not remove label:', e.message); | |
| } |