mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-11 02:26:10 +00:00
fix(opencode): never report repair success while the pre-fix stub is retained
This commit is contained in:
2 files changed
+24
-3
No files matched your search
@@ -447,9 +447,11 @@ function openCodeActivationCandidates(plan, previousOperations) {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function activationIsInactive(kind, operation, content) {
|
||||
function activationIsInactive(kind, operation, content, strict = false) {
|
||||
if (kind === 'plugin') {
|
||||
return isInertOpenCodePluginStub(content);
|
||||
// The final gate accepts only the V2-valid stub: the pre-fix one is inert but OpenCode rejects it.
|
||||
return strict ? content.toString('utf8') === getDisabledOpenCodePluginContent()
|
||||
: isInertOpenCodePluginStub(content);
|
||||
}
|
||||
const text = content.toString('utf8');
|
||||
const relativePath = operation.sourceRelativePath || operation.destinationPath;
|
||||
@@ -505,7 +507,7 @@ function assertOpenCodeHookDeactivationReady(plan, options = {}) {
|
||||
const digest = content === null ? null : crypto.createHash('sha256').update(content).digest('hex');
|
||||
snapshot.set(key, digest);
|
||||
if (content === null) continue;
|
||||
const inactive = activationIsInactive(kind, operation, content);
|
||||
const inactive = activationIsInactive(kind, operation, content, options.requireInactive);
|
||||
if (options.requireInactive) {
|
||||
if (!inactive) {
|
||||
throw new Error(`OpenCode hook activation remains active at ${operation.destinationPath}`);
|
||||
|
||||
@@ -347,6 +347,25 @@ function runTests() {
|
||||
assert.strictEqual(fs.readFileSync(path.join(value.canonicalRoot, 'plugins/ecc-hooks.ts'), 'utf8'),
|
||||
getDisabledOpenCodePluginContent());
|
||||
});
|
||||
test('a retained pre-fix declined factory stub never reports a successful repair', value => {
|
||||
const state = addLegacyActivations(value, 'declined');
|
||||
const oldStub = 'export default async () => ({});\n';
|
||||
const stubPath = path.join(value.legacyRoot, 'plugins/ecc-hooks.ts');
|
||||
fs.writeFileSync(stubPath, oldStub);
|
||||
state.operations.find(operation => operation.destinationPath === stubPath).contentSha256 = sha256(oldStub);
|
||||
writeInstallState(value.legacyStatePath, state);
|
||||
const originalRename = fileSystem.renameSync;
|
||||
fileSystem.renameSync = (from, ...args) => {
|
||||
if (from === stubPath) throw Object.assign(new Error('Synthetic removal denial'), { code: 'EACCES' });
|
||||
return originalRename(from, ...args);
|
||||
};
|
||||
try {
|
||||
const result = repair(value, buildInertPayload);
|
||||
assert.strictEqual(result.results[0].status, 'error', JSON.stringify(result));
|
||||
assert.strictEqual(fs.readFileSync(stubPath, 'utf8'), oldStub);
|
||||
} finally { fileSystem.renameSync = originalRename; }
|
||||
assertBothLocksReleased(value);
|
||||
});
|
||||
for (const defect of ['modified', 'missing-digest', 'source-mismatch', 'source-missing', 'symlink', 'unrecorded-alias', 'merge-json']) {
|
||||
test(`legacy ${defect} activation refuses before build and preserves ownership`, value => {
|
||||
const state = addLegacyActivations(value);
|
||||
|
||||
Reference in new issue
Block a user