Skip to content

Commit a003201

Browse files
authored
Validation script for examples that integrate rollbar locally (#1250)
1 parent 2a8d5bd commit a003201

File tree

5 files changed

+149
-2
lines changed

5 files changed

+149
-2
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ jobs:
5555

5656
- name: Run tests
5757
run: npm run test-ci
58+
59+
- name: Validate examples
60+
run: npm run validate:examples

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@
113113
"test-ci": "./node_modules/.bin/grunt test && npm run test-server",
114114
"lint": "./node_modules/.bin/eslint .",
115115
"pack": "node scripts/pack.js",
116+
"validate": "npm run validate:es5 && npm run validate:examples",
116117
"validate:es5": "es-check es5 './dist/**/*.js' --verbose",
118+
"validate:examples": "node scripts/validate-examples.js -p",
117119
"update:snippets:examples": "node scripts/update-snippets.js examples"
118120
}
119121
}

scripts/pack.js

100644100755
File mode changed.

scripts/util.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function findUp({ fileName, dir = process.cwd() }) {
4949
export async function npm(args, { cwd = process.cwd(), id = '' }) {
5050
const child = spawn('npm', args, {
5151
cwd,
52-
shell: true,
52+
shell: false,
5353
stdio: ['inherit', 'pipe', 'pipe'],
5454
});
5555

@@ -62,7 +62,9 @@ export async function npm(args, { cwd = process.cwd(), id = '' }) {
6262
const [code, signal] = await once(child, 'close');
6363

6464
if (code !== 0 || signal) {
65-
const msg = std.err.trim() || std.out.trim();
65+
const stderr = std.err.trim();
66+
const stdout = std.out.trim();
67+
const msg = `\nSTDERR:\n${stderr}\nSTDOUT:\n${stdout}`;
6668
throw new Error(
6769
`npm ` +
6870
`${args.join(' ')} ` +
@@ -74,3 +76,41 @@ export async function npm(args, { cwd = process.cwd(), id = '' }) {
7476

7577
return std.out;
7678
}
79+
80+
/**
81+
* Transforms each element in a given array in parallel with a specified
82+
* concurrency limit, preserving the original order.
83+
*
84+
* @param {Array} xs - Array of items to transform.
85+
* @param {Function} f - Async transform for each item.
86+
* @param {number} c - Maximum number of concurrent transforms.
87+
* @returns {Promise<Array>} The transformed array.
88+
* @example
89+
* const results = await parallelMap(files, async (file) => {
90+
* return await processFile(file);
91+
* }, 4);
92+
*/
93+
export async function parallelMap(xs, f, c) {
94+
if (c <= 0) {
95+
throw new Error('Concurrency limit must be greater than 0');
96+
}
97+
98+
const ys = new Array(xs.length);
99+
const jobs = new Set();
100+
101+
for (const [i, x] of xs.entries()) {
102+
const p = Promise.resolve()
103+
.then(() => f(x, i))
104+
.then((y) => (ys[i] = y));
105+
106+
p.finally(() => jobs.delete(p));
107+
jobs.add(p);
108+
109+
if (jobs.size >= c) {
110+
await Promise.race(jobs);
111+
}
112+
}
113+
114+
await Promise.all(jobs);
115+
return ys;
116+
}

scripts/validate-examples.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Validates all examples in the `examples` directory by installing dependencies
5+
* and building each example using the local `rollbar.tgz` package.
6+
*/
7+
8+
import { access, readdir, readFile } from 'node:fs/promises';
9+
import path from 'node:path';
10+
import { fileURLToPath } from 'node:url';
11+
import os from 'node:os';
12+
13+
import { findUp, npm, parallelMap } from './util.js';
14+
15+
const dryRun = ['--dry-run', '-n'].some((f) => process.argv.includes(f));
16+
const jobsCount = (() => {
17+
const i = process.argv.findIndex((a) => a === '--parallel' || a === '-p');
18+
const n = i < 0 ? 0 : parseInt(process.argv[i + 1], 10) || os.cpus().length;
19+
return Math.max(n, 1);
20+
})();
21+
22+
async function validateExample(exampleDir, dryRun = false) {
23+
const name = `examples/${path.basename(exampleDir)}`;
24+
25+
if (dryRun) {
26+
console.log(` - ${name} (dry run)`);
27+
return;
28+
}
29+
30+
await npm(['install'], { cwd: exampleDir, id: name });
31+
await npm(['run', 'build'], { cwd: exampleDir, id: name });
32+
console.log(` ✓ ${name}`);
33+
}
34+
35+
async function validateExamples() {
36+
console.log('Validating examples using the local rollbar package...');
37+
if (jobsCount > 1) {
38+
console.log(`Running with ${jobsCount} parallel jobs`);
39+
}
40+
console.log();
41+
42+
const cwd = path.dirname(fileURLToPath(import.meta.url));
43+
const root = await findUp({ fileName: 'package.json', dir: cwd });
44+
const examplesDir = path.join(root, 'examples');
45+
46+
try {
47+
await access(path.join(examplesDir, 'rollbar.tgz'));
48+
} catch {
49+
throw new Error(
50+
`No rollbar.tgz found in ${path.relative(root, examplesDir)}. ` +
51+
`Please build rollbar first.`,
52+
);
53+
}
54+
55+
const entries = await readdir(examplesDir, { withFileTypes: true });
56+
const subdirs = entries
57+
.filter((entry) => entry.isDirectory())
58+
.map((entry) => path.join(examplesDir, entry.name));
59+
60+
let exampleDirs = [];
61+
for (const subdir of subdirs) {
62+
let pkg;
63+
64+
try {
65+
pkg = await readFile(path.join(subdir, 'package.json'), 'utf8');
66+
} catch {
67+
continue;
68+
}
69+
70+
const { dependencies } = JSON.parse(pkg);
71+
if (dependencies?.rollbar === 'file:../rollbar.tgz') {
72+
exampleDirs.push(subdir);
73+
}
74+
}
75+
76+
if (exampleDirs.length === 0) {
77+
throw new Error('No examples found using the local rollbar package.');
78+
}
79+
80+
await parallelMap(
81+
exampleDirs,
82+
async (dir) => validateExample(dir, dryRun),
83+
jobsCount,
84+
);
85+
86+
console.log('Validation succeeded');
87+
}
88+
89+
validateExamples().catch((err) => {
90+
console.error('Error validating examples:', err);
91+
console.error(
92+
'\nUsage: validate-examples [--dry-run|-n] [--parallel|-p <n>]',
93+
);
94+
console.error(' --parallel | -p <n>: run <n> jobs in parallel');
95+
console.error(' if no <n> is given, defaults to cpu cores');
96+
console.error(' --dry-run | -n: do not run commands, just print');
97+
console.error('\nExamples:');
98+
console.error(' validate-examples --parallel 4');
99+
console.error(' validate-examples --dry-run');
100+
console.error(' validate-examples -n -p');
101+
process.exit(1);
102+
});

0 commit comments

Comments
 (0)