@namewta/speculo 2.0.3 → 2.0.4
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/package.json
CHANGED
|
@@ -287,11 +287,11 @@ function repositoryRoot(path) {
|
|
|
287
287
|
if (git(path, ['rev-parse', '--is-inside-work-tree'], { allowed: [0, 128] }).stdout.trim() !== 'true') {
|
|
288
288
|
throw new SquashError('repository path is not a Git worktree');
|
|
289
289
|
}
|
|
290
|
-
return realpathSync(git(path, ['rev-parse', '--show-toplevel']).stdout.trim());
|
|
290
|
+
return realpathSync.native(git(path, ['rev-parse', '--show-toplevel']).stdout.trim());
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
function portable(root, path, externalIndex) {
|
|
294
|
-
const rel = relative(realpathSync(root), realpathSync(path));
|
|
294
|
+
const rel = relative(realpathSync.native(root), realpathSync.native(path));
|
|
295
295
|
if (!isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${sep}`)) return rel ? rel.split(sep).join('/') : '.';
|
|
296
296
|
return `<external-worktree:${externalIndex}>`;
|
|
297
297
|
}
|
|
@@ -450,7 +450,7 @@ function inspectRemote(repo, request, oldHead) {
|
|
|
450
450
|
function inspectRepository(projectRoot, request) {
|
|
451
451
|
const repo = inside(projectRoot, request.path, `${request.id}.path`, { allowRoot: true });
|
|
452
452
|
ensureDirectory(repo, `${request.id}.path`);
|
|
453
|
-
if (repositoryRoot(repo)
|
|
453
|
+
if (relative(repositoryRoot(repo), realpathSync.native(repo)) !== '') throw new SquashError(`${request.id}.path must be the repository toplevel`);
|
|
454
454
|
validateRef(repo, request.branch, `${request.id}.branch`);
|
|
455
455
|
git(repo, ['update-ref', '--stdin'], { input: 'start\nabort\n', env: { GIT_OPTIONAL_LOCKS: '0' } });
|
|
456
456
|
git(repo, ['var', 'GIT_AUTHOR_IDENT'], { env: { GIT_OPTIONAL_LOCKS: '0' } });
|
|
@@ -765,7 +765,7 @@ function validateRunState(projectRoot, state) {
|
|
|
765
765
|
safePath(repo.path, `${repo.id}.path`);
|
|
766
766
|
const path = inside(projectRoot, repo.path, `${repo.id}.path`, { allowRoot: true, mustExist: true });
|
|
767
767
|
ensureDirectory(path, `${repo.id}.path`);
|
|
768
|
-
if (repositoryRoot(path)
|
|
768
|
+
if (relative(repositoryRoot(path), realpathSync.native(path)) !== '') throw new SquashError(`${repo.id}.path must remain a repository toplevel`);
|
|
769
769
|
validateRef(path, repo.branch, `${repo.id}.branch`);
|
|
770
770
|
validateOid(repo.start_sha, `${repo.id}.start_sha`);
|
|
771
771
|
validateOid(repo.end_sha, `${repo.id}.end_sha`);
|
|
@@ -43,8 +43,10 @@ function workingFile(root, path) {
|
|
|
43
43
|
|
|
44
44
|
function workingTree(options) {
|
|
45
45
|
if (['head','commit','parent'].some(key => options[key] !== undefined)) throw new Error('review-scope: working-tree accepts base, not head/commit/parent');
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Native realpath expands Windows short names; relative also handles drive
|
|
47
|
+
// casing without admitting a nested directory that borrows its parent's Git.
|
|
48
|
+
const root = realpathSync.native(resolve(options.repo));
|
|
49
|
+
if (relative(root,realpathSync.native(git(root, ['rev-parse','--show-toplevel']).trim())) !== '') throw new Error('review-scope: working-tree requires the actual project Git root');
|
|
48
50
|
const base = commit(root, options.base, 'base');
|
|
49
51
|
const files = options.files?.map(portableFile);
|
|
50
52
|
if (files && (!files.length || new Set(files).size !== files.length)) throw new Error('review-scope: empty or duplicate working-tree scope');
|
|
@@ -11,6 +11,7 @@ function fixture(t) {
|
|
|
11
11
|
t.after(()=>rmSync(root,{recursive:true,force:true}));
|
|
12
12
|
const git=(...args)=>execFileSync('git',['-C',root,...args],{encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();
|
|
13
13
|
git('init','-b','main');git('config','user.name','Fixture');git('config','user.email','test@example.invalid');
|
|
14
|
+
git('config','core.autocrlf','false');
|
|
14
15
|
for(const path of ['one.txt','removed.txt','renamed.txt']) writeFileSync(join(root,path),'original\n');
|
|
15
16
|
writeFileSync(join(root,'.gitignore'),'ignored/\n');git('add','.');git('-c','commit.gpgsign=false','commit','-m','base');
|
|
16
17
|
const base=git('rev-parse','HEAD');
|
|
@@ -26,14 +27,14 @@ test('working-tree freezes staged, unstaged, added, deleted, renamed and mode ch
|
|
|
26
27
|
const scope=f.scope();
|
|
27
28
|
assert.equal(scope.outcome,'changes');assert.equal(scope.observed_head,f.base);assert.equal(scope.head_sha,undefined);
|
|
28
29
|
assert.deepEqual(scope.changed_paths,['new-name.txt','new.bin','one.txt','removed.txt','renamed.txt']);
|
|
29
|
-
assert.equal(scope.working_snapshot.find(x=>x.path==='one.txt').after.mode,'100755');
|
|
30
|
+
assert.equal(scope.working_snapshot.find(x=>x.path==='one.txt').after.mode,process.platform === 'win32' ? '100644' : '100755');
|
|
30
31
|
assert.equal(scope.working_snapshot.find(x=>x.path==='removed.txt').after,null);
|
|
31
32
|
assert.equal(scope.working_snapshot.find(x=>x.path==='new.bin').after.bytes,3);
|
|
32
33
|
assert.deepEqual(f.scope(),scope);assert.equal(f.git('status','--porcelain'),status);assert.deepEqual(readFileSync(join(f.root,'.git/index')),before);
|
|
33
34
|
writeFileSync(join(f.root,'new.bin'),Buffer.from([0,255,8]));assert.notEqual(f.scope().input_sha256,scope.input_sha256);
|
|
34
35
|
});
|
|
35
36
|
test('explicit scopes are literal, expose exclusions and retain deleted files',t=>{
|
|
36
|
-
const f=fixture(t), path='literal [
|
|
37
|
+
const f=fixture(t), path='literal [abc] $(touch unsafe).txt';
|
|
37
38
|
writeFileSync(join(f.root,path),'literal');rmSync(join(f.root,'removed.txt'));writeFileSync(join(f.root,'one.txt'),'changed');
|
|
38
39
|
const scope=f.scope({files:[path,'removed.txt']});
|
|
39
40
|
assert.deepEqual(scope.changed_paths,[path,'removed.txt']);assert.deepEqual(scope.excluded_paths,['one.txt']);
|
|
@@ -47,7 +48,7 @@ test('links are read as links and directory links never disclose outside bytes',
|
|
|
47
48
|
t.after(()=>rmSync(outside,{recursive:true,force:true}));writeFileSync(join(outside,'secret'),'sensitive');
|
|
48
49
|
symlinkSync(join(outside,'secret'),join(f.root,'link'));
|
|
49
50
|
const scope=f.scope({files:['link']});assert.equal(scope.working_snapshot[0].after.kind,'symlink');
|
|
50
|
-
symlinkSync(outside,join(f.root,'directory'));
|
|
51
|
+
symlinkSync(outside,join(f.root,'directory'),process.platform === 'win32' ? 'junction' : 'dir');
|
|
51
52
|
assert.throws(()=>f.scope({files:['directory/secret']}),/symlink directory/);
|
|
52
53
|
});
|
|
53
54
|
test('unmerged index and borrowed parent repository are rejected',t=>{
|