analysisstorer is not in the default plugin set, so pageSummary JSON files were never written. This plugin writes all message data to disk (browsertime.pageSummary.json, coach.pageSummary.json, etc.) which our parser reads to extract metrics. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
3.5 KiB
JavaScript
104 lines
3.5 KiB
JavaScript
import { spawn, execSync } from 'child_process';
|
|
import { join, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { existsSync } from 'fs';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const LOCAL_BIN = join(__dirname, '..', 'sitespeed.io', 'bin', 'sitespeed.js');
|
|
const REPORTS_DIR = process.env.REPORTS_DIR || join(__dirname, 'reports');
|
|
|
|
export function runTest(job, onLine) {
|
|
return new Promise((resolve, reject) => {
|
|
const outputFolder = join(REPORTS_DIR, job.id);
|
|
const isDocker = !!process.env.IN_DOCKER;
|
|
|
|
// Log to docker logs (stdout of main process) so it appears in `docker logs`
|
|
console.log(`[runner] REPORTS_DIR=${REPORTS_DIR}`);
|
|
console.log(`[runner] outputFolder=${outputFolder}`);
|
|
|
|
const sitespeedArgs = [
|
|
job.url,
|
|
'--browser', job.browser,
|
|
'-n', String(job.runs),
|
|
'--outputFolder', outputFolder,
|
|
'--plugins.add', 'analysisstorer', // not in default set — needed to write pageSummary JSON
|
|
'--sustainable.enable',
|
|
'--sustainable.useGreenWebHostingAPI',
|
|
'--axe.enable',
|
|
'--coach',
|
|
];
|
|
|
|
if (job.mobile) sitespeedArgs.push('--mobile');
|
|
|
|
if (isDocker) {
|
|
sitespeedArgs.push('--browsertime.chrome.args', 'no-sandbox');
|
|
sitespeedArgs.push('--browsertime.chrome.args', 'disable-dev-shm-usage');
|
|
sitespeedArgs.push('--browsertime.chrome.args', 'disable-gpu');
|
|
}
|
|
|
|
const env = { ...process.env };
|
|
|
|
let child;
|
|
|
|
if (isDocker) {
|
|
const bin = process.env.SITESPEED_BIN;
|
|
if (!bin) {
|
|
return reject(new Error(
|
|
'SITESPEED_BIN is not set. Check build logs for "Build-time sitespeed.js found at:"'
|
|
));
|
|
}
|
|
console.log(`[runner] spawning: node ${bin} ${sitespeedArgs.slice(0,3).join(' ')} ...`);
|
|
child = spawn('node', [bin, ...sitespeedArgs], { cwd: __dirname, env });
|
|
} else {
|
|
if (!existsSync(LOCAL_BIN)) {
|
|
return reject(new Error(`Local sitespeed.io not found at ${LOCAL_BIN}`));
|
|
}
|
|
child = spawn('node', [LOCAL_BIN, ...sitespeedArgs], { cwd: __dirname, env });
|
|
}
|
|
|
|
const allLines = [];
|
|
|
|
child.stdout.on('data', (data) => {
|
|
const lines = data.toString().split('\n').filter(Boolean);
|
|
for (const line of lines) {
|
|
allLines.push(line);
|
|
onLine(line);
|
|
// Mirror INFO/ERROR lines to docker logs too
|
|
if (line.includes('INFO:') || line.includes('ERROR:') || line.includes('stored in')) {
|
|
console.log('[sitespeed]', line);
|
|
}
|
|
}
|
|
});
|
|
|
|
child.stderr.on('data', (data) => {
|
|
const lines = data.toString().split('\n').filter(Boolean);
|
|
for (const line of lines) {
|
|
allLines.push('[stderr] ' + line);
|
|
onLine('[stderr] ' + line);
|
|
if (line.includes('ERROR:')) console.error('[sitespeed stderr]', line);
|
|
}
|
|
});
|
|
|
|
child.on('close', (code) => {
|
|
if (code === 0) {
|
|
// Log what was actually written so we can debug the parser
|
|
try {
|
|
const found = execSync(
|
|
`find "${outputFolder}" -name "*.json" 2>/dev/null | head -30`,
|
|
{ encoding: 'utf8' }
|
|
).trim();
|
|
console.log(`[runner] JSON files written:\n${found || '(none found)'}`);
|
|
} catch {}
|
|
resolve(outputFolder);
|
|
} else {
|
|
const tail = allLines.slice(-20).join('\n');
|
|
reject(new Error(`sitespeed.io exited with code ${code}\n${tail}`));
|
|
}
|
|
});
|
|
|
|
child.on('error', (err) => {
|
|
reject(new Error(`Failed to spawn process: ${err.message}`));
|
|
});
|
|
});
|
|
}
|