@fern-api/replay 0.1.0 → 0.2.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/dist/LockfileManager.d.ts +1 -0
- package/dist/LockfileManager.d.ts.map +1 -1
- package/dist/LockfileManager.js +4 -0
- package/dist/LockfileManager.js.map +1 -1
- package/dist/ReplayDetector.d.ts +6 -0
- package/dist/ReplayDetector.d.ts.map +1 -1
- package/dist/ReplayDetector.js +40 -0
- package/dist/ReplayDetector.js.map +1 -1
- package/dist/ReplayService.d.ts +29 -27
- package/dist/ReplayService.d.ts.map +1 -1
- package/dist/ReplayService.js +100 -145
- package/dist/ReplayService.js.map +1 -1
- package/dist/cli.js +6 -31
- package/dist/cli.js.map +1 -1
- package/dist/git/GitClient.d.ts +5 -0
- package/dist/git/GitClient.d.ts.map +1 -1
- package/dist/git/GitClient.js +14 -0
- package/dist/git/GitClient.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +0 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -7
- package/dist/types.js.map +1 -1
- package/package.json +43 -43
- package/dist/ConflictPrompt.d.ts +0 -13
- package/dist/ConflictPrompt.d.ts.map +0 -1
- package/dist/ConflictPrompt.js +0 -38
- package/dist/ConflictPrompt.js.map +0 -1
- package/dist/ConflictResolver.d.ts +0 -11
- package/dist/ConflictResolver.d.ts.map +0 -1
- package/dist/ConflictResolver.js +0 -53
- package/dist/ConflictResolver.js.map +0 -1
- package/dist/environment.d.ts +0 -5
- package/dist/environment.d.ts.map +0 -1
- package/dist/environment.js +0 -14
- package/dist/environment.js.map +0 -1
|
@@ -19,6 +19,7 @@ export declare class LockfileManager {
|
|
|
19
19
|
addPatch(patch: StoredPatch): void;
|
|
20
20
|
updatePatch(patchId: string, updates: Partial<Pick<StoredPatch, "base_generation" | "patch_content" | "content_hash" | "files">>): void;
|
|
21
21
|
removePatch(patchId: string): void;
|
|
22
|
+
clearPatches(): void;
|
|
22
23
|
getPatches(): StoredPatch[];
|
|
23
24
|
getGeneration(commitSha: string): GenerationRecord | undefined;
|
|
24
25
|
getCustomizationsConfig(): CustomizationsConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LockfileManager.d.ts","sourceRoot":"","sources":["../src/LockfileManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACvB,MAAM,YAAY,CAAC;AAIpB,qBAAa,eAAe;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAA+B;gBAE/B,SAAS,EAAE,MAAM;IAI7B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,kBAAkB,IAAI,MAAM,CAE/B;IAED,MAAM,IAAI,OAAO;IAIjB,IAAI,IAAI,cAAc;IAYtB,UAAU,CAAC,eAAe,EAAE,gBAAgB,GAAG,IAAI;IAKnD;;;;OAIG;IACH,kBAAkB,CAAC,eAAe,EAAE,gBAAgB,GAAG,IAAI;IAS3D,IAAI,IAAI,IAAI;IAmBZ,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAM7C,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAKlC,WAAW,CACP,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC,GACpG,IAAI;IASP,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKlC,UAAU,IAAI,WAAW,EAAE;IAK3B,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAK9D,uBAAuB,IAAI,oBAAoB;IAQ/C,OAAO,CAAC,YAAY;CAKvB"}
|
|
1
|
+
{"version":3,"file":"LockfileManager.d.ts","sourceRoot":"","sources":["../src/LockfileManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACvB,MAAM,YAAY,CAAC;AAIpB,qBAAa,eAAe;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAA+B;gBAE/B,SAAS,EAAE,MAAM;IAI7B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,kBAAkB,IAAI,MAAM,CAE/B;IAED,MAAM,IAAI,OAAO;IAIjB,IAAI,IAAI,cAAc;IAYtB,UAAU,CAAC,eAAe,EAAE,gBAAgB,GAAG,IAAI;IAKnD;;;;OAIG;IACH,kBAAkB,CAAC,eAAe,EAAE,gBAAgB,GAAG,IAAI;IAS3D,IAAI,IAAI,IAAI;IAmBZ,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAM7C,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAKlC,WAAW,CACP,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC,GACpG,IAAI;IASP,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKlC,YAAY,IAAI,IAAI;IAKpB,UAAU,IAAI,WAAW,EAAE;IAK3B,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAK9D,uBAAuB,IAAI,oBAAoB;IAQ/C,OAAO,CAAC,YAAY;CAKvB"}
|
package/dist/LockfileManager.js
CHANGED
|
@@ -84,6 +84,10 @@ export class LockfileManager {
|
|
|
84
84
|
this.ensureLoaded();
|
|
85
85
|
this.lock.patches = this.lock.patches.filter((p) => p.id !== patchId);
|
|
86
86
|
}
|
|
87
|
+
clearPatches() {
|
|
88
|
+
this.ensureLoaded();
|
|
89
|
+
this.lock.patches = [];
|
|
90
|
+
}
|
|
87
91
|
getPatches() {
|
|
88
92
|
this.ensureLoaded();
|
|
89
93
|
return this.lock.patches;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LockfileManager.js","sourceRoot":"","sources":["../src/LockfileManager.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LockfileManager.js","sourceRoot":"","sources":["../src/LockfileManager.ts"],"names":[],"mappings":"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"}
|
package/dist/ReplayDetector.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export declare class ReplayDetector {
|
|
|
19
19
|
* so rebased commits with same content produce the same hash.
|
|
20
20
|
*/
|
|
21
21
|
computeContentHash(patchContent: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Detect customization patches via tree diff when the generation commit
|
|
24
|
+
* is not an ancestor of HEAD (non-linear history from squash merge).
|
|
25
|
+
* Returns a single composite patch covering all customizations.
|
|
26
|
+
*/
|
|
27
|
+
private detectPatchesViaTreeDiff;
|
|
22
28
|
private parseGitLog;
|
|
23
29
|
private getLastGeneration;
|
|
24
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplayDetector.d.ts","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ReplayDetector.d.ts","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAgD,WAAW,EAAE,MAAM,YAAY,CAAC;AAM5F,qBAAa,cAAc;IACvB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,YAAY,CAAS;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAM;gBAErB,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM;IAM9E;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAyFhD;;;;OAIG;IACH,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAShD;;;;OAIG;YACW,wBAAwB;IA+BtC,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,iBAAiB;CAG5B"}
|
package/dist/ReplayDetector.js
CHANGED
|
@@ -30,6 +30,13 @@ export class ReplayDetector {
|
|
|
30
30
|
`Skipping new patch detection. Existing lockfile patches will still be applied.`);
|
|
31
31
|
return [];
|
|
32
32
|
}
|
|
33
|
+
// If the generation commit is not an ancestor of HEAD (e.g., after a squash
|
|
34
|
+
// merge of a divergent PR), git log would return all of main's history.
|
|
35
|
+
// Fall back to tree-diff-based detection which handles non-linear history.
|
|
36
|
+
const isAncestor = await this.git.isAncestor(lastGen.commit_sha, "HEAD");
|
|
37
|
+
if (!isAncestor) {
|
|
38
|
+
return this.detectPatchesViaTreeDiff(lastGen);
|
|
39
|
+
}
|
|
33
40
|
const log = await this.git.exec([
|
|
34
41
|
"log",
|
|
35
42
|
"--format=%H%x00%an%x00%ae%x00%s",
|
|
@@ -94,6 +101,39 @@ export class ReplayDetector {
|
|
|
94
101
|
.join("\n");
|
|
95
102
|
return `sha256:${createHash("sha256").update(normalized).digest("hex")}`;
|
|
96
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Detect customization patches via tree diff when the generation commit
|
|
106
|
+
* is not an ancestor of HEAD (non-linear history from squash merge).
|
|
107
|
+
* Returns a single composite patch covering all customizations.
|
|
108
|
+
*/
|
|
109
|
+
async detectPatchesViaTreeDiff(lastGen) {
|
|
110
|
+
// Get changed files first, then filter before computing the diff
|
|
111
|
+
const filesOutput = await this.git.exec(["diff", "--name-only", lastGen.commit_sha, "HEAD"]);
|
|
112
|
+
const files = filesOutput
|
|
113
|
+
.trim()
|
|
114
|
+
.split("\n")
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.filter((f) => !INFRASTRUCTURE_FILES.has(f))
|
|
117
|
+
.filter((f) => !f.startsWith(".fern/"));
|
|
118
|
+
if (files.length === 0)
|
|
119
|
+
return [];
|
|
120
|
+
// Compute diff only for the filtered files
|
|
121
|
+
const diff = await this.git.exec(["diff", lastGen.commit_sha, "HEAD", "--", ...files]);
|
|
122
|
+
if (!diff.trim())
|
|
123
|
+
return [];
|
|
124
|
+
const contentHash = this.computeContentHash(diff);
|
|
125
|
+
const headSha = (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
126
|
+
return [{
|
|
127
|
+
id: `patch-composite-${headSha.slice(0, 8)}`,
|
|
128
|
+
content_hash: contentHash,
|
|
129
|
+
original_commit: headSha,
|
|
130
|
+
original_message: "Customer customizations (composite)",
|
|
131
|
+
original_author: "composite",
|
|
132
|
+
base_generation: lastGen.commit_sha,
|
|
133
|
+
files,
|
|
134
|
+
patch_content: diff,
|
|
135
|
+
}];
|
|
136
|
+
}
|
|
97
137
|
parseGitLog(log) {
|
|
98
138
|
return log
|
|
99
139
|
.trim()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplayDetector.js","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReplayDetector.js","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"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"}
|
package/dist/ReplayService.d.ts
CHANGED
|
@@ -15,15 +15,12 @@ export interface ReplayReport {
|
|
|
15
15
|
patchesRepointed?: number;
|
|
16
16
|
patchesContentRebased?: number;
|
|
17
17
|
patchesKeptAsUserOwned?: number;
|
|
18
|
+
patchesConflictResolved?: number;
|
|
18
19
|
conflicts: FileResult[];
|
|
19
20
|
conflictDetails?: ConflictDetail[];
|
|
20
21
|
wouldApply?: StoredPatch[];
|
|
21
22
|
warnings?: string[];
|
|
22
23
|
}
|
|
23
|
-
export declare class ReplayConflictError extends Error {
|
|
24
|
-
readonly report: ReplayReport;
|
|
25
|
-
constructor(report: ReplayReport);
|
|
26
|
-
}
|
|
27
24
|
export interface ReplayOptions {
|
|
28
25
|
/** Log what would happen but don't modify anything */
|
|
29
26
|
dryRun?: boolean;
|
|
@@ -36,17 +33,32 @@ export interface ReplayOptions {
|
|
|
36
33
|
}
|
|
37
34
|
export declare class ReplayService {
|
|
38
35
|
private git;
|
|
39
|
-
private config;
|
|
40
36
|
private detector;
|
|
41
37
|
private applicator;
|
|
42
38
|
private committer;
|
|
43
39
|
private lockManager;
|
|
44
40
|
private outputDir;
|
|
45
|
-
constructor(outputDir: string,
|
|
41
|
+
constructor(outputDir: string, _config: ReplayConfig);
|
|
46
42
|
/**
|
|
47
43
|
* Run the full replay flow.
|
|
48
44
|
*/
|
|
49
45
|
runReplay(options?: ReplayOptions): Promise<ReplayReport>;
|
|
46
|
+
/**
|
|
47
|
+
* Sync the lockfile after a divergent PR was squash-merged.
|
|
48
|
+
*
|
|
49
|
+
* When a divergent PR is merged, the lockfile's current_generation is stale
|
|
50
|
+
* (points to gen N while the code is at gen N+1). This method:
|
|
51
|
+
* 1. Adds the real generation (from the tag) to the lockfile
|
|
52
|
+
* 2. Updates current_generation to point to it
|
|
53
|
+
* 3. Clears existing patches (superseded — they'll be re-detected via tree diff)
|
|
54
|
+
* 4. Saves the lockfile
|
|
55
|
+
*
|
|
56
|
+
* Call this BEFORE runReplay() when the CLI detects a merged divergent PR.
|
|
57
|
+
*/
|
|
58
|
+
syncFromDivergentMerge(generationCommitSha: string, options?: {
|
|
59
|
+
cliVersion?: string;
|
|
60
|
+
generatorVersions?: Record<string, string>;
|
|
61
|
+
}): Promise<void>;
|
|
50
62
|
/**
|
|
51
63
|
* Determine which flow to use based on lockfile state.
|
|
52
64
|
*/
|
|
@@ -63,33 +75,23 @@ export declare class ReplayService {
|
|
|
63
75
|
* Normal flow - apply existing patches + detect new ones.
|
|
64
76
|
*/
|
|
65
77
|
private handleNormalRegeneration;
|
|
66
|
-
/**
|
|
67
|
-
* Get the active conflict policy based on config and environment.
|
|
68
|
-
* Returns undefined when on_conflict is not configured (backwards-compatible).
|
|
69
|
-
*/
|
|
70
|
-
private getConflictPolicy;
|
|
71
|
-
/**
|
|
72
|
-
* Auto-resolve all conflict markers in conflicted files.
|
|
73
|
-
* "ours" = keep generated code, "theirs" = keep user customization.
|
|
74
|
-
*/
|
|
75
|
-
private autoResolveConflicts;
|
|
76
|
-
/**
|
|
77
|
-
* Phase 1: Resolve conflict markers on disk BEFORE rebasing.
|
|
78
|
-
* Handles keep-mine, keep-generated, and prompt policies.
|
|
79
|
-
* Must run before rebasePatches() so rebased content captures clean files.
|
|
80
|
-
*/
|
|
81
|
-
private resolveConflictsOnDisk;
|
|
82
|
-
/**
|
|
83
|
-
* Phase 2: Throw for "fail" policy AFTER lockfile save.
|
|
84
|
-
* This ensures the lockfile is preserved even when we reject the commit.
|
|
85
|
-
*/
|
|
86
|
-
private enforceConflictFailPolicy;
|
|
87
78
|
/**
|
|
88
79
|
* Rebase cleanly applied patches so they are relative to the current generation.
|
|
89
80
|
* This prevents recurring conflicts on subsequent regenerations.
|
|
90
81
|
* Returns the number of patches rebased.
|
|
91
82
|
*/
|
|
92
83
|
private rebasePatches;
|
|
84
|
+
/**
|
|
85
|
+
* Pre-generation rebase: update stale conflicted patches using the customer's
|
|
86
|
+
* resolved state. Called BEFORE commitGeneration() so that HEAD still reflects
|
|
87
|
+
* the customer's code (generator output is in the working tree, not committed).
|
|
88
|
+
*
|
|
89
|
+
* For each patch with a stale base_generation:
|
|
90
|
+
* - If conflict markers remain in HEAD → skip (not yet resolved)
|
|
91
|
+
* - If diff(currentGen, HEAD) is empty → customer reverted, remove patch
|
|
92
|
+
* - Otherwise → update patch_content, content_hash, base_generation
|
|
93
|
+
*/
|
|
94
|
+
private preGenerationRebase;
|
|
93
95
|
/**
|
|
94
96
|
* Read .fernignore patterns from the output directory.
|
|
95
97
|
* Returns an empty array if .fernignore doesn't exist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplayService.d.ts","sourceRoot":"","sources":["../src/ReplayService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReplayService.d.ts","sourceRoot":"","sources":["../src/ReplayService.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAoB,YAAY,EAAgB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExG,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,kBAAkB,GAAG,YAAY,GAAG,qBAAqB,CAAC;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC1B,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,SAAS,CAAS;gBAGtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY;IAWzB;;OAEG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAa/D;;;;;;;;;;;OAWG;IACG,sBAAsB,CACxB,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAC9E,OAAO,CAAC,IAAI,CAAC;IAwBhB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;YACW,qBAAqB;IA+BnC;;OAEG;YACW,2BAA2B;IA2DzC;;OAEG;YACW,wBAAwB;IAiEtC;;;;OAIG;YACW,aAAa;IAoG3B;;;;;;;;;OASG;YACW,mBAAmB;IAkDjC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;OAEG;IACH,OAAO,CAAC,WAAW;CAsCtB"}
|
package/dist/ReplayService.js
CHANGED
|
@@ -6,29 +6,16 @@ import { LockfileManager } from "./LockfileManager.js";
|
|
|
6
6
|
import { ReplayDetector } from "./ReplayDetector.js";
|
|
7
7
|
import { ReplayApplicator } from "./ReplayApplicator.js";
|
|
8
8
|
import { ReplayCommitter } from "./ReplayCommitter.js";
|
|
9
|
-
import { resolveConflictMarkers } from "./ConflictResolver.js";
|
|
10
|
-
import { promptForConflictResolution } from "./ConflictPrompt.js";
|
|
11
|
-
import { isCI } from "./environment.js";
|
|
12
|
-
export class ReplayConflictError extends Error {
|
|
13
|
-
report;
|
|
14
|
-
constructor(report) {
|
|
15
|
-
super(`Replay produced ${report.patchesWithConflicts} patch conflict(s)`);
|
|
16
|
-
this.name = "ReplayConflictError";
|
|
17
|
-
this.report = report;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
9
|
export class ReplayService {
|
|
21
10
|
git;
|
|
22
|
-
config;
|
|
23
11
|
detector;
|
|
24
12
|
applicator;
|
|
25
13
|
committer;
|
|
26
14
|
lockManager;
|
|
27
15
|
outputDir;
|
|
28
|
-
constructor(outputDir,
|
|
16
|
+
constructor(outputDir, _config) {
|
|
29
17
|
const git = new GitClient(outputDir);
|
|
30
18
|
this.git = git;
|
|
31
|
-
this.config = config;
|
|
32
19
|
this.outputDir = outputDir;
|
|
33
20
|
this.lockManager = new LockfileManager(outputDir);
|
|
34
21
|
this.detector = new ReplayDetector(git, this.lockManager, outputDir);
|
|
@@ -49,6 +36,38 @@ export class ReplayService {
|
|
|
49
36
|
return this.handleNormalRegeneration(options);
|
|
50
37
|
}
|
|
51
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Sync the lockfile after a divergent PR was squash-merged.
|
|
41
|
+
*
|
|
42
|
+
* When a divergent PR is merged, the lockfile's current_generation is stale
|
|
43
|
+
* (points to gen N while the code is at gen N+1). This method:
|
|
44
|
+
* 1. Adds the real generation (from the tag) to the lockfile
|
|
45
|
+
* 2. Updates current_generation to point to it
|
|
46
|
+
* 3. Clears existing patches (superseded — they'll be re-detected via tree diff)
|
|
47
|
+
* 4. Saves the lockfile
|
|
48
|
+
*
|
|
49
|
+
* Call this BEFORE runReplay() when the CLI detects a merged divergent PR.
|
|
50
|
+
*/
|
|
51
|
+
async syncFromDivergentMerge(generationCommitSha, options) {
|
|
52
|
+
const treeHash = await this.git.getTreeHash(generationCommitSha);
|
|
53
|
+
const timestamp = (await this.git.exec(["log", "-1", "--format=%aI", generationCommitSha])).trim();
|
|
54
|
+
const record = {
|
|
55
|
+
commit_sha: generationCommitSha,
|
|
56
|
+
tree_hash: treeHash,
|
|
57
|
+
timestamp,
|
|
58
|
+
cli_version: options?.cliVersion ?? "unknown",
|
|
59
|
+
generator_versions: options?.generatorVersions ?? {},
|
|
60
|
+
};
|
|
61
|
+
if (!this.lockManager.exists()) {
|
|
62
|
+
this.lockManager.initializeInMemory(record);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.lockManager.read();
|
|
66
|
+
this.lockManager.addGeneration(record);
|
|
67
|
+
this.lockManager.clearPatches();
|
|
68
|
+
}
|
|
69
|
+
this.lockManager.save();
|
|
70
|
+
}
|
|
52
71
|
/**
|
|
53
72
|
* Determine which flow to use based on lockfile state.
|
|
54
73
|
*/
|
|
@@ -120,10 +139,6 @@ export class ReplayService {
|
|
|
120
139
|
if (newPatches.length > 0) {
|
|
121
140
|
results = await this.applicator.applyPatches(newPatches);
|
|
122
141
|
}
|
|
123
|
-
// Phase 1: Resolve conflict markers on disk BEFORE rebasing.
|
|
124
|
-
// This ensures rebasePatches() captures clean file content, not
|
|
125
|
-
// content with conflict markers baked in.
|
|
126
|
-
await this.resolveConflictsOnDisk(results, newPatches, "no-patches", options, warnings);
|
|
127
142
|
// Rebase cleanly applied patches to the current generation.
|
|
128
143
|
// This prevents recurring conflicts on subsequent regenerations.
|
|
129
144
|
const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);
|
|
@@ -135,9 +150,6 @@ export class ReplayService {
|
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
this.lockManager.save();
|
|
138
|
-
// Phase 2: For "fail" policy, throw AFTER lockfile save so patches
|
|
139
|
-
// aren't lost, but BEFORE replay commit so broken code isn't committed.
|
|
140
|
-
this.enforceConflictFailPolicy(results, newPatches, "no-patches", options, warnings, rebaseCounts);
|
|
141
153
|
if (newPatches.length > 0) {
|
|
142
154
|
if (!options?.stageOnly) {
|
|
143
155
|
const appliedCount = results.filter((r) => r.status === "applied" || r.status === "conflict").length;
|
|
@@ -155,11 +167,11 @@ export class ReplayService {
|
|
|
155
167
|
* Normal flow - apply existing patches + detect new ones.
|
|
156
168
|
*/
|
|
157
169
|
async handleNormalRegeneration(options) {
|
|
158
|
-
const existingPatches = this.lockManager.getPatches();
|
|
159
|
-
const newPatches = await this.detector.detectNewPatches();
|
|
160
|
-
const warnings = [...this.detector.warnings];
|
|
161
|
-
const allPatches = [...existingPatches, ...newPatches];
|
|
162
170
|
if (options?.dryRun) {
|
|
171
|
+
const existingPatches = this.lockManager.getPatches();
|
|
172
|
+
const newPatches = await this.detector.detectNewPatches();
|
|
173
|
+
const warnings = [...this.detector.warnings];
|
|
174
|
+
const allPatches = [...existingPatches, ...newPatches];
|
|
163
175
|
return {
|
|
164
176
|
flow: "normal-regeneration",
|
|
165
177
|
patchesDetected: allPatches.length,
|
|
@@ -171,6 +183,15 @@ export class ReplayService {
|
|
|
171
183
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
172
184
|
};
|
|
173
185
|
}
|
|
186
|
+
// Pre-generation rebase: update stale conflicted patches using customer's
|
|
187
|
+
// resolved state. At this point HEAD still has the customer's code — the
|
|
188
|
+
// generator output is in the working tree but not yet committed.
|
|
189
|
+
let existingPatches = this.lockManager.getPatches();
|
|
190
|
+
const preRebaseCounts = await this.preGenerationRebase(existingPatches);
|
|
191
|
+
existingPatches = this.lockManager.getPatches();
|
|
192
|
+
const newPatches = await this.detector.detectNewPatches();
|
|
193
|
+
const warnings = [...this.detector.warnings];
|
|
194
|
+
const allPatches = [...existingPatches, ...newPatches];
|
|
174
195
|
const commitOpts = options ? {
|
|
175
196
|
cliVersion: options.cliVersion ?? "unknown",
|
|
176
197
|
generatorVersions: options.generatorVersions ?? {},
|
|
@@ -178,8 +199,6 @@ export class ReplayService {
|
|
|
178
199
|
await this.committer.commitGeneration("Update SDK", commitOpts);
|
|
179
200
|
const genRecord = await this.committer.createGenerationRecord(commitOpts);
|
|
180
201
|
const results = await this.applicator.applyPatches(allPatches);
|
|
181
|
-
// Phase 1: Resolve conflict markers on disk BEFORE rebasing.
|
|
182
|
-
await this.resolveConflictsOnDisk(results, allPatches, "normal-regeneration", options, warnings);
|
|
183
202
|
// Rebase cleanly applied patches to the current generation.
|
|
184
203
|
const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);
|
|
185
204
|
// Save lockfile BEFORE commit — lockfile is source of truth.
|
|
@@ -190,8 +209,6 @@ export class ReplayService {
|
|
|
190
209
|
}
|
|
191
210
|
}
|
|
192
211
|
this.lockManager.save();
|
|
193
|
-
// Phase 2: For "fail" policy, throw AFTER lockfile save.
|
|
194
|
-
this.enforceConflictFailPolicy(results, allPatches, "normal-regeneration", options, warnings, rebaseCounts);
|
|
195
212
|
if (options?.stageOnly) {
|
|
196
213
|
await this.committer.stageAll();
|
|
197
214
|
}
|
|
@@ -201,119 +218,7 @@ export class ReplayService {
|
|
|
201
218
|
await this.committer.commitReplay(appliedCount, allPatches);
|
|
202
219
|
}
|
|
203
220
|
}
|
|
204
|
-
return this.buildReport("normal-regeneration", allPatches, results, options, warnings, rebaseCounts);
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Get the active conflict policy based on config and environment.
|
|
208
|
-
* Returns undefined when on_conflict is not configured (backwards-compatible).
|
|
209
|
-
*/
|
|
210
|
-
getConflictPolicy() {
|
|
211
|
-
const onConflict = this.config.on_conflict;
|
|
212
|
-
if (!onConflict)
|
|
213
|
-
return undefined;
|
|
214
|
-
return isCI() ? onConflict.ci : onConflict.local;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Auto-resolve all conflict markers in conflicted files.
|
|
218
|
-
* "ours" = keep generated code, "theirs" = keep user customization.
|
|
219
|
-
*/
|
|
220
|
-
async autoResolveConflicts(results, strategy) {
|
|
221
|
-
const { join } = await import("node:path");
|
|
222
|
-
for (const result of results) {
|
|
223
|
-
if (result.status !== "conflict" || !result.fileResults)
|
|
224
|
-
continue;
|
|
225
|
-
for (const fileResult of result.fileResults) {
|
|
226
|
-
if (fileResult.status !== "conflict")
|
|
227
|
-
continue;
|
|
228
|
-
const filePath = join(this.outputDir, fileResult.file);
|
|
229
|
-
await resolveConflictMarkers(filePath, strategy);
|
|
230
|
-
fileResult.status = "merged";
|
|
231
|
-
fileResult.reason = `auto-resolved (${strategy === "ours" ? "keep-generated" : "keep-mine"})`;
|
|
232
|
-
}
|
|
233
|
-
// If all files resolved, mark the patch as applied
|
|
234
|
-
const allResolved = result.fileResults.every((f) => f.status !== "conflict");
|
|
235
|
-
if (allResolved) {
|
|
236
|
-
result.status = "applied";
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Phase 1: Resolve conflict markers on disk BEFORE rebasing.
|
|
242
|
-
* Handles keep-mine, keep-generated, and prompt policies.
|
|
243
|
-
* Must run before rebasePatches() so rebased content captures clean files.
|
|
244
|
-
*/
|
|
245
|
-
async resolveConflictsOnDisk(results, allPatches, flow, options, warnings) {
|
|
246
|
-
const hasConflicts = results.some((r) => r.status === "conflict");
|
|
247
|
-
if (!hasConflicts)
|
|
248
|
-
return;
|
|
249
|
-
const policy = this.getConflictPolicy();
|
|
250
|
-
if (policy === undefined)
|
|
251
|
-
return; // backwards-compatible: commit with markers
|
|
252
|
-
switch (policy) {
|
|
253
|
-
case "fail":
|
|
254
|
-
// Don't resolve — enforceConflictFailPolicy() handles this after lockfile save
|
|
255
|
-
break;
|
|
256
|
-
case "keep-mine":
|
|
257
|
-
await this.autoResolveConflicts(results, "theirs");
|
|
258
|
-
break;
|
|
259
|
-
case "keep-generated":
|
|
260
|
-
await this.autoResolveConflicts(results, "ours");
|
|
261
|
-
break;
|
|
262
|
-
case "prompt": {
|
|
263
|
-
if (!process.stdin.isTTY)
|
|
264
|
-
break; // No TTY → fall through to default
|
|
265
|
-
const { join } = await import("node:path");
|
|
266
|
-
for (const result of results) {
|
|
267
|
-
if (result.status !== "conflict" || !result.fileResults)
|
|
268
|
-
continue;
|
|
269
|
-
for (const fileResult of result.fileResults) {
|
|
270
|
-
if (fileResult.status !== "conflict")
|
|
271
|
-
continue;
|
|
272
|
-
const answer = await promptForConflictResolution({
|
|
273
|
-
patchId: result.patch.id,
|
|
274
|
-
patchMessage: result.patch.original_message,
|
|
275
|
-
file: fileResult,
|
|
276
|
-
});
|
|
277
|
-
if (answer === "abort") {
|
|
278
|
-
const report = this.buildReport(flow, allPatches, results, options, warnings);
|
|
279
|
-
throw new ReplayConflictError(report);
|
|
280
|
-
}
|
|
281
|
-
if (answer === "ours" || answer === "theirs") {
|
|
282
|
-
const filePath = join(this.outputDir, fileResult.file);
|
|
283
|
-
await resolveConflictMarkers(filePath, answer);
|
|
284
|
-
fileResult.status = "merged";
|
|
285
|
-
fileResult.reason = `resolved (${answer === "ours" ? "keep-generated" : "keep-mine"})`;
|
|
286
|
-
}
|
|
287
|
-
// "manual" → leave conflict markers
|
|
288
|
-
}
|
|
289
|
-
const allResolved = result.fileResults.every((f) => f.status !== "conflict");
|
|
290
|
-
if (allResolved) {
|
|
291
|
-
result.status = "applied";
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
case "create-pr":
|
|
297
|
-
// Future feature. Fall through with a warning.
|
|
298
|
-
if (warnings) {
|
|
299
|
-
warnings.push("on_conflict: create-pr is not yet implemented; conflict markers left in place");
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Phase 2: Throw for "fail" policy AFTER lockfile save.
|
|
306
|
-
* This ensures the lockfile is preserved even when we reject the commit.
|
|
307
|
-
*/
|
|
308
|
-
enforceConflictFailPolicy(results, allPatches, flow, options, warnings, rebaseCounts) {
|
|
309
|
-
const hasConflicts = results.some((r) => r.status === "conflict");
|
|
310
|
-
if (!hasConflicts)
|
|
311
|
-
return;
|
|
312
|
-
const policy = this.getConflictPolicy();
|
|
313
|
-
if (policy !== "fail")
|
|
314
|
-
return;
|
|
315
|
-
const report = this.buildReport(flow, allPatches, results, options, warnings, rebaseCounts);
|
|
316
|
-
throw new ReplayConflictError(report);
|
|
221
|
+
return this.buildReport("normal-regeneration", allPatches, results, options, warnings, rebaseCounts, preRebaseCounts);
|
|
317
222
|
}
|
|
318
223
|
/**
|
|
319
224
|
* Rebase cleanly applied patches so they are relative to the current generation.
|
|
@@ -380,9 +285,8 @@ export class ReplayService {
|
|
|
380
285
|
]).catch(() => null);
|
|
381
286
|
if (!diff || !diff.trim()) {
|
|
382
287
|
// Patch is now empty — customization was absorbed by the generator.
|
|
383
|
-
// Only removePatch (not absorbedPatchIds) — new patches that haven't
|
|
384
|
-
// been added yet are correctly ignored by the no-op removePatch.
|
|
385
288
|
this.lockManager.removePatch(patch.id);
|
|
289
|
+
absorbedPatchIds.add(patch.id);
|
|
386
290
|
absorbed++;
|
|
387
291
|
continue;
|
|
388
292
|
}
|
|
@@ -410,6 +314,55 @@ export class ReplayService {
|
|
|
410
314
|
}
|
|
411
315
|
return { absorbed, repointed, contentRebased, keptAsUserOwned, absorbedPatchIds };
|
|
412
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Pre-generation rebase: update stale conflicted patches using the customer's
|
|
319
|
+
* resolved state. Called BEFORE commitGeneration() so that HEAD still reflects
|
|
320
|
+
* the customer's code (generator output is in the working tree, not committed).
|
|
321
|
+
*
|
|
322
|
+
* For each patch with a stale base_generation:
|
|
323
|
+
* - If conflict markers remain in HEAD → skip (not yet resolved)
|
|
324
|
+
* - If diff(currentGen, HEAD) is empty → customer reverted, remove patch
|
|
325
|
+
* - Otherwise → update patch_content, content_hash, base_generation
|
|
326
|
+
*/
|
|
327
|
+
async preGenerationRebase(patches) {
|
|
328
|
+
const lock = this.lockManager.read();
|
|
329
|
+
const currentGen = lock.current_generation;
|
|
330
|
+
let conflictResolved = 0;
|
|
331
|
+
let conflictAbsorbed = 0;
|
|
332
|
+
for (const patch of patches) {
|
|
333
|
+
if (patch.base_generation === currentGen)
|
|
334
|
+
continue;
|
|
335
|
+
try {
|
|
336
|
+
// Check for unresolved conflict markers in HEAD
|
|
337
|
+
const markerFiles = await this.git.exec(["grep", "-l", "<<<<<<<", "HEAD", "--", ...patch.files]).catch(() => "");
|
|
338
|
+
if (markerFiles.trim())
|
|
339
|
+
continue;
|
|
340
|
+
// Compute customer's resolved state relative to current generation
|
|
341
|
+
const diff = await this.git.exec(["diff", currentGen, "HEAD", "--", ...patch.files]).catch(() => null);
|
|
342
|
+
// Diff command failed (e.g., invalid SHA) — skip this patch
|
|
343
|
+
if (diff === null)
|
|
344
|
+
continue;
|
|
345
|
+
if (!diff.trim()) {
|
|
346
|
+
// Empty diff — customer reverted their customization
|
|
347
|
+
this.lockManager.removePatch(patch.id);
|
|
348
|
+
conflictAbsorbed++;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
// Update the patch with the resolved content
|
|
352
|
+
const newContentHash = this.detector.computeContentHash(diff);
|
|
353
|
+
this.lockManager.updatePatch(patch.id, {
|
|
354
|
+
base_generation: currentGen,
|
|
355
|
+
patch_content: diff,
|
|
356
|
+
content_hash: newContentHash,
|
|
357
|
+
});
|
|
358
|
+
conflictResolved++;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// If anything fails, leave the patch unchanged
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return { conflictResolved, conflictAbsorbed };
|
|
365
|
+
}
|
|
413
366
|
/**
|
|
414
367
|
* Read .fernignore patterns from the output directory.
|
|
415
368
|
* Returns an empty array if .fernignore doesn't exist.
|
|
@@ -426,7 +379,7 @@ export class ReplayService {
|
|
|
426
379
|
/**
|
|
427
380
|
* Build the final report from results.
|
|
428
381
|
*/
|
|
429
|
-
buildReport(flow, patches, results, options, warnings, rebaseCounts) {
|
|
382
|
+
buildReport(flow, patches, results, options, warnings, rebaseCounts, preRebaseCounts) {
|
|
430
383
|
const conflictResults = results.filter((r) => r.status === "conflict");
|
|
431
384
|
const conflictDetails = conflictResults
|
|
432
385
|
.map((r) => ({
|
|
@@ -446,6 +399,8 @@ export class ReplayService {
|
|
|
446
399
|
patchesRepointed: rebaseCounts && rebaseCounts.repointed > 0 ? rebaseCounts.repointed : undefined,
|
|
447
400
|
patchesContentRebased: rebaseCounts && rebaseCounts.contentRebased > 0 ? rebaseCounts.contentRebased : undefined,
|
|
448
401
|
patchesKeptAsUserOwned: rebaseCounts && rebaseCounts.keptAsUserOwned > 0 ? rebaseCounts.keptAsUserOwned : undefined,
|
|
402
|
+
patchesConflictResolved: preRebaseCounts && (preRebaseCounts.conflictResolved + preRebaseCounts.conflictAbsorbed) > 0
|
|
403
|
+
? preRebaseCounts.conflictResolved + preRebaseCounts.conflictAbsorbed : undefined,
|
|
449
404
|
conflicts: conflictResults
|
|
450
405
|
.flatMap((r) => r.fileResults?.filter((f) => f.status === "conflict") ?? []),
|
|
451
406
|
conflictDetails: conflictDetails.length > 0 ? conflictDetails : undefined,
|