fix(installer): include automator runtime from source-root
Co-authored-by: bmad <bmad.directory@gmail.com>
This commit is contained in:
parent
f1b9679232
commit
c792ef169d
|
|
@ -122,6 +122,27 @@ async function createAutomatorBmadFixture() {
|
||||||
return fixtureDir;
|
return fixtureDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createAutomatorSourceRootFixture() {
|
||||||
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-source-'));
|
||||||
|
const sourceRoot = path.join(repoRoot, 'payload', '.claude', 'skills');
|
||||||
|
|
||||||
|
for (const skillName of ['bmad-story-automator', 'bmad-story-automator-review']) {
|
||||||
|
const skillDir = path.join(sourceRoot, skillName);
|
||||||
|
await fs.ensureDir(skillDir);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(skillDir, 'SKILL.md'),
|
||||||
|
['---', `name: ${skillName}`, 'description: Automator skill', '---', '', 'Automator body.'].join('\n'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.ensureDir(path.join(repoRoot, 'source', 'scripts'));
|
||||||
|
await fs.writeFile(path.join(repoRoot, 'source', 'scripts', 'story-automator'), '#!/usr/bin/env bash\n');
|
||||||
|
await fs.ensureDir(path.join(repoRoot, 'source', 'src', 'story_automator'));
|
||||||
|
await fs.writeFile(path.join(repoRoot, 'source', 'src', 'story_automator', 'cli.py'), 'def main():\n return 0\n');
|
||||||
|
|
||||||
|
return { repoRoot, sourceRoot };
|
||||||
|
}
|
||||||
|
|
||||||
async function createSkillCollisionFixture() {
|
async function createSkillCollisionFixture() {
|
||||||
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-skill-collision-'));
|
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-skill-collision-'));
|
||||||
const fixtureDir = path.join(fixtureRoot, '_bmad');
|
const fixtureDir = path.join(fixtureRoot, '_bmad');
|
||||||
|
|
@ -3564,6 +3585,8 @@ async function runTests() {
|
||||||
|
|
||||||
let tempProjectDir42;
|
let tempProjectDir42;
|
||||||
let installedBmadDir42;
|
let installedBmadDir42;
|
||||||
|
let automatorSourceFixture42;
|
||||||
|
let runtimeTargetRoot42;
|
||||||
try {
|
try {
|
||||||
const externalManager42 = new ExternalModuleManager();
|
const externalManager42 = new ExternalModuleManager();
|
||||||
const automatorInfo42 = await externalManager42.getModuleByCode('bma');
|
const automatorInfo42 = await externalManager42.getModuleByCode('bma');
|
||||||
|
|
@ -3597,6 +3620,23 @@ async function runTests() {
|
||||||
'External module requirements normalize scalar array entries',
|
'External module requirements normalize scalar array entries',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
automatorSourceFixture42 = await createAutomatorSourceRootFixture();
|
||||||
|
runtimeTargetRoot42 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-runtime-target-'));
|
||||||
|
const runtimeBmadDir42 = path.join(runtimeTargetRoot42, '_bmad');
|
||||||
|
const officialModules42 = new OfficialModules();
|
||||||
|
officialModules42.findModuleSource = async () => automatorSourceFixture42.sourceRoot;
|
||||||
|
await officialModules42.install('bma', runtimeBmadDir42, null, { skipModuleInstaller: true, silent: true });
|
||||||
|
assert(
|
||||||
|
await fs.pathExists(path.join(runtimeBmadDir42, 'bma', 'bmad-story-automator', 'scripts', 'story-automator')),
|
||||||
|
'BMad Automator source-root install includes runtime helper',
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
await fs.pathExists(path.join(runtimeBmadDir42, 'bma', 'bmad-story-automator', 'src', 'story_automator', 'cli.py')),
|
||||||
|
'BMad Automator source-root install includes Python runtime source',
|
||||||
|
);
|
||||||
|
await fs.remove(runtimeTargetRoot42).catch(() => {});
|
||||||
|
runtimeTargetRoot42 = null;
|
||||||
|
|
||||||
tempProjectDir42 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-target-'));
|
tempProjectDir42 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-target-'));
|
||||||
installedBmadDir42 = await createAutomatorBmadFixture();
|
installedBmadDir42 = await createAutomatorBmadFixture();
|
||||||
|
|
||||||
|
|
@ -3663,6 +3703,8 @@ async function runTests() {
|
||||||
} finally {
|
} finally {
|
||||||
if (tempProjectDir42) await fs.remove(tempProjectDir42).catch(() => {});
|
if (tempProjectDir42) await fs.remove(tempProjectDir42).catch(() => {});
|
||||||
if (installedBmadDir42) await fs.remove(path.dirname(installedBmadDir42)).catch(() => {});
|
if (installedBmadDir42) await fs.remove(path.dirname(installedBmadDir42)).catch(() => {});
|
||||||
|
if (automatorSourceFixture42) await fs.remove(automatorSourceFixture42.repoRoot).catch(() => {});
|
||||||
|
if (runtimeTargetRoot42) await fs.remove(runtimeTargetRoot42).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,7 @@ class OfficialModules {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
|
await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
|
||||||
|
await this.copyAutomatorRuntimeIfNeeded(moduleName, sourcePath, targetPath, fileTrackingCallback);
|
||||||
|
|
||||||
if (!options.skipModuleInstaller) {
|
if (!options.skipModuleInstaller) {
|
||||||
await this.createModuleDirectories(moduleName, bmadDir, options);
|
await this.createModuleDirectories(moduleName, bmadDir, options);
|
||||||
|
|
@ -572,6 +573,29 @@ class OfficialModules {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async copyAutomatorRuntimeIfNeeded(moduleName, sourcePath, targetPath, fileTrackingCallback = null) {
|
||||||
|
if (moduleName !== 'bma') return;
|
||||||
|
|
||||||
|
const storyTarget = path.join(targetPath, 'bmad-story-automator');
|
||||||
|
if (!(await fs.pathExists(path.join(storyTarget, 'SKILL.md')))) return;
|
||||||
|
|
||||||
|
const repoRoot = path.resolve(sourcePath, '..', '..', '..');
|
||||||
|
const runtimeRoot = path.join(repoRoot, 'source');
|
||||||
|
const runtimeParts = [
|
||||||
|
['scripts', 'scripts'],
|
||||||
|
['src', 'src'],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const [sourceRel, targetRel] of runtimeParts) {
|
||||||
|
const sourceDir = path.join(runtimeRoot, sourceRel);
|
||||||
|
const targetDir = path.join(storyTarget, targetRel);
|
||||||
|
if (!(await fs.pathExists(sourceDir))) {
|
||||||
|
throw new Error(`BMad Automator runtime source missing: source/${sourceRel}`);
|
||||||
|
}
|
||||||
|
await this.copyModuleWithFiltering(sourceDir, targetDir, fileTrackingCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create directories declared in module.yaml's `directories` key
|
* Create directories declared in module.yaml's `directories` key
|
||||||
* This replaces the security-risky module installer pattern with declarative config
|
* This replaces the security-risky module installer pattern with declarative config
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue