feat: force sync skill trees
This commit is contained in:
+37
-4
@@ -20,6 +20,12 @@ export const SYNC_PATHS = [
|
||||
'CLAUDE.md',
|
||||
'GEMINI.md',
|
||||
];
|
||||
const SYNC_TREE_PATHS = [
|
||||
'.codex/skills/triage-findings',
|
||||
'.claude/skills/triage-findings',
|
||||
'.gemini/skills/triage-findings',
|
||||
'.github/skills/triage-findings',
|
||||
];
|
||||
|
||||
function makeRunner(spawn) {
|
||||
return function run(args, cwd, env) {
|
||||
@@ -51,6 +57,26 @@ function readGitOutput(run, args, cwd, env) {
|
||||
}
|
||||
}
|
||||
|
||||
function copyTree(sourceRoot, repoDir, relDir) {
|
||||
const srcDir = path.join(sourceRoot, relDir);
|
||||
if (!fs.existsSync(srcDir)) return [];
|
||||
|
||||
const copied = [];
|
||||
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
||||
const relPath = path.join(relDir, entry.name);
|
||||
const src = path.join(sourceRoot, relPath);
|
||||
const dest = path.join(repoDir, relPath);
|
||||
if (entry.isDirectory()) {
|
||||
copied.push(...copyTree(sourceRoot, repoDir, relPath));
|
||||
continue;
|
||||
}
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.copyFileSync(src, dest);
|
||||
copied.push(relPath);
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
export function getRepoState(repoDir, _spawnSync = spawnSync) {
|
||||
const run = makeRunner(_spawnSync);
|
||||
const headSha = readGitOutput(run, ['rev-parse', 'HEAD'], repoDir);
|
||||
@@ -101,21 +127,28 @@ export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync,
|
||||
run(['reset', '--hard', `origin/${PR_HEAD_BRANCH}`], repoDir);
|
||||
}
|
||||
|
||||
const existingSyncPaths = [];
|
||||
const existingSyncPaths = new Set();
|
||||
|
||||
// Copy action skill files into the target repo. Existing files are overwritten;
|
||||
// Copy action skill trees into the target repo. Existing files are overwritten;
|
||||
// missing source files are ignored so we do not delete target repo content.
|
||||
for (const relDir of SYNC_TREE_PATHS) {
|
||||
for (const relPath of copyTree(sourceRoot, repoDir, relDir)) {
|
||||
existingSyncPaths.add(relPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy standalone action files into the target repo. Existing files are overwritten.
|
||||
for (const relPath of SYNC_PATHS) {
|
||||
const src = path.join(sourceRoot, relPath);
|
||||
const dest = path.join(repoDir, relPath);
|
||||
if (fs.existsSync(src)) {
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.copyFileSync(src, dest);
|
||||
existingSyncPaths.push(relPath);
|
||||
existingSyncPaths.add(relPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingSyncPaths.length > 0) {
|
||||
if (existingSyncPaths.size > 0) {
|
||||
run(['add', ...existingSyncPaths], repoDir);
|
||||
}
|
||||
const generatedSyncPaths = GENERATED_SYNC_PATHS.filter(relPath => fs.existsSync(path.join(workspace, relPath)));
|
||||
|
||||
@@ -166,6 +166,21 @@ describe('commitAndPush', () => {
|
||||
assert.equal(fs.readFileSync(path.join(repoDir, 'CLAUDE.md'), 'utf8'), 'CLAUDE.md');
|
||||
});
|
||||
|
||||
it('recursively overwrites skill tree files from the action source', async () => {
|
||||
const repoDir = path.join(workspace, 'repo');
|
||||
const nestedRelPath = '.codex/skills/triage-findings/assets/example.txt';
|
||||
const sourceNestedPath = path.join(sourceRoot, nestedRelPath);
|
||||
const repoNestedPath = path.join(repoDir, nestedRelPath);
|
||||
fs.mkdirSync(path.dirname(sourceNestedPath), { recursive: true });
|
||||
fs.writeFileSync(sourceNestedPath, 'fresh');
|
||||
fs.mkdirSync(path.dirname(repoNestedPath), { recursive: true });
|
||||
fs.writeFileSync(repoNestedPath, 'stale');
|
||||
|
||||
await commitAndPush(workspace, repoDir, makeSpawn(), sourceRoot);
|
||||
|
||||
assert.equal(fs.readFileSync(repoNestedPath, 'utf8'), 'fresh');
|
||||
});
|
||||
|
||||
it('does not throw when git command fails', async () => {
|
||||
const failSpawn = () => ({ status: 1, stdout: '', stderr: 'fatal: error', error: null });
|
||||
await assert.doesNotReject(() => commitAndPush(workspace, path.join(workspace, 'repo'), failSpawn, sourceRoot));
|
||||
|
||||
Reference in New Issue
Block a user