@lumenflow/cli 2.1.1 → 2.1.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.
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file guard-main-branch.test.ts
|
|
3
|
+
* @description Tests for guard-main-branch worktree context detection (WU-1130)
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { guardMainBranch } from '../guard-main-branch.js';
|
|
7
|
+
// Mock the core module
|
|
8
|
+
vi.mock('@lumenflow/core', () => ({
|
|
9
|
+
createGitForPath: vi.fn(),
|
|
10
|
+
getGitForCwd: vi.fn(),
|
|
11
|
+
isAgentBranch: vi.fn().mockResolvedValue(false),
|
|
12
|
+
getConfig: vi.fn().mockReturnValue({
|
|
13
|
+
git: {
|
|
14
|
+
mainBranch: 'main',
|
|
15
|
+
laneBranchPrefix: 'lane/',
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
19
|
+
// Mock the worktree-guard module
|
|
20
|
+
vi.mock('@lumenflow/core/dist/core/worktree-guard.js', () => ({
|
|
21
|
+
isInWorktree: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
import { getGitForCwd, createGitForPath } from '@lumenflow/core';
|
|
24
|
+
import { isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
|
|
25
|
+
describe('guard-main-branch (WU-1130)', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
});
|
|
32
|
+
describe('lane branch worktree detection', () => {
|
|
33
|
+
it('should allow operations when on lane branch AND in worktree', async () => {
|
|
34
|
+
// Setup: On lane branch, in worktree
|
|
35
|
+
const mockGit = {
|
|
36
|
+
getCurrentBranch: vi.fn().mockResolvedValue('lane/framework-cli/wu-1130'),
|
|
37
|
+
};
|
|
38
|
+
vi.mocked(getGitForCwd).mockReturnValue(mockGit);
|
|
39
|
+
vi.mocked(isInWorktree).mockReturnValue(true);
|
|
40
|
+
const result = await guardMainBranch({});
|
|
41
|
+
expect(result.success).toBe(true);
|
|
42
|
+
expect(result.isProtected).toBe(false);
|
|
43
|
+
expect(result.currentBranch).toBe('lane/framework-cli/wu-1130');
|
|
44
|
+
});
|
|
45
|
+
it('should block operations when on lane branch but NOT in worktree', async () => {
|
|
46
|
+
// Setup: On lane branch, but not in worktree (e.g., checked out directly)
|
|
47
|
+
const mockGit = {
|
|
48
|
+
getCurrentBranch: vi.fn().mockResolvedValue('lane/framework-cli/wu-1130'),
|
|
49
|
+
};
|
|
50
|
+
vi.mocked(getGitForCwd).mockReturnValue(mockGit);
|
|
51
|
+
vi.mocked(isInWorktree).mockReturnValue(false);
|
|
52
|
+
const result = await guardMainBranch({});
|
|
53
|
+
expect(result.success).toBe(true);
|
|
54
|
+
expect(result.isProtected).toBe(true);
|
|
55
|
+
expect(result.reason).toContain('requires worktree');
|
|
56
|
+
});
|
|
57
|
+
it('should use baseDir for worktree detection when provided', async () => {
|
|
58
|
+
const mockGit = {
|
|
59
|
+
getCurrentBranch: vi.fn().mockResolvedValue('lane/ops-tooling/wu-2725'),
|
|
60
|
+
};
|
|
61
|
+
vi.mocked(createGitForPath).mockReturnValue(mockGit);
|
|
62
|
+
vi.mocked(isInWorktree).mockReturnValue(true);
|
|
63
|
+
const result = await guardMainBranch({ baseDir: '/path/to/worktrees/ops-tooling-wu-2725' });
|
|
64
|
+
expect(isInWorktree).toHaveBeenCalledWith({ cwd: '/path/to/worktrees/ops-tooling-wu-2725' });
|
|
65
|
+
expect(result.isProtected).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('main branch protection', () => {
|
|
69
|
+
it('should block operations on main branch', async () => {
|
|
70
|
+
const mockGit = {
|
|
71
|
+
getCurrentBranch: vi.fn().mockResolvedValue('main'),
|
|
72
|
+
};
|
|
73
|
+
vi.mocked(getGitForCwd).mockReturnValue(mockGit);
|
|
74
|
+
const result = await guardMainBranch({});
|
|
75
|
+
expect(result.isProtected).toBe(true);
|
|
76
|
+
expect(result.reason).toContain("'main' is protected");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* WU-1109: INIT-003 Phase 4b - Migrate git operations
|
|
14
14
|
*/
|
|
15
15
|
import { createGitForPath, getGitForCwd, isAgentBranch, getConfig } from '@lumenflow/core';
|
|
16
|
+
import { isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
|
|
16
17
|
/**
|
|
17
18
|
* Parse command line arguments for guard-main-branch
|
|
18
19
|
*/
|
|
@@ -90,6 +91,16 @@ export async function guardMainBranch(args) {
|
|
|
90
91
|
}
|
|
91
92
|
// Check if on a lane branch (requires worktree discipline)
|
|
92
93
|
if (isLaneBranch(currentBranch)) {
|
|
94
|
+
// If we're actually in a worktree, allow the operation (WU-1130)
|
|
95
|
+
const cwd = args.baseDir ?? process.cwd();
|
|
96
|
+
if (isInWorktree({ cwd })) {
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
isProtected: false,
|
|
100
|
+
currentBranch,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// On lane branch but not in worktree - block
|
|
93
104
|
return {
|
|
94
105
|
success: true,
|
|
95
106
|
isProtected: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -132,11 +132,11 @@
|
|
|
132
132
|
"pretty-ms": "^9.2.0",
|
|
133
133
|
"simple-git": "^3.30.0",
|
|
134
134
|
"yaml": "^2.8.2",
|
|
135
|
-
"@lumenflow/core": "2.1.
|
|
136
|
-
"@lumenflow/metrics": "2.1.
|
|
137
|
-
"@lumenflow/memory": "2.1.
|
|
138
|
-
"@lumenflow/initiatives": "2.1.
|
|
139
|
-
"@lumenflow/agent": "2.1.
|
|
135
|
+
"@lumenflow/core": "2.1.2",
|
|
136
|
+
"@lumenflow/metrics": "2.1.2",
|
|
137
|
+
"@lumenflow/memory": "2.1.2",
|
|
138
|
+
"@lumenflow/initiatives": "2.1.2",
|
|
139
|
+
"@lumenflow/agent": "2.1.2"
|
|
140
140
|
},
|
|
141
141
|
"devDependencies": {
|
|
142
142
|
"@vitest/coverage-v8": "^4.0.17",
|