mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
JavaScript
/*
|
|
* HTTP server to intercept benchmark data from Codspeed runner.
|
|
*
|
|
* Codspeed runner makes 2 API calls:
|
|
* 1. Uploading metadata
|
|
* 2. Uploading archive of CPU profile files
|
|
*
|
|
* Server starts on an available port, saves the files sent by Codspeed runner to a directory,
|
|
* then shuts itself down.
|
|
*/
|
|
|
|
import express from 'express';
|
|
import { createWriteStream } from 'fs';
|
|
import fs from 'fs/promises';
|
|
import { join as pathJoin } from 'path';
|
|
import { pipeline } from 'stream/promises';
|
|
import { extract } from 'tar';
|
|
|
|
const DEFAULT_PORT = 3000,
|
|
LISTEN_ATTEMPTS = 10;
|
|
|
|
// Create directory for saving assets
|
|
const rand = Math.round(Math.random() * 100000000000000).toString(16),
|
|
dataDir = `/tmp/oxc_bench_data_${rand}`;
|
|
await fs.mkdir(dataDir);
|
|
|
|
let component = process.env.COMPONENT;
|
|
if (process.env.FIXTURE) component += process.env.FIXTURE;
|
|
|
|
const app = express();
|
|
|
|
const wrapHandler = fn => (req, res, next) => {
|
|
fn(req, res).catch(next);
|
|
};
|
|
const getFilePath = filename => pathJoin(dataDir, `${component}_${filename}`);
|
|
|
|
app.post(
|
|
'/upload',
|
|
wrapHandler(async (req, res) => {
|
|
const stream = createWriteStream(getFilePath('metadata.json'));
|
|
await pipeline(req, stream);
|
|
|
|
res.json({
|
|
status: 'success',
|
|
uploadUrl: `http://localhost:${port}/upload_archive`,
|
|
runId: 'dummy_value',
|
|
});
|
|
}),
|
|
);
|
|
|
|
app.put(
|
|
'/upload_archive',
|
|
wrapHandler(async (req, res) => {
|
|
// Stream uploaded tarball to file
|
|
const path = getFilePath('archive.tar.gz');
|
|
const stream = createWriteStream(path);
|
|
await pipeline(req, stream);
|
|
|
|
// Untar contents + delete tarball
|
|
await extract({ file: path, cwd: dataDir });
|
|
await fs.rm(path);
|
|
|
|
// Rename `.out` files + delete `.log` files
|
|
const filenames = await fs.readdir(dataDir);
|
|
for (const filename of filenames) {
|
|
if (filename.endsWith('.log')) {
|
|
await fs.rm(pathJoin(dataDir, filename));
|
|
} else if (filename.endsWith('.out')) {
|
|
await fs.rename(pathJoin(dataDir, filename), getFilePath(filename));
|
|
}
|
|
}
|
|
|
|
// Send response
|
|
res.send('');
|
|
server.close(() => {});
|
|
}),
|
|
);
|
|
|
|
// Open server on a port which is not already in use
|
|
let server,
|
|
port = DEFAULT_PORT;
|
|
for (let i = 0; i < LISTEN_ATTEMPTS; i++) {
|
|
console.log(`Starting server on port ${port}`);
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
server = app.listen(port, resolve);
|
|
server.on('error', reject);
|
|
});
|
|
break;
|
|
} catch (err) {
|
|
if (err?.code !== 'EADDRINUSE') throw err;
|
|
console.log(`Port ${port} in use. Trying again.`);
|
|
port = DEFAULT_PORT + Math.round(Math.random() * 5000);
|
|
}
|
|
}
|
|
console.log(`Server listening on port ${port}`);
|
|
|
|
// Output data dir path + port to env vars
|
|
await fs.appendFile(process.env.GITHUB_ENV, `DATA_DIR=${dataDir}\nINTERCEPT_PORT=${port}\n`);
|