mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-24 16:32:24 +02:00
chore(continuous-learning-v2): standardize shell shebangs to env bash (#2401)
* chore(continuous-learning-v2): standardize shell shebangs to env bash Three scripts under skills/continuous-learning-v2/ used the hardcoded `#!/bin/bash` shebang while the other four already used the portable `#!/usr/bin/env bash`: - hooks/observe.sh (runs on every hook invocation) - scripts/detect-project.sh - agents/start-observer.sh The hardcoded interpreter path fails to execute on systems where bash is not installed at /bin/bash (NixOS, some Homebrew layouts, FreeBSD). Standardize all three to `#!/usr/bin/env bash`, matching the repo-wide majority convention, and add a regression test that asserts shebang uniformity for every shell script in this skill so the inconsistency cannot reappear. Fixes #2303 * test(continuous-learning-v2): harden shebang test runner Address review feedback on the shebang-consistency regression test: - firstLine() now splits on /\r?\n/ so a script checked out with CRLF line endings does not leave a trailing carriage return that would break the shebang comparison on Windows. - The test() helper now surfaces the full error (stack trace, not just the message) on failure and writes pass/fail lines via process.stdout/stderr so diagnostics are preserved. * test(continuous-learning-v2): skip hidden dirs in shebang scan The recursive shell-script scan now skips hidden directories (e.g. the observer's runtime `.observer-tmp`). This keeps the shebang-consistency check deterministic: only committed skill scripts are examined, and an untracked local artifact left over from an observer run can no longer cause a false failure.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Continuous Learning v2 - Observer Agent Launcher
|
||||
#
|
||||
# Starts the background observer agent that analyzes observations
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Continuous Learning v2 - Observation Hook
|
||||
#
|
||||
# Captures tool use events for pattern analysis.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Continuous Learning v2 - Project Detection Helper
|
||||
#
|
||||
# Shared logic for detecting current project context.
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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);
|
||||
Reference in New Issue
Block a user