dsh-config-manager 0.2.2 → 0.2.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.
@@ -53,7 +53,7 @@ export const zh = {
53
53
  'core.journal.notTerminal': 'moveToCompleted 仅接受 terminal journal: {opId} state={state}',
54
54
  // ---------- run-registry ----------
55
55
  'core.run.autosyncBusy': '自动同步已在进行中',
56
- 'core.run.syncApplyBusy': '一键同步已在进行中',
56
+ 'core.run.syncApplyBusy': '正在从远端合并',
57
57
  'core.run.backupScheduleBusy': '定时备份已在进行中',
58
58
  'core.run.restoreBusy': '快照恢复已在进行中(请等待当前恢复完成)',
59
59
  'core.run.recoveryBusy': '恢复/回滚已在进行中(请等待当前恢复完成)',
@@ -349,7 +349,7 @@ export const en = {
349
349
  'core.journal.notTerminal': 'moveToCompleted only accepts a terminal journal: {opId} state={state}',
350
350
  // ---------- run-registry ----------
351
351
  'core.run.autosyncBusy': 'Automatic sync is already in progress',
352
- 'core.run.syncApplyBusy': 'One-click sync is already in progress',
352
+ 'core.run.syncApplyBusy': 'Merge from remote is already in progress',
353
353
  'core.run.backupScheduleBusy': 'Scheduled backup is already in progress',
354
354
  'core.run.restoreBusy': 'Snapshot restore is already in progress (wait for the current restore to finish)',
355
355
  'core.run.recoveryBusy': 'Recovery/rollback is already in progress (wait for the current recovery to finish)',
package/lib/index.d.ts CHANGED
@@ -77,7 +77,7 @@ export declare const name = "config-manager";
77
77
  /** Services required before the engine can mount (present in every profile). */
78
78
  export declare const inject: string[];
79
79
  /** Plugin version, kept in sync with package.json ("version"). */
80
- export declare const PLUGIN_VERSION = "0.2.2";
80
+ export declare const PLUGIN_VERSION = "0.2.4";
81
81
  /**
82
82
  * Star 引导弹窗指向的 GitHub 仓库(用户引导点 Star 的目标)。
83
83
  * 与 package.json 的 repository 字段保持一致;界面不可改(硬编码,参照
package/lib/index.js CHANGED
@@ -136,7 +136,7 @@ export const name = 'config-manager';
136
136
  /** Services required before the engine can mount (present in every profile). */
137
137
  export const inject = ['settings', 'credentials'];
138
138
  /** Plugin version, kept in sync with package.json ("version"). */
139
- export const PLUGIN_VERSION = '0.2.2';
139
+ export const PLUGIN_VERSION = '0.2.4';
140
140
  /** Plugin own package name — excluded from its own exported plugins list. */
141
141
  const PLUGIN_NAME = 'dsh-config-manager';
142
142
  /**
@@ -21,13 +21,13 @@ export declare const zh: {
21
21
  readonly 'route.syncPreviewFailed': '同步预览失败';
22
22
  readonly 'route.syncSessionExpired': '同步会话不存在或已过期,请重新拉取预览';
23
23
  readonly 'route.syncConflictResolutionRequired': '冲突项 {id} 必须提供 resolution(useRemote/keepLocal/skip)';
24
- readonly 'route.syncApplySummary': '一键同步应用:{count} 项{status}{ineffective}';
24
+ readonly 'route.syncApplySummary': '从远端合并:{count} 项{status}{ineffective}';
25
25
  readonly 'route.syncRollbackIncomplete': '(回滚未完成)';
26
26
  readonly 'route.syncRolledBack': '(已回滚)';
27
27
  readonly 'route.syncIneffectiveSuffix': '({count} 项未生效)';
28
28
  readonly 'route.syncApplyPartial': '同步应用部分成功:有项未生效(见告警明细)';
29
29
  readonly 'route.syncApplyFailed': '同步应用未完全成功';
30
- readonly 'route.syncRollbackSummary': '一键同步回滚({id})';
30
+ readonly 'route.syncRollbackSummary': '从远端合并回滚({id})';
31
31
  readonly 'route.recoverySessionRepairRollback': '会话修复回滚';
32
32
  readonly 'route.recoverySessionRepairDropped': '会话修复(丢弃重放重复行 {count} 行)';
33
33
  readonly 'route.recoverySessionRepairRejected': '会话修复被拒绝:{reason}';
@@ -39,13 +39,13 @@ export const zh = {
39
39
  'route.syncPreviewFailed': '同步预览失败',
40
40
  'route.syncSessionExpired': '同步会话不存在或已过期,请重新拉取预览',
41
41
  'route.syncConflictResolutionRequired': '冲突项 {id} 必须提供 resolution(useRemote/keepLocal/skip)',
42
- 'route.syncApplySummary': '一键同步应用:{count} 项{status}{ineffective}',
42
+ 'route.syncApplySummary': '从远端合并:{count} 项{status}{ineffective}',
43
43
  'route.syncRollbackIncomplete': '(回滚未完成)',
44
44
  'route.syncRolledBack': '(已回滚)',
45
45
  'route.syncIneffectiveSuffix': '({count} 项未生效)',
46
46
  'route.syncApplyPartial': '同步应用部分成功:有项未生效(见告警明细)',
47
47
  'route.syncApplyFailed': '同步应用未完全成功',
48
- 'route.syncRollbackSummary': '一键同步回滚({id})',
48
+ 'route.syncRollbackSummary': '从远端合并回滚({id})',
49
49
  // ---------- 事故恢复 ----------
50
50
  'route.recoverySessionRepairRollback': '会话修复回滚',
51
51
  'route.recoverySessionRepairDropped': '会话修复(丢弃重放重复行 {count} 行)',
@@ -79,13 +79,13 @@ export const en = {
79
79
  'route.syncPreviewFailed': 'Sync preview failed',
80
80
  'route.syncSessionExpired': 'The sync session does not exist or has expired; pull a fresh preview',
81
81
  'route.syncConflictResolutionRequired': 'Conflict item {id} must provide a resolution (useRemote/keepLocal/skip)',
82
- 'route.syncApplySummary': 'One-click sync apply: {count} item(s){status}{ineffective}',
82
+ 'route.syncApplySummary': 'Merge from remote: {count} item(s){status}{ineffective}',
83
83
  'route.syncRollbackIncomplete': ' (rollback incomplete)',
84
84
  'route.syncRolledBack': ' (rolled back)',
85
85
  'route.syncIneffectiveSuffix': ' ({count} item(s) had no effect)',
86
86
  'route.syncApplyPartial': 'Sync apply partially succeeded: some items had no effect (see the warning details)',
87
87
  'route.syncApplyFailed': 'Sync apply did not fully succeed',
88
- 'route.syncRollbackSummary': 'One-click sync rollback ({id})',
88
+ 'route.syncRollbackSummary': 'Merge from remote rollback ({id})',
89
89
  'route.recoverySessionRepairRollback': 'Session repair rollback',
90
90
  'route.recoverySessionRepairDropped': 'Session repair (dropped {count} replayed duplicate row(s))',
91
91
  'route.recoverySessionRepairRejected': 'Session repair was rejected: {reason}',
@@ -7,7 +7,7 @@ export interface GitExecResult {
7
7
  stderr: string;
8
8
  code: number;
9
9
  }
10
- /** 可注入的 git 执行器(测试 mock 用);默认实现 = child_process.execFile promise 封装 */
10
+ /** 可注入的 git 执行器(测试 mock 用);默认实现 = child_process.spawn promise 封装(超时杀进程树) */
11
11
  export type GitExecFn = (cmd: string, args: string[], opts: {
12
12
  cwd?: string;
13
13
  timeoutMs?: number;
@@ -59,7 +59,7 @@ export interface GitTransportOptions {
59
59
  timeoutMs?: number;
60
60
  /** 幂等读操作(list/download 的 clone/pull)的网络重试参数;缺省 attempts=3、250ms 起指数退避 */
61
61
  retry?: SyncRetryOptions;
62
- /** 注入 exec(测试 mock 用);缺省 = execFile 封装 */
62
+ /** 注入 exec(测试 mock 用);缺省 = spawn 封装(超时杀进程树) */
63
63
  exec?: GitExecFn;
64
64
  /** 提交作者(写入远端历史),默认 DSH Config Sync <sync@dsh.local> */
65
65
  author?: GitAuthor;
@@ -75,11 +75,26 @@ export interface GitTransportOptions {
75
75
  export declare class GitTransportError extends SyncTransportError {
76
76
  constructor(message: string, opts?: SyncTransportErrorOptions);
77
77
  }
78
+ /**
79
+ * 默认 git 执行器:spawn + **进程树** kill(issue #122)。
80
+ *
81
+ * 为什么不再用 execFile:它的 timeout 只对 git.exe 自己发 SIGTERM,而真正在下包的是 git.exe 的子进程
82
+ * (git-remote-https / git index-pack)。父进程被杀、孙进程继续写 tmp_pack_*,管道不关闭 ⇒ execFile 的
83
+ * promise 连超时错误都可能不返回 ⇒ 上层「正在检查远端变更...」永久挂起,下一轮重试再起一份,最终 N 份
84
+ * 并发下载同一个 pack。
85
+ * 这里改为:POSIX 上 detached 起独立进程组,超时对**整组**发 SIGKILL;Windows 上 `taskkill /T /F`;
86
+ * 无论 kill 是否成功,宽限期到点都带着已收集输出收尾 —— 绝不把「管道没关」变成上层无限等待。
87
+ *
88
+ * 导出仅供测试直接验证「超时 + 进程树 kill + 有界收尾」(issue #122 的验收面)。
89
+ */
90
+ export declare const defaultGitExec: GitExecFn;
78
91
  /** 实现 SyncTransport 的 git 通道。所有操作前 ensureRepo() 保证工作副本就绪,网络命令带凭据。 */
79
92
  export declare class GitTransport implements SyncTransport {
80
93
  readonly type = "git";
81
94
  private readonly o;
82
95
  private repoReady;
96
+ /** 该工作副本是否 partial clone(remote.origin.promisor);null = 尚未判定(issue #122) */
97
+ private partialClone;
83
98
  /**
84
99
  * 工作副本锁的可重入深度:list/upload/download/delete 都会经 ensureRepo → reconcileOrigin,
85
100
  * rebuildWorkDir → adoptRemoteHistory 也各自嵌套;外层入口持锁,内层直接跑。
@@ -151,6 +166,30 @@ export declare class GitTransport implements SyncTransport {
151
166
  */
152
167
  private withWorkdir;
153
168
  private ensureRepo;
169
+ /**
170
+ * 首次 clone(issue #122):partial clone + sparse-checkout,只落地同步内容。
171
+ *
172
+ * 为什么:同步仓库可能比同步内容大几个数量级(报告者现场 1.8 GB 仓库 / 6 MB 内容)。此前 `git clone`
173
+ * 全量落地,之后每次 fetch 还要拉完整对象图 ⇒ 一次「检查远端变更」就下几百 MB。
174
+ * `--filter=blob:none` 只取 commit+tree,`--no-checkout` 先不落地任何文件,
175
+ * `sparse-checkout set --cone <三个目录>` 之后 checkout 只 materialize 这三个目录的 blob。
176
+ *
177
+ * 老 git 不认这些选项(unknown option)时回落全量 clone —— 行为与修复前完全一致,绝不因优化失败。
178
+ */
179
+ private cloneIntoWorkDir;
180
+ /**
181
+ * 该工作副本是不是 partial clone(`remote.origin.promisor=true`)。
182
+ *
183
+ * 只有 partial clone 才能给 fetch 加 `--filter=blob:none`:对普通全量副本加过滤会让后续 checkout
184
+ * 拿到缺失的 blob。存量副本(1.8 GB 那种)因此只受益于 ls-remote 短路与 `--no-tags`,不会被改坏。
185
+ */
186
+ private isPartialClone;
187
+ /** 拉取姿态:partial clone 保持 blob 过滤;同步仓库不需要 tag(issue #122) */
188
+ private fetchOptionFlags;
189
+ /** 远端分支当前的 sha(ls-remote 只取一行 refs);读不到 → null(短路是优化,失败一律回落正常 fetch) */
190
+ private lsRemoteBranchSha;
191
+ /** 解析引用指向的 sha;读不到 / 不是 sha → null */
192
+ private revParseSha;
154
193
  /**
155
194
  * 让工作副本的 origin 与配置的 repoUrl 保持一致(issue #79)。
156
195
  *
@@ -229,6 +268,11 @@ export declare class GitTransport implements SyncTransport {
229
268
  * 还会在本地留下一个已提交却推不上去的快照。
230
269
  */
231
270
  private runGit;
271
+ /**
272
+ * 把「git 退出非零」归一成可操作错误(runGit 与 clone 回落共用同一套文案与分类)。
273
+ * token 只用于脱敏,绝不进 message。
274
+ */
275
+ private raiseGitFailure;
232
276
  /**
233
277
  * 为网络命令构造 credential helper 参数:
234
278
  * 仅 http(s) 远端需要 token;本地路径 / ssh 走 git 原生认证(密钥),不调用 provider。