diff --git a/scripts/sync-ecc-to-codex.sh b/scripts/sync-ecc-to-codex.sh index e85a689cf..1e157bad1 100755 --- a/scripts/sync-ecc-to-codex.sh +++ b/scripts/sync-ecc-to-codex.sh @@ -208,9 +208,6 @@ if [[ "$MODE" == "apply" ]]; then --previous-hooks-path "$previous_hooks_path" \ --installed-hooks-path "${ECC_GLOBAL_HOOKS_DIR:-$CODEX_HOME/git-hooks}" )" - record_managed_path "$CONFIG_FILE" - record_managed_path "$AGENTS_FILE" - rollback_legacy_sync() { local exit_status="${1:-1}" trap - ERR INT TERM @@ -223,6 +220,9 @@ if [[ "$MODE" == "apply" ]]; then trap 'rollback_legacy_sync $?' ERR trap 'rollback_legacy_sync 130' INT trap 'rollback_legacy_sync 143' TERM + + record_managed_path "$CONFIG_FILE" + record_managed_path "$AGENTS_FILE" fi ECC_BEGIN_MARKER="" diff --git a/tests/ci/packed-artifact-lifecycle.js b/tests/ci/packed-artifact-lifecycle.js index 25d841d32..1eac64ec0 100644 --- a/tests/ci/packed-artifact-lifecycle.js +++ b/tests/ci/packed-artifact-lifecycle.js @@ -419,7 +419,18 @@ function runLifecycle(options) { ], }; } finally { - fs.rmSync(tempRoot, { recursive: true, force: true }); + try { + fs.rmSync(tempRoot, { + recursive: true, + force: true, + maxRetries: 10, + retryDelay: 100, + }); + } catch (cleanupError) { + process.stderr.write( + `Could not remove lifecycle temp root ${tempRoot}: ${cleanupError.message}\n` + ); + } } } diff --git a/tests/ci/packed-artifact-lifecycle.test.js b/tests/ci/packed-artifact-lifecycle.test.js index 994555be7..b6569afae 100644 --- a/tests/ci/packed-artifact-lifecycle.test.js +++ b/tests/ci/packed-artifact-lifecycle.test.js @@ -125,6 +125,16 @@ test('public CLI invocations use npm exec instead of internal package paths', () assert.ok(!unixInvocation.args.some(argument => argument.includes('node_modules'))); }); +test('lifecycle cleanup retries Windows file locks without masking results', () => { + const source = fs.readFileSync( + path.join(__dirname, 'packed-artifact-lifecycle.js'), + 'utf8' + ); + assert.match(source, /maxRetries:\s*10/); + assert.match(source, /retryDelay:\s*100/); + assert.match(source, /Could not remove lifecycle temp root/); +}); + console.log(`\nPassed: ${passed}`); console.log(`Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); diff --git a/tests/scripts/sync-ecc-to-codex.test.js b/tests/scripts/sync-ecc-to-codex.test.js index 4ef47d6f5..a53f61529 100644 --- a/tests/scripts/sync-ecc-to-codex.test.js +++ b/tests/scripts/sync-ecc-to-codex.test.js @@ -119,6 +119,11 @@ function runTests() { assert.match(source, /^set -Eeuo pipefail$/m); assert.ok(source.includes("trap 'rollback_legacy_sync $?' ERR")); assert.ok(source.includes('node "$LEGACY_STATE_HELPER" rollback --state "$LEGACY_STATE_PATH"')); + assert.ok( + source.indexOf("trap 'rollback_legacy_sync $?' ERR") + < source.indexOf('record_managed_path "$CONFIG_FILE"'), + 'rollback trap must be active before the first ownership record' + ); })) passed++; else failed++; console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);