rdev/deployments/k8s/base/playwright-scripts-configmap.yaml
jordan 9a1309a0c5 feat: fix composable monorepo CI builds + health endpoint improvements
Composable monorepo CI fixes:
- Add empty go.sum.tmpl files for pkg, service, worker, and cli components
- Fix Dockerfile.tmpl glob patterns (COPY go.work.sum* is invalid in Kaniko)
- Add deps step to CI that runs go work sync and go mod tidy before builds
- Fix scalar-go dependency version (v0.1.2 doesn't exist, use v0.13.0)

Health endpoint improvements:
- Add registry health check (zot OCI /v2/ endpoint)
- Add health metrics for CI, registry, and Git
- Add /health/ci endpoint for Woodpecker health

Visual verification scaffolding:
- Add Playwright pod and scripts ConfigMap
- Add vision.md and implementation breakdown plan

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:46:51 -07:00

109 lines
3.4 KiB
YAML

apiVersion: v1
kind: ConfigMap
metadata:
name: playwright-scripts
namespace: rdev
labels:
app.kubernetes.io/name: playwright
app.kubernetes.io/part-of: rdev
data:
capture.js: |
#!/usr/bin/env node
// capture.js - Playwright screenshot/video capture script
// Input: --url, --viewports (comma-separated), --output (dir),
// --wait-for (selector), --full-page, --video
// Output: JSON manifest to stdout
const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');
async function main() {
const args = parseArgs(process.argv.slice(2));
if (!args.url) {
console.error('Error: --url is required');
process.exit(1);
}
const outputDir = args.output || '/captures/default';
const viewports = args.viewports ? args.viewports.split(',') : ['1920x1080', '768x1024', '375x667'];
const waitFor = args['wait-for'] || 'body';
const fullPage = args['full-page'] === 'true';
const recordVideo = args.video === 'true';
// Ensure output directory exists
fs.mkdirSync(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const result = { screenshots: {} };
try {
for (const viewport of viewports) {
const [width, height] = viewport.split('x').map(Number);
const viewportName = `${width}x${height}`;
const contextOptions = {
viewport: { width, height },
};
if (recordVideo && viewport === viewports[0]) {
contextOptions.recordVideo = {
dir: outputDir,
size: { width, height }
};
}
const context = await browser.newContext(contextOptions);
const page = await context.newPage();
await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 });
await page.waitForSelector(waitFor, { timeout: 10000 }).catch(() => {});
const screenshotPath = path.join(outputDir, `${viewportName.replace('x', '_')}.png`);
await page.screenshot({ path: screenshotPath, fullPage });
result.screenshots[viewportName] = screenshotPath;
if (recordVideo && viewport === viewports[0]) {
await page.close();
const video = page.video();
if (video) {
const videoPath = await video.path();
const finalVideoPath = path.join(outputDir, 'recording.webm');
fs.renameSync(videoPath, finalVideoPath);
result.video = finalVideoPath;
}
}
await context.close();
}
} finally {
await browser.close();
}
console.log(JSON.stringify(result));
}
function parseArgs(argv) {
const args = {};
for (let i = 0; i < argv.length; i++) {
if (argv[i].startsWith('--')) {
const key = argv[i].slice(2);
const eqIdx = key.indexOf('=');
if (eqIdx !== -1) {
args[key.slice(0, eqIdx)] = key.slice(eqIdx + 1);
} else if (argv[i + 1] && !argv[i + 1].startsWith('--')) {
args[key] = argv[++i];
} else {
args[key] = 'true';
}
}
}
return args;
}
main().catch(err => {
console.error('Error:', err.message);
process.exit(1);
});