Files
affaan-m--everything-claude…/tests/hooks/continuous-learning-shebang-consistency.test.js
T
wehub-resource-sync d48cda4081
CI / Test (ubuntu-latest, Node 18.x, bun) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, npm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, pnpm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, yarn) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 20.x, bun) (push) Failing after 17m13s
CI / Test (ubuntu-latest, Node 20.x, npm) (push) Failing after 18m42s
CI / Test (ubuntu-latest, Node 20.x, pnpm) (push) Failing after 15m0s
CI / Test (ubuntu-latest, Node 20.x, yarn) (push) Failing after 49m44s
CI / Test (ubuntu-latest, Node 22.x, bun) (push) Failing after 51m55s
CI / Test (ubuntu-latest, Node 22.x, pnpm) (push) Failing after 21m57s
CI / Test (ubuntu-latest, Node 22.x, npm) (push) Failing after 37m39s
CI / Test (ubuntu-latest, Node 22.x, yarn) (push) Failing after 34m7s
CI / Validate Components (push) Failing after 37m15s
CI / Python Tests (push) Failing after 10m1s
CI / Security Scan (push) Failing after 10m1s
CI / Lint (push) Failing after 17m12s
CI / Coverage (push) Failing after 20m19s
CI / Test (macos-latest, Node 18.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, yarn) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:55:55 +08:00

102 lines
3.3 KiB
JavaScript

/**
* Tests for shebang consistency across continuous-learning-v2 shell scripts
*
* Every `*.sh` script under skills/continuous-learning-v2/ must use the
* portable `#!/usr/bin/env bash` shebang rather than the hardcoded
* `#!/bin/bash`. The hardcoded interpreter path fails on systems where bash
* is not installed at /bin/bash (NixOS, some Homebrew layouts, FreeBSD), and
* observe.sh runs on every hook invocation. This guards against the
* inconsistency (#2303) reappearing.
*
* Run with: node tests/hooks/continuous-learning-shebang-consistency.test.js
*/
'use strict';
const assert = require('assert');
const path = require('path');
const fs = require('fs');
let passed = 0;
let failed = 0;
function test(name, fn) {
try {
fn();
process.stdout.write(` ✓ ${name}\n`);
passed++;
} catch (err) {
process.stderr.write(` ✗ ${name}\n`);
process.stderr.write(` ${err && err.stack ? err.stack : String(err)}\n`);
failed++;
}
}
const repoRoot = path.resolve(__dirname, '..', '..');
const skillDir = path.join(repoRoot, 'skills', 'continuous-learning-v2');
const PORTABLE_SHEBANG = '#!/usr/bin/env bash';
const HARDCODED_SHEBANG = '#!/bin/bash';
function collectShellScripts(dir, acc = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
// Skip hidden/runtime directories (e.g. the observer's `.observer-tmp`)
// so an untracked local artifact cannot trigger a false failure; only
// committed skill scripts are checked.
if (entry.name.startsWith('.')) {
continue;
}
collectShellScripts(fullPath, acc);
} else if (entry.isFile() && entry.name.endsWith('.sh')) {
acc.push(fullPath);
}
}
return acc;
}
function firstLine(filePath) {
return fs.readFileSync(filePath, 'utf8').split(/\r?\n/, 1)[0];
}
console.log('\n=== continuous-learning-v2 shebang consistency ===\n');
const scripts = collectShellScripts(skillDir);
test('skill directory contains shell scripts to check', () => {
assert.ok(scripts.length > 0, `expected at least one .sh under ${skillDir}`);
});
for (const scriptPath of scripts) {
const rel = path.relative(repoRoot, scriptPath).split(path.sep).join('/');
test(`${rel} uses portable '#!/usr/bin/env bash'`, () => {
assert.strictEqual(
firstLine(scriptPath),
PORTABLE_SHEBANG,
`${rel} should start with '${PORTABLE_SHEBANG}'`
);
});
}
test('no continuous-learning-v2 script uses hardcoded #!/bin/bash', () => {
const offenders = scripts
.filter(scriptPath => firstLine(scriptPath) === HARDCODED_SHEBANG)
.map(scriptPath => path.relative(repoRoot, scriptPath).split(path.sep).join('/'));
assert.strictEqual(
offenders.length,
0,
`hardcoded #!/bin/bash found in: ${offenders.join(', ')}`
);
});
// ──────────────────────────────────────────────────────
// Summary
// ──────────────────────────────────────────────────────
console.log('\n=== Test Results ===');
console.log(`Passed: ${passed}`);
console.log(`Failed: ${failed}`);
console.log(`Total: ${passed + failed}\n`);
process.exit(failed > 0 ? 1 : 0);