worktree-bay 1.0.3 → 1.1.0
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/README.md +14 -10
- package/dist/cli.js +23 -6
- package/dist/commands/add.js +9 -3
- package/dist/commands/completion.js +39 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,12 +27,8 @@ npm i -g worktree-bay
|
|
|
27
27
|
## 快速上手
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
#
|
|
31
|
-
worktree-bay
|
|
32
|
-
|
|
33
|
-
# 把服务挂进这个功能的槽(开 worktree + 跑该服务的步骤)
|
|
34
|
-
worktree-bay add drill-fix api feature/drill-fix # api 起在 6011
|
|
35
|
-
worktree-bay add drill-fix lms feature/drill-ui # 前端自动接 6011
|
|
30
|
+
# 一条命令起整个功能:自动占槽 + 在 api/lms 上开 worktree(分支默认 = 功能名)
|
|
31
|
+
worktree-bay up drill-fix api lms
|
|
36
32
|
|
|
37
33
|
# 看占用
|
|
38
34
|
worktree-bay ls
|
|
@@ -40,11 +36,15 @@ worktree-bay ls
|
|
|
40
36
|
# 在服务运行体里跑命令(透传)
|
|
41
37
|
worktree-bay run drill-fix api test
|
|
42
38
|
|
|
43
|
-
#
|
|
44
|
-
worktree-bay
|
|
45
|
-
|
|
39
|
+
# 拆除整个功能(默认查脏/未推保护,-f 强删)
|
|
40
|
+
worktree-bay down drill-fix
|
|
41
|
+
|
|
42
|
+
# 回收已合并的(默认 dry-run,--apply 实际执行)
|
|
43
|
+
worktree-bay gc
|
|
46
44
|
```
|
|
47
45
|
|
|
46
|
+
> 需要更细的控制:`claim <feature>` 单独占槽、`add <feature> <service> [branch]` 单加一个服务(branch 可自定义,省略则用功能名)、`rm <feature> [service]` 拆单个服务。
|
|
47
|
+
|
|
48
48
|
## 配置
|
|
49
49
|
|
|
50
50
|
在工作区根放一份 `worktree-bay.config.json`,集中声明所有服务。工具运行时自下而上查找它(或用环境变量 `WORKTREE_BAY_CONFIG` 指定)。
|
|
@@ -106,10 +106,14 @@ worktree-bay gc # 合并感知回收(默认 dry-run,--app
|
|
|
106
106
|
|
|
107
107
|
## Shell 补全
|
|
108
108
|
|
|
109
|
+
一条命令装好(自动探测 shell、幂等写入对应 rc;fish 直接写补全目录):
|
|
110
|
+
|
|
109
111
|
```bash
|
|
110
|
-
worktree-bay completion
|
|
112
|
+
worktree-bay completion install
|
|
111
113
|
```
|
|
112
114
|
|
|
115
|
+
执行 `source ~/.bashrc`(或重开终端)即可 tab 补全子命令、功能名、服务名。也可手动:`worktree-bay completion bash`(打印脚本,自行接入)。
|
|
116
|
+
|
|
113
117
|
## 许可证
|
|
114
118
|
|
|
115
119
|
[MIT](./LICENSE)
|
package/dist/cli.js
CHANGED
|
@@ -4,11 +4,11 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { loadConfig } from './config.js';
|
|
5
5
|
import { claimCommand } from './commands/claim.js';
|
|
6
6
|
import { lsCommand } from './commands/ls.js';
|
|
7
|
-
import { addCommand } from './commands/add.js';
|
|
7
|
+
import { addCommand, upCommand } from './commands/add.js';
|
|
8
8
|
import { runCommand, shCommand } from './commands/passthrough.js';
|
|
9
9
|
import { rmCommand } from './commands/rm.js';
|
|
10
10
|
import { gcCommand } from './commands/gc.js';
|
|
11
|
-
import { complete, completionCommand } from './commands/completion.js';
|
|
11
|
+
import { complete, completionCommand, installCompletion } from './commands/completion.js';
|
|
12
12
|
import { die } from './util/log.js';
|
|
13
13
|
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
14
14
|
const program = new Command();
|
|
@@ -28,7 +28,14 @@ catch (e) {
|
|
|
28
28
|
} });
|
|
29
29
|
program.command('ls').description('列出所有槽位与占用状态')
|
|
30
30
|
.action(() => sync(lsCommand));
|
|
31
|
-
program.command('
|
|
31
|
+
program.command('up <feature> <services...>').description('一条命令为功能起多个服务(自动 claim + 各服务默认分支 = 功能名)')
|
|
32
|
+
.action(async (f, services) => { try {
|
|
33
|
+
await upCommand(loadConfig(process.cwd()), f, services);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
die(e.message);
|
|
37
|
+
} });
|
|
38
|
+
program.command('add <feature> <service> [branch] [base]').description('为功能在某服务开 worktree(branch 默认 = 功能名)')
|
|
32
39
|
.action(async (f, s, b, base) => { try {
|
|
33
40
|
await addCommand(loadConfig(process.cwd()), f, s, b, base);
|
|
34
41
|
}
|
|
@@ -39,6 +46,13 @@ program.command('run <feature> <service> <name> [args...]').description('在服
|
|
|
39
46
|
.action((f, s, n, args) => sync((c) => runCommand(c, f, s, n, args ?? [])));
|
|
40
47
|
program.command('sh <feature> <service>').description('进入服务运行体的 shell')
|
|
41
48
|
.action((f, s) => sync((c) => shCommand(c, f, s)));
|
|
49
|
+
program.command('down <feature>').description('拆除整个功能的所有服务 worktree(= rm <feature>)').option('-f, --force', '跳过脏/未推检查强制删除')
|
|
50
|
+
.action(async (f, o) => { try {
|
|
51
|
+
await rmCommand(loadConfig(process.cwd()), f, undefined, !!o.force);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
die(e.message);
|
|
55
|
+
} });
|
|
42
56
|
program.command('rm <feature> [service]').description('拆除某服务或整槽的 worktree(默认查脏/未推保护)').option('-f, --force', '跳过脏/未推检查强制删除')
|
|
43
57
|
.action(async (f, s, o) => { try {
|
|
44
58
|
await rmCommand(loadConfig(process.cwd()), f, s, !!o.force);
|
|
@@ -53,9 +67,12 @@ program.command('gc').description('合并感知回收(默认 dry-run)').opti
|
|
|
53
67
|
catch (e) {
|
|
54
68
|
die(e.message);
|
|
55
69
|
} });
|
|
56
|
-
program.command('completion <shell
|
|
57
|
-
.action((
|
|
58
|
-
|
|
70
|
+
program.command('completion <target> [shell]').description('install 一键装进 shell;或 bash|zsh|fish 打印补全脚本')
|
|
71
|
+
.action((target, shell) => { try {
|
|
72
|
+
if (target === 'install')
|
|
73
|
+
installCompletion(shell);
|
|
74
|
+
else
|
|
75
|
+
completionCommand(target);
|
|
59
76
|
}
|
|
60
77
|
catch (e) {
|
|
61
78
|
die(e.message);
|
package/dist/commands/add.js
CHANGED
|
@@ -14,12 +14,18 @@ export function resolveAdd(cfg, feature, service, branch) {
|
|
|
14
14
|
return { service, slot, slug, dir: path.join(repoPath(cfg, service), '.worktrees', slug), repo: repoPath(cfg, service) };
|
|
15
15
|
}
|
|
16
16
|
export async function addCommand(cfg, feature, service, branch, base) {
|
|
17
|
+
const br = branch || feature; // 默认分支 = 功能名
|
|
17
18
|
await withLock(cfg.workspaceRoot, async () => {
|
|
18
|
-
const p = resolveAdd(cfg, feature, service,
|
|
19
|
+
const p = resolveAdd(cfg, feature, service, br);
|
|
19
20
|
const sp = cfg.services[service];
|
|
20
21
|
const ctxBase = { cfg, service, sp, slot: p.slot, slug: p.slug, dir: p.dir, repo: p.repo };
|
|
21
22
|
const ctx = { ...ctxBase, vars: buildVars(cfg, ctxBase) };
|
|
22
|
-
await bringUp(ctx, base ?? `origin/${mainBranch(p.repo)}`,
|
|
23
|
-
log(`✓ ${service} 挂入 "${feature}"(槽 ${p.slot},端口 ${ctx.vars.port})`);
|
|
23
|
+
await bringUp(ctx, base ?? `origin/${mainBranch(p.repo)}`, br);
|
|
24
|
+
log(`✓ ${service} 挂入 "${feature}"(槽 ${p.slot},端口 ${ctx.vars.port},分支 ${br})`);
|
|
24
25
|
});
|
|
25
26
|
}
|
|
27
|
+
// up: 一条命令为功能批量起多个服务(claim 自动 + 各服务默认分支)
|
|
28
|
+
export async function upCommand(cfg, feature, services, base) {
|
|
29
|
+
for (const service of services)
|
|
30
|
+
await addCommand(cfg, feature, service, undefined, base);
|
|
31
|
+
}
|
|
@@ -1,25 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { readLabels } from '../slots.js';
|
|
2
5
|
import { log } from '../util/log.js';
|
|
3
|
-
const SUBCMDS = ['claim', 'add', 'ls', 'gc', 'rm', 'run', 'sh', 'completion'];
|
|
6
|
+
const SUBCMDS = ['claim', 'up', 'add', 'ls', 'gc', 'down', 'rm', 'run', 'sh', 'completion'];
|
|
7
|
+
// words = 命令名 + 光标前已输入完的词(不含当前正在补的词)
|
|
4
8
|
export function complete(cfg, words) {
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
9
|
+
const prev = words.slice(1);
|
|
10
|
+
if (prev.length === 0)
|
|
7
11
|
return SUBCMDS;
|
|
8
|
-
const sub =
|
|
9
|
-
const pos =
|
|
10
|
-
|
|
12
|
+
const sub = prev[0];
|
|
13
|
+
const pos = prev.length;
|
|
14
|
+
const featureSubs = ['up', 'add', 'rm', 'down', 'run', 'sh'];
|
|
15
|
+
if (featureSubs.includes(sub) && pos === 1)
|
|
11
16
|
return Object.values(readLabels(cfg));
|
|
12
17
|
if (['add', 'run', 'sh'].includes(sub) && pos === 2)
|
|
13
18
|
return Object.keys(cfg.services);
|
|
19
|
+
if (sub === 'up' && pos >= 2)
|
|
20
|
+
return Object.keys(cfg.services); // up 接变长服务列表
|
|
14
21
|
return [];
|
|
15
22
|
}
|
|
16
23
|
export function completionScript(shell) {
|
|
24
|
+
// 脚本只传"光标前已完成的词",不含当前正在补的词,与 complete() 的模型一致
|
|
17
25
|
if (shell === 'bash')
|
|
18
|
-
return `_worktree_bay(){ COMPREPLY=( $(worktree-bay __complete -- "\${COMP_WORDS[@]}") ); }\ncomplete -F _worktree_bay worktree-bay`;
|
|
26
|
+
return `_worktree_bay(){ COMPREPLY=( $(worktree-bay __complete -- "\${COMP_WORDS[@]:0:\$COMP_CWORD}") ); }\ncomplete -F _worktree_bay worktree-bay`;
|
|
19
27
|
if (shell === 'zsh')
|
|
20
|
-
return `#compdef worktree-bay\n_worktree_bay(){ compadd -- $(worktree-bay __complete -- "\${words[
|
|
28
|
+
return `#compdef worktree-bay\n_worktree_bay(){ compadd -- $(worktree-bay __complete -- "\${(@)words[1,CURRENT-1]}") }\ncompdef _worktree_bay worktree-bay`;
|
|
21
29
|
if (shell === 'fish')
|
|
22
|
-
return `complete -c worktree-bay -a '(worktree-bay __complete -- (commandline -opc))'`;
|
|
30
|
+
return `complete -c worktree-bay -f -a '(worktree-bay __complete -- (commandline -opc))'`;
|
|
23
31
|
throw new Error('unsupported shell: ' + shell);
|
|
24
32
|
}
|
|
25
33
|
export function completionCommand(shell) { log(completionScript(shell)); }
|
|
34
|
+
// 一键把补全装进当前 shell(幂等)。fish 直接写补全目录(零配置生效);bash/zsh 往 rc 加一行 eval。
|
|
35
|
+
export function installCompletion(shell) {
|
|
36
|
+
const sh = shell || path.basename(process.env.SHELL || 'bash');
|
|
37
|
+
if (sh === 'fish') {
|
|
38
|
+
const dir = path.join(os.homedir(), '.config', 'fish', 'completions');
|
|
39
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
40
|
+
const file = path.join(dir, 'worktree-bay.fish');
|
|
41
|
+
fs.writeFileSync(file, completionScript('fish') + '\n');
|
|
42
|
+
log(`✓ fish 补全已写入 ${file}(新开 fish 即生效)`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const isZsh = sh === 'zsh';
|
|
46
|
+
const rc = path.join(os.homedir(), isZsh ? '.zshrc' : '.bashrc');
|
|
47
|
+
const line = `eval "$(worktree-bay completion ${isZsh ? 'zsh' : 'bash'})"`;
|
|
48
|
+
const cur = fs.existsSync(rc) ? fs.readFileSync(rc, 'utf8') : '';
|
|
49
|
+
if (cur.includes(line)) {
|
|
50
|
+
log(`✓ 补全已在 ${rc},无需重复安装`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
fs.appendFileSync(rc, `\n# worktree-bay completion\n${line}\n`);
|
|
54
|
+
log(`✓ 补全已加入 ${rc},执行 'source ${rc}' 或重开终端即生效`);
|
|
55
|
+
}
|