claude-dev-env 8.35.1 → 8.35.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/install.mjs +10 -5
- package/bin/install.transaction.test.mjs +43 -0
- package/package.json +1 -1
package/bin/install.mjs
CHANGED
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
} from './resolve-package-managed-directory.mjs';
|
|
77
77
|
import {
|
|
78
78
|
ensureDirectoryPointer,
|
|
79
|
+
POINTER_ACTION_CREATED,
|
|
79
80
|
} from './publish-directory-pointer.mjs';
|
|
80
81
|
import {
|
|
81
82
|
parseInstallTargetSelectionFromArgv,
|
|
@@ -872,7 +873,11 @@ function owningManagedRoot(installedFilePath) {
|
|
|
872
873
|
* so those two paths are pointers. A real directory from an older install at a
|
|
873
874
|
* lookup path is merged into the agents home before the pointer is written.
|
|
874
875
|
*
|
|
875
|
-
*
|
|
876
|
+
* A pointer that already aimed at its target is left out of the result, so a
|
|
877
|
+
* rollback that removes this run's written paths keeps the pointer a prior
|
|
878
|
+
* install published.
|
|
879
|
+
*
|
|
880
|
+
* @returns {string[]} The lookup paths this run created.
|
|
876
881
|
*/
|
|
877
882
|
function publishManagedLookupPointers() {
|
|
878
883
|
const pointerPairs = [
|
|
@@ -897,10 +902,10 @@ function publishManagedLookupPointers() {
|
|
|
897
902
|
INSTALL_ROOT_RESOLUTION.hooksInstallDirectory,
|
|
898
903
|
],
|
|
899
904
|
];
|
|
900
|
-
|
|
901
|
-
ensureDirectoryPointer(pointerPath, targetPath)
|
|
902
|
-
|
|
903
|
-
|
|
905
|
+
return pointerPairs
|
|
906
|
+
.map(([pointerPath, targetPath]) => ensureDirectoryPointer(pointerPath, targetPath))
|
|
907
|
+
.filter((pointerResult) => pointerResult.action === POINTER_ACTION_CREATED)
|
|
908
|
+
.map((pointerResult) => pointerResult.pointerPath);
|
|
904
909
|
}
|
|
905
910
|
|
|
906
911
|
/**
|
|
@@ -6,9 +6,11 @@ import { test } from 'node:test';
|
|
|
6
6
|
import { strict as assert } from 'node:assert';
|
|
7
7
|
import {
|
|
8
8
|
existsSync,
|
|
9
|
+
lstatSync,
|
|
9
10
|
mkdtempSync,
|
|
10
11
|
mkdirSync,
|
|
11
12
|
readFileSync,
|
|
13
|
+
realpathSync,
|
|
12
14
|
rmSync,
|
|
13
15
|
writeFileSync,
|
|
14
16
|
} from 'node:fs';
|
|
@@ -501,3 +503,44 @@ test('successful install after prior install leaves one manifest and no fault',
|
|
|
501
503
|
rmSync(homeDirectory, { recursive: true, force: true });
|
|
502
504
|
}
|
|
503
505
|
});
|
|
506
|
+
|
|
507
|
+
test('installer fault keeps the lookup pointers a prior install published', () => {
|
|
508
|
+
const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-txn-e2e-pointers-'));
|
|
509
|
+
try {
|
|
510
|
+
writeFileSync(join(homeDirectory, '.gitconfig'), '');
|
|
511
|
+
const homeOnlyEnvironment = { CLAUDE_CONFIG_DIR: '' };
|
|
512
|
+
const okRun = runInstaller(homeDirectory, ['--only', 'core'], {
|
|
513
|
+
environment: homeOnlyEnvironment,
|
|
514
|
+
});
|
|
515
|
+
assert.equal(okRun.status, 0, okRun.stdout + okRun.stderr);
|
|
516
|
+
const allPointerTargets = [
|
|
517
|
+
...['skills', 'agents', 'hooks', 'scripts'].map((name) => ({
|
|
518
|
+
pointerPath: join(homeDirectory, '.claude', name),
|
|
519
|
+
targetPath: realpathSync(join(homeDirectory, '.agents', name)),
|
|
520
|
+
})),
|
|
521
|
+
{
|
|
522
|
+
pointerPath: join(homeDirectory, '.codex', 'hooks'),
|
|
523
|
+
targetPath: realpathSync(join(homeDirectory, '.agents', 'hooks')),
|
|
524
|
+
},
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
for (const faultPhase of [FAULT_PHASES.AFTER_FILE_STAGING, FAULT_PHASES.AFTER_MANIFEST_WRITE]) {
|
|
528
|
+
const failedRun = runInstaller(homeDirectory, ['--only', 'core'], {
|
|
529
|
+
faultPhase,
|
|
530
|
+
environment: homeOnlyEnvironment,
|
|
531
|
+
});
|
|
532
|
+
assert.notEqual(failedRun.status, 0, failedRun.stdout + failedRun.stderr);
|
|
533
|
+
assert.match(`${failedRun.stdout}${failedRun.stderr}`, /prior installation restored/);
|
|
534
|
+
for (const { pointerPath, targetPath } of allPointerTargets) {
|
|
535
|
+
assert.equal(
|
|
536
|
+
lstatSync(pointerPath, { throwIfNoEntry: false })?.isSymbolicLink(),
|
|
537
|
+
true,
|
|
538
|
+
`${pointerPath} survives a fault at ${faultPhase}`,
|
|
539
|
+
);
|
|
540
|
+
assert.equal(realpathSync(pointerPath), targetPath);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} finally {
|
|
544
|
+
rmSync(homeDirectory, { recursive: true, force: true });
|
|
545
|
+
}
|
|
546
|
+
});
|