fix(worktree): gate sessions on bootstrap readiness (#1762)
Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Leonid Skorobogatyy
Bohdan Triapitsyn
parent
03e6f789a4
commit
8c1a24089d
@@ -3550,6 +3550,19 @@ export async function validateWorktreeCreate(directory, input = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
const assertWorktreeCreatePreflight = async (directory, input = {}) => {
|
||||
const validation = await validateWorktreeCreate(directory, input);
|
||||
if (validation?.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = validation?.errors
|
||||
?.map((error) => error?.message)
|
||||
.filter(Boolean)
|
||||
.join('\n') || 'Failed to validate worktree creation';
|
||||
throw new Error(message);
|
||||
};
|
||||
|
||||
export async function previewWorktreeCreate(directory, input = {}) {
|
||||
const mode = input?.mode === 'existing' ? 'existing' : 'new';
|
||||
const context = await resolveWorktreeProjectContext(directory);
|
||||
@@ -3698,6 +3711,11 @@ async function attachGitWorktreeToCandidate(context, candidate, input = {}) {
|
||||
export async function createWorktree(directory, input = {}) {
|
||||
const mode = input?.mode === 'existing' ? 'existing' : 'new';
|
||||
const context = await resolveWorktreeProjectContext(directory);
|
||||
|
||||
if (input?.returnAfterDirectoryCreated === true) {
|
||||
await assertWorktreeCreatePreflight(directory, input);
|
||||
}
|
||||
|
||||
await fsp.mkdir(context.worktreeRoot, { recursive: true });
|
||||
|
||||
const preferredName = String(input?.worktreeName || input?.name || '').trim();
|
||||
|
||||
@@ -8,6 +8,7 @@ import simpleGit from 'simple-git';
|
||||
import {
|
||||
checkoutCommit,
|
||||
cherryPick,
|
||||
createWorktree,
|
||||
getStatus,
|
||||
removeWorktree,
|
||||
resolvePrimaryWorktreeRoot,
|
||||
@@ -315,6 +316,53 @@ describe('worktree root resolution', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// createWorktree
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('createWorktree', () => {
|
||||
it('preflights fast create branch-in-use failures before creating the candidate directory', async () => {
|
||||
if (!canRunGit()) return;
|
||||
|
||||
const previousXdgDataHome = process.env.XDG_DATA_HOME;
|
||||
const dataHome = createTempDir();
|
||||
process.env.XDG_DATA_HOME = dataHome;
|
||||
|
||||
try {
|
||||
const repo = createTempDir();
|
||||
const worktree = createTempDir();
|
||||
runGit(repo, ['init', '-b', 'main']);
|
||||
runGit(repo, ['config', 'user.email', 'test@example.com']);
|
||||
runGit(repo, ['config', 'user.name', 'Test User']);
|
||||
fs.writeFileSync(path.join(repo, 'README.md'), '# Test\n');
|
||||
runGit(repo, ['add', 'README.md']);
|
||||
runGit(repo, ['commit', '-m', 'Initial commit']);
|
||||
const projectID = runGit(repo, ['rev-list', '--max-parents=0', '--all']).trim();
|
||||
|
||||
fs.rmSync(worktree, { recursive: true, force: true });
|
||||
runGit(repo, ['worktree', 'add', '-b', 'feature/in-use', worktree, 'HEAD']);
|
||||
const canonicalWorktree = fs.realpathSync(worktree);
|
||||
|
||||
await expect(createWorktree(repo, {
|
||||
mode: 'existing',
|
||||
existingBranch: 'feature/in-use',
|
||||
branchName: 'feature/in-use',
|
||||
worktreeName: 'feature-in-use',
|
||||
returnAfterDirectoryCreated: true,
|
||||
})).rejects.toThrow(`Branch is already checked out in ${canonicalWorktree}`);
|
||||
|
||||
const candidateDirectory = path.join(dataHome, 'opencode', 'worktree', projectID, 'feature-in-use');
|
||||
expect(fs.existsSync(candidateDirectory)).toBe(false);
|
||||
} finally {
|
||||
if (previousXdgDataHome === undefined) {
|
||||
delete process.env.XDG_DATA_HOME;
|
||||
} else {
|
||||
process.env.XDG_DATA_HOME = previousXdgDataHome;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// removeWorktree
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user