@longzai-intelligence-git/massive-sync-pipeline 0.0.1
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/dist/index.d.ts +149 -0
- package/dist/index.js +1 -0
- package/package.json +38 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { CommitSha, GitExecutor, OnProgress, RefName } from "@longzai-intelligence-git/core";
|
|
2
|
+
//#region src/pipeline.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* 同步策略(与 contract 的 MassiveTaskStrategy 同构,但 pipeline 不依赖 contract)
|
|
5
|
+
*/
|
|
6
|
+
type SyncStrategy = 'fast_forward_only' | 'rebase' | 'merge';
|
|
7
|
+
/**
|
|
8
|
+
* 阶段标识(与 core 的 MassiveStage 同构)
|
|
9
|
+
*/
|
|
10
|
+
type PipelineStage = 'fetch' | 'rebase' | 'merge' | 'push';
|
|
11
|
+
/**
|
|
12
|
+
* 单阶段结果
|
|
13
|
+
*/
|
|
14
|
+
type StageResult = {
|
|
15
|
+
stage: PipelineStage;
|
|
16
|
+
status: 'completed' | 'skipped';
|
|
17
|
+
durationMs: number;
|
|
18
|
+
resultingSha: CommitSha | null;
|
|
19
|
+
error: null;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 断点续传起点(pipeline 只关心阶段跳过逻辑,不关心存储介质)
|
|
23
|
+
*/
|
|
24
|
+
type ResumePoint = {
|
|
25
|
+
/**
|
|
26
|
+
* 已完成的阶段(如 ['fetch'],这些阶段会被跳过)
|
|
27
|
+
*/
|
|
28
|
+
completedStages?: PipelineStage[];
|
|
29
|
+
/**
|
|
30
|
+
* 当前位置 sha(fetch 跳过时作为 remoteTip)
|
|
31
|
+
*/
|
|
32
|
+
currentPosition?: CommitSha;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* hooks 回调(业务过程管理由调用方实现)
|
|
36
|
+
*/
|
|
37
|
+
type PipelineHooks = {
|
|
38
|
+
/**
|
|
39
|
+
* 阶段开始通知(调用方据此发布 task_stage_started 事件)
|
|
40
|
+
*
|
|
41
|
+
* 仅在实际执行的阶段触发(skipped 阶段不触发)。
|
|
42
|
+
*/
|
|
43
|
+
onStageStarted?: (stage: PipelineStage) => Promise<void> | void;
|
|
44
|
+
/**
|
|
45
|
+
* 阶段完成通知(调用方据此存断点)
|
|
46
|
+
*/
|
|
47
|
+
onStageCompleted?: (stage: PipelineStage, resultingSha: CommitSha | null, processedCount: number) => Promise<void> | void;
|
|
48
|
+
/**
|
|
49
|
+
* 进度回调转发(调用方据此上报到 progressPort / eventBus)
|
|
50
|
+
*/
|
|
51
|
+
onProgress?: OnProgress;
|
|
52
|
+
/**
|
|
53
|
+
* 协作式取消检查(调用方返回 true 则在阶段切换处中止)
|
|
54
|
+
*/
|
|
55
|
+
shouldCancel?: () => boolean;
|
|
56
|
+
/**
|
|
57
|
+
* 建议提示回调(调用方据此向用户展示建议,如"建议启用 LFS")
|
|
58
|
+
*
|
|
59
|
+
* pipeline 在 LFS 探测发现"远端支持 LFS 但仓库未启用"时调用,
|
|
60
|
+
* 上抛建议给业务层。pipeline 自身不执行 LFS migrate(破坏性不可逆)。
|
|
61
|
+
*/
|
|
62
|
+
onAdvice?: (advice: string) => Promise<void> | void;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* pipeline 执行选项
|
|
66
|
+
*/
|
|
67
|
+
type PipelineOptions = {
|
|
68
|
+
/**
|
|
69
|
+
* git 执行器
|
|
70
|
+
*/
|
|
71
|
+
executor: GitExecutor;
|
|
72
|
+
/**
|
|
73
|
+
* 仓库工作目录
|
|
74
|
+
*/
|
|
75
|
+
repoPath: string;
|
|
76
|
+
/**
|
|
77
|
+
* 远端名
|
|
78
|
+
*/
|
|
79
|
+
remote: string;
|
|
80
|
+
/**
|
|
81
|
+
* 目标 ref
|
|
82
|
+
*/
|
|
83
|
+
ref: RefName;
|
|
84
|
+
/**
|
|
85
|
+
* 同步策略
|
|
86
|
+
*/
|
|
87
|
+
strategy: SyncStrategy;
|
|
88
|
+
/**
|
|
89
|
+
* 分段大小
|
|
90
|
+
*/
|
|
91
|
+
segmentSize: number;
|
|
92
|
+
/**
|
|
93
|
+
* fetch 停滞超时
|
|
94
|
+
*/
|
|
95
|
+
stallTimeoutMs: number;
|
|
96
|
+
/**
|
|
97
|
+
* fetch 绝对超时
|
|
98
|
+
*/
|
|
99
|
+
absoluteTimeoutMs: number;
|
|
100
|
+
/**
|
|
101
|
+
* rebase/merge/push 单阶段超时
|
|
102
|
+
*/
|
|
103
|
+
stageTimeoutMs: number;
|
|
104
|
+
/**
|
|
105
|
+
* 单阶段重试次数
|
|
106
|
+
*/
|
|
107
|
+
stageRetries: number;
|
|
108
|
+
/**
|
|
109
|
+
* 断点续传起点(缺省从头开始)
|
|
110
|
+
*/
|
|
111
|
+
resumeFrom?: ResumePoint;
|
|
112
|
+
/**
|
|
113
|
+
* 是否启用 LFS 探测 + 透传,默认 true
|
|
114
|
+
*
|
|
115
|
+
* 启用后:
|
|
116
|
+
* - fetch 阶段起始处探测 LFS 三项状态(客户端/仓库/远端)
|
|
117
|
+
* - 仓库已启用 LFS → fetch 后透传 git lfs fetch,push 前透传 git lfs push
|
|
118
|
+
* - 远端支持但仓库未启用 → 上抛建议提示(不自动 migrate)
|
|
119
|
+
*/
|
|
120
|
+
enableLfsPassthrough?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* hooks 回调
|
|
123
|
+
*/
|
|
124
|
+
hooks?: PipelineHooks;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* pipeline 执行结果
|
|
128
|
+
*/
|
|
129
|
+
type PipelineResult = {
|
|
130
|
+
/**
|
|
131
|
+
* 各阶段结果
|
|
132
|
+
*/
|
|
133
|
+
stages: StageResult[];
|
|
134
|
+
/**
|
|
135
|
+
* 总耗时(ms)
|
|
136
|
+
*/
|
|
137
|
+
totalDurationMs: number;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* 创建 sync pipeline
|
|
141
|
+
*
|
|
142
|
+
* @param executor - GitExecutor 实例
|
|
143
|
+
* @returns 提供 execute 方法的执行编排管线
|
|
144
|
+
*/
|
|
145
|
+
declare const createSyncPipeline: (executor: GitExecutor) => {
|
|
146
|
+
execute: (options: PipelineOptions) => Promise<PipelineResult>;
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { type PipelineHooks, type PipelineOptions, type PipelineResult, type PipelineStage, type ResumePoint, type StageResult, type SyncStrategy, createSyncPipeline };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CHECKOUT_FILE_THRESHOLD as e,PATCH_BYTE_THRESHOLD as t,createChunkedFetcher as n,createCommitSha as r,createIncrementalPusher as i,createMerger as a,createRefName as o,createSegmentedRebaser as s,detectStrategy as c,estimateRebaseCost as l,fetchLfs as u,probeLfs as d,pushLfs as f,withMassiveGuard as p}from"@longzai-intelligence-git/core";const m=e=>e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(1)} GB`,h=h=>({execute:async g=>{let _=Date.now(),v=[],y=g.hooks??{},b=()=>{if(y.shouldCancel?.()===!0)throw Error(`任务已取消`)},x=g.enableLfsPassthrough??!0,S=null;x&&(S=await d(h,g.repoPath,g.remote),S.clientInstalled&&S.remoteSupported&&!S.repoEnabled&&await y.onAdvice?.(`检测到远端支持 LFS(Git Large File Storage),但当前仓库未启用。仓库含大文件时,启用 LFS 可显著优化 fetch/push 传输(大文件不进 git pack)。请手动执行 git lfs install + git lfs migrate import 后重新同步。`));let C=g.resumeFrom?.completedStages?.includes(`fetch`)??!1,w;C?(w={remoteTip:g.resumeFrom?.currentPosition??null,durationMs:0,stallRetries:0,completeness:`tip`,layersFetched:0,refetchBatches:0},v.push({stage:`fetch`,status:`skipped`,durationMs:0,resultingSha:w.remoteTip,error:null})):(await y.onStageStarted?.(`fetch`),w=await n(h).fetch({repoPath:g.repoPath,remote:g.remote,ref:o(g.ref),stallTimeoutMs:g.stallTimeoutMs,absoluteTimeoutMs:g.absoluteTimeoutMs,maxStallRetries:g.stageRetries+2,onProgress:y.onProgress??null,fetchCompleteness:`full`,deepenStep:g.segmentSize,maxDeepenLayers:50,enableNegotiationOpt:!0}),v.push({stage:`fetch`,status:`completed`,durationMs:w.durationMs,resultingSha:w.remoteTip,error:null}),await y.onStageCompleted?.(`fetch`,w.remoteTip,w.stallRetries)),S?.repoEnabled===!0&&!C&&await u(h,{repoPath:g.repoPath,remote:g.remote,stallTimeoutMs:g.stallTimeoutMs,absoluteTimeoutMs:g.absoluteTimeoutMs,onProgress:y.onProgress??null}),b();let T,E=``;if(w.remoteTip!=null){E=(await p({executor:h,repoPath:g.repoPath,args:[`rev-parse`,`HEAD`],readOnly:!0,onStderrLine:()=>{}})).stdout.trim();let n=(await p({executor:h,repoPath:g.repoPath,args:[`merge-base`,E,w.remoteTip],readOnly:!0,onStderrLine:()=>{}})).stdout.trim(),i=await p({executor:h,repoPath:g.repoPath,args:[`rev-list`,`--count`,`${n}..${E}`],readOnly:!0,onStderrLine:()=>{}}),a=Number.parseInt(i.stdout,10)||0,o=await p({executor:h,repoPath:g.repoPath,args:[`rev-list`,`--count`,`${n}..${w.remoteTip}`],readOnly:!0,onStderrLine:()=>{}}),s=Number.parseInt(o.stdout,10)||0,u=c(r(E),w.remoteTip,r(n),a,s);if(u.strategy===`rebase`&&g.strategy===`fast_forward_only`)throw Error(`[策略校验失败] 实际分叉(本地领先 ${a},远端领先 ${s})需要 rebase/merge,但 strategy=fast_forward_only。禁止在分叉场景强制快进(会失败或诱发 force push 事故)。请改用 strategy=rebase(小差异)或 strategy=merge(大差异/平行链)。`);if(u.strategy===`rebase`&&g.strategy===`rebase`){let n=await l(h,g.repoPath,w.remoteTip,r(E)),i=n.patchFileUnionCount>e,a=n.patchFileBytes!=null&&n.patchFileBytes>t;if(i||a){let r=[`patch 触碰 ${n.patchFileUnionCount} 文件(阈值 ${e})`];throw n.patchFileBytes!=null&&r.push(`累计 ${m(n.patchFileBytes)}(阈值 ${m(t)})`),Error(`[D5 代价评估] 待重放 ${n.commitCount} commit 的 ${r.join(`,`)}。大差异/大文件 rebase 会触发大量 checkout(事故根因,见 issue 001/008)。平行链应使用 strategy=merge(保留两条链历史,只 checkout 一次)。请改用 strategy=merge。`)}}}if(g.strategy===`fast_forward_only`||w.remoteTip==null)T=r(E||``),v.push({stage:`rebase`,status:`skipped`,durationMs:0,resultingSha:T,error:null}),v.push({stage:`merge`,status:`skipped`,durationMs:0,resultingSha:T,error:null});else if(g.strategy===`merge`){v.push({stage:`rebase`,status:`skipped`,durationMs:0,resultingSha:null,error:null}),await y.onStageStarted?.(`merge`);let e=r(E),t=await a(h).merge({repoPath:g.repoPath,remote:g.remote,ref:o(g.ref),remoteTip:w.remoteTip,localTip:e,timeoutMs:g.stageTimeoutMs,retries:g.stageRetries,onProgress:y.onProgress??null});T=t.newLocalTip,v.push({stage:`merge`,status:`completed`,durationMs:t.durationMs,resultingSha:T,error:null}),await y.onStageCompleted?.(`merge`,T,+!!t.mergeCommitSha)}else{v.push({stage:`merge`,status:`skipped`,durationMs:0,resultingSha:null,error:null}),await y.onStageStarted?.(`rebase`);let e=r(E),t=await s(h).rebase({repoPath:g.repoPath,remote:g.remote,ref:o(g.ref),baseSha:w.remoteTip,localTip:e,segmentSize:g.segmentSize,timeoutMs:g.stageTimeoutMs,retries:g.stageRetries,onProgress:y.onProgress??null});T=t.newLocalTip,v.push({stage:`rebase`,status:`completed`,durationMs:t.durationMs,resultingSha:T,error:null}),await y.onStageCompleted?.(`rebase`,T,t.segmentsRebased)}if(b(),await y.onStageStarted?.(`push`),S?.repoEnabled===!0){let e=await f(h,{repoPath:g.repoPath,remote:g.remote,stallTimeoutMs:g.stallTimeoutMs,absoluteTimeoutMs:g.absoluteTimeoutMs,onProgress:y.onProgress??null},g.ref,r(T));if(!e.success)throw Error(`LFS push 失败(LFS 对象缺失会导致远端拒绝 commit): ${e.error??`(无 stderr)`}`)}let D=await i(h).push({repoPath:g.repoPath,remote:g.remote,ref:o(g.ref),localTip:r(T),remoteCurrentSha:w.remoteTip??r(T),segmentSize:g.segmentSize,timeoutMs:g.stageTimeoutMs,retries:g.stageRetries,onProgress:y.onProgress??null});return v.push({stage:`push`,status:`completed`,durationMs:D.durationMs,resultingSha:D.remoteFinalSha,error:null}),{stages:v,totalDurationMs:Date.now()-_}}});export{h as createSyncPipeline};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-git/massive-sync-pipeline",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Longzai Intelligence Git - 执行编排管线",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "lzi-builder",
|
|
19
|
+
"build:prod": "NODE_ENV=production bun run build",
|
|
20
|
+
"prepublishOnly": "bun run build:prod",
|
|
21
|
+
"typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
|
|
22
|
+
"typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
|
|
23
|
+
"typecheck:node": "lzi-tsgo typecheck tsconfig/node.json",
|
|
24
|
+
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json",
|
|
25
|
+
"lint": "oxlint && oxfmt --check",
|
|
26
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"test:watch": "bun test --watch",
|
|
29
|
+
"test:coverage": "bun test --coverage",
|
|
30
|
+
"clean": "lzi-dev-cli clean"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@longzai-intelligence-git/core": "0.0.1"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"zod": "^4.4.3"
|
|
37
|
+
}
|
|
38
|
+
}
|