@fern-api/generator-cli 0.6.9 → 0.6.11
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/api.d.ts +15 -19
- package/dist/api.js +124 -356
- package/dist/cli.js +612 -414
- package/package.json +2 -2
package/dist/api.js
CHANGED
|
@@ -893,13 +893,11 @@ __export(api_exports2, {
|
|
|
893
893
|
githubRelease: () => githubRelease,
|
|
894
894
|
logReplaySummary: () => logReplaySummary,
|
|
895
895
|
replayInit: () => replayInit,
|
|
896
|
+
replayResolve: () => replayResolve,
|
|
896
897
|
replayRun: () => replayRun
|
|
897
898
|
});
|
|
898
899
|
module.exports = __toCommonJS(api_exports2);
|
|
899
900
|
|
|
900
|
-
// src/api/generate-readme.ts
|
|
901
|
-
var import_node_stream = require("stream");
|
|
902
|
-
|
|
903
901
|
// ../commons/github/lib/ClonedRepository.js
|
|
904
902
|
var import_promises = require("fs/promises");
|
|
905
903
|
var import_path = __toESM(require("path"), 1);
|
|
@@ -8256,7 +8254,7 @@ var StreamWriter = class extends Writer {
|
|
|
8256
8254
|
this.stream = stream;
|
|
8257
8255
|
}
|
|
8258
8256
|
async write(content) {
|
|
8259
|
-
return new Promise((
|
|
8257
|
+
return new Promise((resolve4, reject) => {
|
|
8260
8258
|
const handleError = (err) => {
|
|
8261
8259
|
this.stream.removeListener("error", handleError);
|
|
8262
8260
|
reject(err);
|
|
@@ -8267,12 +8265,12 @@ var StreamWriter = class extends Writer {
|
|
|
8267
8265
|
if (error) {
|
|
8268
8266
|
reject(error);
|
|
8269
8267
|
} else {
|
|
8270
|
-
|
|
8268
|
+
resolve4();
|
|
8271
8269
|
}
|
|
8272
8270
|
});
|
|
8273
8271
|
this.stream.once("error", handleError);
|
|
8274
8272
|
if (!canWrite) {
|
|
8275
|
-
this.stream.once("drain",
|
|
8273
|
+
this.stream.once("drain", resolve4);
|
|
8276
8274
|
}
|
|
8277
8275
|
} else {
|
|
8278
8276
|
const canWrite = this.stream.write(content);
|
|
@@ -8280,26 +8278,26 @@ var StreamWriter = class extends Writer {
|
|
|
8280
8278
|
if (!canWrite) {
|
|
8281
8279
|
this.stream.once("drain", () => {
|
|
8282
8280
|
this.stream.removeListener("error", handleError);
|
|
8283
|
-
|
|
8281
|
+
resolve4();
|
|
8284
8282
|
});
|
|
8285
8283
|
} else {
|
|
8286
8284
|
this.stream.removeListener("error", handleError);
|
|
8287
|
-
|
|
8285
|
+
resolve4();
|
|
8288
8286
|
}
|
|
8289
8287
|
}
|
|
8290
8288
|
});
|
|
8291
8289
|
}
|
|
8292
8290
|
end() {
|
|
8293
|
-
return new Promise((
|
|
8291
|
+
return new Promise((resolve4, reject) => {
|
|
8294
8292
|
if (this.stream instanceof import_fs.default.WriteStream) {
|
|
8295
8293
|
this.stream.end(() => {
|
|
8296
|
-
|
|
8294
|
+
resolve4();
|
|
8297
8295
|
});
|
|
8298
8296
|
this.stream.once("error", reject);
|
|
8299
|
-
this.stream.once("finish",
|
|
8297
|
+
this.stream.once("finish", resolve4);
|
|
8300
8298
|
} else {
|
|
8301
8299
|
this.stream.write("", () => {
|
|
8302
|
-
|
|
8300
|
+
resolve4();
|
|
8303
8301
|
});
|
|
8304
8302
|
}
|
|
8305
8303
|
});
|
|
@@ -8388,7 +8386,8 @@ var BlockList = class {
|
|
|
8388
8386
|
}
|
|
8389
8387
|
addBlock(block) {
|
|
8390
8388
|
if (this.hasBlock(block.id)) {
|
|
8391
|
-
|
|
8389
|
+
console.warn(`[readme] Duplicate block with id "${block.id}" detected; overriding with latest content`);
|
|
8390
|
+
this.blocks = this.blocks.filter((b) => b.id !== block.id);
|
|
8392
8391
|
}
|
|
8393
8392
|
this.ids.add(block.id);
|
|
8394
8393
|
this.blocks.push(block);
|
|
@@ -8402,7 +8401,9 @@ var BlockList = class {
|
|
|
8402
8401
|
// }
|
|
8403
8402
|
insertBlock(block, precedingBlockId) {
|
|
8404
8403
|
if (this.hasBlock(block.id)) {
|
|
8405
|
-
|
|
8404
|
+
console.warn(`[readme] Duplicate block with id "${block.id}" detected; overriding with latest content`);
|
|
8405
|
+
this.blocks = this.blocks.filter((b) => b.id !== block.id);
|
|
8406
|
+
this.ids.delete(block.id);
|
|
8406
8407
|
}
|
|
8407
8408
|
if (precedingBlockId === void 0) {
|
|
8408
8409
|
this.addBlock(block);
|
|
@@ -8453,16 +8454,25 @@ var ReadmeGenerator = class {
|
|
|
8453
8454
|
this.apiName = this.readmeConfig.apiName ?? this.organizationPascalCase;
|
|
8454
8455
|
}
|
|
8455
8456
|
async generateReadme({ output }) {
|
|
8457
|
+
const writer = new StreamWriter(output);
|
|
8458
|
+
await this.writeReadmeContent({ writer });
|
|
8459
|
+
await writer.end();
|
|
8460
|
+
}
|
|
8461
|
+
async generateReadmeToString() {
|
|
8462
|
+
const writer = new StringWriter();
|
|
8463
|
+
await this.writeReadmeContent({ writer });
|
|
8464
|
+
await writer.end();
|
|
8465
|
+
return writer.toString();
|
|
8466
|
+
}
|
|
8467
|
+
async writeReadmeContent({ writer }) {
|
|
8456
8468
|
const blocks = await this.generateBlocks();
|
|
8457
8469
|
const mergedBlocks = await this.mergeBlocks({ blocks });
|
|
8458
|
-
const writer = new StreamWriter(output);
|
|
8459
8470
|
await this.writeHeader({ writer });
|
|
8460
8471
|
await this.writeTableOfContents({ writer, blocks: mergedBlocks });
|
|
8461
8472
|
await this.writeBlocks({
|
|
8462
8473
|
writer,
|
|
8463
8474
|
blocks: mergedBlocks
|
|
8464
8475
|
});
|
|
8465
|
-
await writer.end();
|
|
8466
8476
|
}
|
|
8467
8477
|
async generateBlocks() {
|
|
8468
8478
|
const blocks = [];
|
|
@@ -9334,28 +9344,14 @@ async function generateReadmeToStream(params) {
|
|
|
9334
9344
|
}
|
|
9335
9345
|
async function generateReadme(params) {
|
|
9336
9346
|
const { originalReadmeContent, readmeConfig } = params;
|
|
9337
|
-
const
|
|
9338
|
-
|
|
9339
|
-
write(chunk, _encoding, callback) {
|
|
9340
|
-
if (typeof chunk === "string") {
|
|
9341
|
-
content.push(chunk);
|
|
9342
|
-
} else {
|
|
9343
|
-
content.push(Buffer.from(chunk).toString("utf8"));
|
|
9344
|
-
}
|
|
9345
|
-
callback();
|
|
9346
|
-
}
|
|
9347
|
-
});
|
|
9348
|
-
await generateReadmeToStream({
|
|
9349
|
-
originalReadmeContent,
|
|
9347
|
+
const generator = new ReadmeGenerator({
|
|
9348
|
+
readmeParser: new ReadmeParser(),
|
|
9350
9349
|
readmeConfig,
|
|
9351
|
-
|
|
9350
|
+
originalReadme: originalReadmeContent
|
|
9352
9351
|
});
|
|
9353
|
-
return
|
|
9352
|
+
return generator.generateReadmeToString();
|
|
9354
9353
|
}
|
|
9355
9354
|
|
|
9356
|
-
// src/api/generate-reference.ts
|
|
9357
|
-
var import_node_stream2 = require("stream");
|
|
9358
|
-
|
|
9359
9355
|
// src/reference/ReferenceGenerator.ts
|
|
9360
9356
|
var ReferenceGenerator = class {
|
|
9361
9357
|
referenceConfig;
|
|
@@ -9366,6 +9362,16 @@ var ReferenceGenerator = class {
|
|
|
9366
9362
|
}
|
|
9367
9363
|
async generate({ output }) {
|
|
9368
9364
|
const writer = new StreamWriter(output);
|
|
9365
|
+
await this.writeReferenceContent({ writer });
|
|
9366
|
+
await writer.end();
|
|
9367
|
+
}
|
|
9368
|
+
async generateToString() {
|
|
9369
|
+
const writer = new StringWriter();
|
|
9370
|
+
await this.writeReferenceContent({ writer });
|
|
9371
|
+
await writer.end();
|
|
9372
|
+
return writer.toString();
|
|
9373
|
+
}
|
|
9374
|
+
async writeReferenceContent({ writer }) {
|
|
9369
9375
|
await writer.writeLine("# Reference");
|
|
9370
9376
|
if (this.referenceConfig.rootSection != null) {
|
|
9371
9377
|
await this.writeRootSection({
|
|
@@ -9376,7 +9382,6 @@ var ReferenceGenerator = class {
|
|
|
9376
9382
|
for (const section of this.referenceConfig.sections) {
|
|
9377
9383
|
await this.writeSection({ section, writer });
|
|
9378
9384
|
}
|
|
9379
|
-
await writer.end();
|
|
9380
9385
|
}
|
|
9381
9386
|
async writeRootSection({
|
|
9382
9387
|
section,
|
|
@@ -9477,22 +9482,8 @@ async function generateReferenceToStream(params) {
|
|
|
9477
9482
|
}
|
|
9478
9483
|
async function generateReference(params) {
|
|
9479
9484
|
const { referenceConfig } = params;
|
|
9480
|
-
const
|
|
9481
|
-
|
|
9482
|
-
write(chunk, _encoding, callback) {
|
|
9483
|
-
if (typeof chunk === "string") {
|
|
9484
|
-
content.push(chunk);
|
|
9485
|
-
} else {
|
|
9486
|
-
content.push(Buffer.from(chunk).toString("utf8"));
|
|
9487
|
-
}
|
|
9488
|
-
callback();
|
|
9489
|
-
}
|
|
9490
|
-
});
|
|
9491
|
-
await generateReferenceToStream({
|
|
9492
|
-
referenceConfig,
|
|
9493
|
-
outputStream: sink
|
|
9494
|
-
});
|
|
9495
|
-
return content.join("");
|
|
9485
|
+
const generator = new ReferenceGenerator({ referenceConfig });
|
|
9486
|
+
return generator.generateToString();
|
|
9496
9487
|
}
|
|
9497
9488
|
|
|
9498
9489
|
// ../commons/path-utils/lib/convertToOsPath.js
|
|
@@ -9686,41 +9677,19 @@ var import_path3 = require("path");
|
|
|
9686
9677
|
|
|
9687
9678
|
// src/pipeline/github/createReplayBranch.ts
|
|
9688
9679
|
async function createReplayBranch(repository, branchName, commitMessage, replayConflictInfo, logger) {
|
|
9689
|
-
if (replayConflictInfo
|
|
9690
|
-
const parentSha = replayConflictInfo.previousGenerationSha;
|
|
9691
|
-
const lockfileRestoreSha = replayConflictInfo.baseBranchHead ?? replayConflictInfo.previousGenerationSha;
|
|
9692
|
-
logger.debug(`Creating divergent PR branch with generation tree (parent: ${parentSha.substring(0, 7)})`);
|
|
9693
|
-
const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
|
|
9694
|
-
const syntheticCommitSha = await repository.commitTree(
|
|
9695
|
-
genTreeHash,
|
|
9696
|
-
parentSha,
|
|
9697
|
-
`[fern-generated] ${commitMessage ?? "Update SDK"}`
|
|
9698
|
-
);
|
|
9699
|
-
await repository.createBranchFromCommit(branchName, syntheticCommitSha);
|
|
9700
|
-
try {
|
|
9701
|
-
await repository.restoreFilesFromCommit(lockfileRestoreSha, ".fern/replay.lock");
|
|
9702
|
-
await repository.commitAllChanges(`[fern-generated] ${commitMessage ?? "Update SDK"}`);
|
|
9703
|
-
} catch (error) {
|
|
9704
|
-
logger.debug(
|
|
9705
|
-
`Could not restore lockfile from base branch: ${error instanceof Error ? error.message : String(error)}`
|
|
9706
|
-
);
|
|
9707
|
-
}
|
|
9708
|
-
return syntheticCommitSha;
|
|
9709
|
-
} else if (replayConflictInfo != null) {
|
|
9710
|
-
const parentSha = replayConflictInfo.previousGenerationSha;
|
|
9711
|
-
logger.debug(`Creating linear PR branch from HEAD (no conflicts)`);
|
|
9712
|
-
await repository.createBranchFromHead(branchName);
|
|
9713
|
-
const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
|
|
9714
|
-
const tagCommitSha = await repository.commitTree(
|
|
9715
|
-
genTreeHash,
|
|
9716
|
-
parentSha,
|
|
9717
|
-
`[fern-generated] ${commitMessage ?? "Update SDK"}`
|
|
9718
|
-
);
|
|
9719
|
-
return tagCommitSha;
|
|
9720
|
-
} else {
|
|
9680
|
+
if (replayConflictInfo == null) {
|
|
9721
9681
|
await repository.createBranchFromHead(branchName);
|
|
9722
9682
|
return void 0;
|
|
9723
9683
|
}
|
|
9684
|
+
const parentSha = replayConflictInfo.previousGenerationSha;
|
|
9685
|
+
await repository.createBranchFromHead(branchName);
|
|
9686
|
+
const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
|
|
9687
|
+
const tagCommitSha = await repository.commitTree(
|
|
9688
|
+
genTreeHash,
|
|
9689
|
+
parentSha,
|
|
9690
|
+
`[fern-generated] ${commitMessage ?? "Update SDK"}`
|
|
9691
|
+
);
|
|
9692
|
+
return tagCommitSha;
|
|
9724
9693
|
}
|
|
9725
9694
|
|
|
9726
9695
|
// src/pipeline/github/findExistingUpdatablePR.ts
|
|
@@ -9815,11 +9784,11 @@ function formatConflictReason(reason) {
|
|
|
9815
9784
|
case "same-line-edit":
|
|
9816
9785
|
return "The new generation changed the same lines you edited";
|
|
9817
9786
|
case "new-file-both":
|
|
9818
|
-
return "
|
|
9787
|
+
return "Both the generator and your customization created this file";
|
|
9819
9788
|
case "base-generation-mismatch":
|
|
9820
|
-
return "
|
|
9789
|
+
return "This customization was from a previous generation";
|
|
9821
9790
|
case "patch-apply-failed":
|
|
9822
|
-
return "
|
|
9791
|
+
return "The customization could not be applied to the new code";
|
|
9823
9792
|
default:
|
|
9824
9793
|
return "Your edit overlaps with changes in the new generation";
|
|
9825
9794
|
}
|
|
@@ -9828,7 +9797,7 @@ function patchDescription(detail) {
|
|
|
9828
9797
|
if (detail.patchMessage && detail.patchMessage !== "update") {
|
|
9829
9798
|
return detail.patchMessage;
|
|
9830
9799
|
}
|
|
9831
|
-
const firstFile = detail.files[0]
|
|
9800
|
+
const firstFile = detail.files[0];
|
|
9832
9801
|
return firstFile != null ? `changes in ${firstFile}` : "customization";
|
|
9833
9802
|
}
|
|
9834
9803
|
function logReplaySummary(result, logger) {
|
|
@@ -9837,10 +9806,10 @@ function logReplaySummary(result, logger) {
|
|
|
9837
9806
|
}
|
|
9838
9807
|
const applied = result.patchesApplied ?? 0;
|
|
9839
9808
|
const absorbed = result.patchesAbsorbed ?? 0;
|
|
9840
|
-
const
|
|
9809
|
+
const unresolvedCount = result.unresolvedPatches?.length ?? 0;
|
|
9841
9810
|
const preserved = applied - absorbed;
|
|
9842
9811
|
logger.debug(
|
|
9843
|
-
`Replay: flow=${result.flow}, detected=${result.patchesDetected ?? 0}, applied=${applied}, absorbed=${absorbed},
|
|
9812
|
+
`Replay: flow=${result.flow}, detected=${result.patchesDetected ?? 0}, applied=${applied}, absorbed=${absorbed}, unresolved=${unresolvedCount}`
|
|
9844
9813
|
);
|
|
9845
9814
|
if (preserved > 0) {
|
|
9846
9815
|
const absorbedNote = absorbed > 0 ? ` (some customizations now part of generated code)` : "";
|
|
@@ -9848,14 +9817,17 @@ function logReplaySummary(result, logger) {
|
|
|
9848
9817
|
} else if (absorbed > 0) {
|
|
9849
9818
|
logger.info(`Replay: customizations now part of generated code`);
|
|
9850
9819
|
}
|
|
9851
|
-
if (
|
|
9852
|
-
const totalFiles = (result.
|
|
9820
|
+
if (unresolvedCount > 0) {
|
|
9821
|
+
const totalFiles = (result.unresolvedPatches ?? []).reduce(
|
|
9822
|
+
(sum, patch) => sum + patch.conflictDetails.length,
|
|
9823
|
+
0
|
|
9824
|
+
);
|
|
9853
9825
|
logger.warn(
|
|
9854
|
-
`Replay: ${plural(totalFiles, "file")} ${totalFiles === 1 ? "has" : "have"}
|
|
9826
|
+
`Replay: ${plural(totalFiles, "file")} ${totalFiles === 1 ? "has" : "have"} unresolved conflicts \u2014 resolve via \`fern replay resolve\``
|
|
9855
9827
|
);
|
|
9856
|
-
for (const
|
|
9857
|
-
logger.warn(` "${patchDescription(
|
|
9858
|
-
for (const file of
|
|
9828
|
+
for (const patch of result.unresolvedPatches ?? []) {
|
|
9829
|
+
logger.warn(` "${patchDescription(patch)}":`);
|
|
9830
|
+
for (const file of patch.conflictDetails) {
|
|
9859
9831
|
logger.warn(` ${file.file} \u2014 ${formatConflictReason(file.conflictReason)}`);
|
|
9860
9832
|
}
|
|
9861
9833
|
}
|
|
@@ -9870,70 +9842,66 @@ function formatReplayPrBody(result, options) {
|
|
|
9870
9842
|
}
|
|
9871
9843
|
const applied = result.patchesApplied ?? 0;
|
|
9872
9844
|
const absorbed = result.patchesAbsorbed ?? 0;
|
|
9873
|
-
const
|
|
9845
|
+
const unresolvedPatches = result.unresolvedPatches ?? [];
|
|
9846
|
+
const hasUnresolved = unresolvedPatches.length > 0;
|
|
9874
9847
|
const preserved = applied - absorbed;
|
|
9875
|
-
if (preserved === 0 &&
|
|
9848
|
+
if (preserved === 0 && !hasUnresolved) {
|
|
9876
9849
|
return void 0;
|
|
9877
9850
|
}
|
|
9878
9851
|
const parts = [];
|
|
9879
|
-
if (preserved > 0 &&
|
|
9852
|
+
if (preserved > 0 && !hasUnresolved) {
|
|
9880
9853
|
parts.push(`\u2705 Customizations automatically preserved in this update.`);
|
|
9881
9854
|
} else if (preserved > 0) {
|
|
9855
|
+
const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
|
|
9882
9856
|
parts.push(
|
|
9883
|
-
`\u2705 Customizations automatically preserved, but ${plural(
|
|
9857
|
+
`\u2705 Customizations automatically preserved, but ${plural(totalFiles, "file")} ${totalFiles === 1 ? "needs" : "need"} your attention.`
|
|
9884
9858
|
);
|
|
9885
9859
|
}
|
|
9886
|
-
if (
|
|
9887
|
-
const
|
|
9888
|
-
const totalFiles = allFiles.length;
|
|
9860
|
+
if (hasUnresolved) {
|
|
9861
|
+
const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
|
|
9889
9862
|
parts.push(`
|
|
9890
|
-
###
|
|
9863
|
+
### Action required: ${plural(totalFiles, "file")} with unresolved customization conflicts
|
|
9891
9864
|
`);
|
|
9892
9865
|
parts.push(
|
|
9893
|
-
`
|
|
9894
|
-
`
|
|
9895
|
-
);
|
|
9896
|
-
parts.push(
|
|
9897
|
-
`> \u26A0\uFE0F **Label guide:** In GitHub's conflict editor, \`<<<<<<< HEAD\` ("Accept current") = new generated code. \`>>>>>>> main\` ("Accept incoming") = your customization.
|
|
9866
|
+
`The new generation changed code you previously customized. Non-conflicting customizations have been applied automatically. The following files need manual resolution:
|
|
9898
9867
|
`
|
|
9899
9868
|
);
|
|
9900
9869
|
parts.push(`| File | Your customization | Why it conflicted |`);
|
|
9901
9870
|
parts.push(`|------|-------------------|-------------------|`);
|
|
9902
|
-
for (const
|
|
9903
|
-
const description = patchDescription(
|
|
9904
|
-
for (const
|
|
9905
|
-
parts.push(`| \`${
|
|
9871
|
+
for (const patch of unresolvedPatches) {
|
|
9872
|
+
const description = patchDescription(patch);
|
|
9873
|
+
for (const file of patch.conflictDetails) {
|
|
9874
|
+
parts.push(`| \`${file.file}\` | ${description} | ${formatConflictReason(file.conflictReason)} |`);
|
|
9906
9875
|
}
|
|
9907
9876
|
}
|
|
9908
9877
|
const branch = options?.branchName ?? "<branch-name>";
|
|
9878
|
+
const repoUri = options?.repoUri;
|
|
9879
|
+
const repoName = repoUri?.split("/").pop() ?? "<repo>";
|
|
9909
9880
|
parts.push(`
|
|
9910
|
-
#### How to
|
|
9911
|
-
`);
|
|
9912
|
-
parts.push(`**Option A: Resolve in GitHub (recommended for simple conflicts)**
|
|
9913
|
-
`);
|
|
9914
|
-
parts.push(`1. Click the **Resolve conflicts** button below`);
|
|
9915
|
-
parts.push(`2. For each file, choose which code to keep:`);
|
|
9916
|
-
parts.push(` - \`<<<<<<< HEAD\` / "Accept current changes" = **new generated code**`);
|
|
9917
|
-
parts.push(` - \`>>>>>>> main\` / "Accept incoming changes" = **your customization**`);
|
|
9918
|
-
parts.push(` - Or manually combine both, then delete the conflict markers`);
|
|
9919
|
-
parts.push(`3. Click **Mark as resolved** for each file, then **Commit merge**
|
|
9920
|
-
`);
|
|
9921
|
-
parts.push(`**Option B: Resolve locally**
|
|
9881
|
+
#### How to resolve
|
|
9922
9882
|
`);
|
|
9923
|
-
parts.push(`1.
|
|
9883
|
+
parts.push(`1. Check out this branch:`);
|
|
9924
9884
|
parts.push(` \`\`\`sh`);
|
|
9925
|
-
parts.push(` git fetch origin && git checkout ${branch}`);
|
|
9885
|
+
parts.push(` git fetch origin && git checkout -b ${branch} origin/${branch}`);
|
|
9926
9886
|
parts.push(` \`\`\``);
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9887
|
+
if (repoUri != null) {
|
|
9888
|
+
parts.push(` Or if you don't have the repo cloned:`);
|
|
9889
|
+
parts.push(` \`\`\`sh`);
|
|
9890
|
+
parts.push(
|
|
9891
|
+
` git clone https://github.com/${repoUri}.git && cd ${repoName} && git checkout -b ${branch} origin/${branch}`
|
|
9892
|
+
);
|
|
9893
|
+
parts.push(` \`\`\``);
|
|
9894
|
+
}
|
|
9895
|
+
parts.push(`2. Run: \`fern replay resolve\``);
|
|
9896
|
+
parts.push(`3. Open the conflicting files in your editor \u2014 you'll see standard merge conflict markers`);
|
|
9897
|
+
parts.push(`4. Resolve using your editor's merge tools (VS Code, IntelliJ, etc.)`);
|
|
9898
|
+
parts.push(`5. Run: \`fern replay resolve\` again to finalize`);
|
|
9899
|
+
parts.push(`6. Push your changes
|
|
9935
9900
|
`);
|
|
9936
|
-
parts.push(`Your resolved
|
|
9901
|
+
parts.push(`Your resolved customizations will be remembered on future SDK generations.`);
|
|
9902
|
+
parts.push(
|
|
9903
|
+
`If you merge this PR without resolving, your unresolved customizations will conflict again on the next generation.`
|
|
9904
|
+
);
|
|
9937
9905
|
}
|
|
9938
9906
|
return parts.join("\n");
|
|
9939
9907
|
}
|
|
@@ -10069,16 +10037,9 @@ var GithubStep = class extends BaseStep {
|
|
|
10069
10037
|
}
|
|
10070
10038
|
}
|
|
10071
10039
|
}
|
|
10072
|
-
if (!this.config.previewMode) {
|
|
10073
|
-
const headSha = await repository.getHeadSha();
|
|
10074
|
-
await this.postReplayConflictStatus(octokit, owner, repo, headSha, replayConflictInfo, replayResult);
|
|
10075
|
-
if (isUpdatingExistingPR && existingPR != null) {
|
|
10076
|
-
await this.togglePrDraftState(octokit, existingPR, replayConflictInfo);
|
|
10077
|
-
}
|
|
10078
|
-
}
|
|
10079
10040
|
const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
|
|
10080
10041
|
const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage);
|
|
10081
|
-
const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch });
|
|
10042
|
+
const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch, repoUri: this.config.uri });
|
|
10082
10043
|
const enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody;
|
|
10083
10044
|
if (isUpdatingExistingPR && existingPR != null) {
|
|
10084
10045
|
this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`);
|
|
@@ -10100,7 +10061,6 @@ var GithubStep = class extends BaseStep {
|
|
|
10100
10061
|
}
|
|
10101
10062
|
} else {
|
|
10102
10063
|
const head = `${owner}:${prBranch}`;
|
|
10103
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10104
10064
|
try {
|
|
10105
10065
|
const { data: pullRequest } = await octokit.pulls.create({
|
|
10106
10066
|
owner,
|
|
@@ -10108,8 +10068,7 @@ var GithubStep = class extends BaseStep {
|
|
|
10108
10068
|
title: prTitle,
|
|
10109
10069
|
body: enrichedBody,
|
|
10110
10070
|
head,
|
|
10111
|
-
base: baseBranch
|
|
10112
|
-
draft: hasConflicts
|
|
10071
|
+
base: baseBranch
|
|
10113
10072
|
});
|
|
10114
10073
|
this.logger.info(`Created pull request: ${pullRequest.html_url}`);
|
|
10115
10074
|
result.prUrl = pullRequest.html_url;
|
|
@@ -10123,17 +10082,6 @@ var GithubStep = class extends BaseStep {
|
|
|
10123
10082
|
}
|
|
10124
10083
|
}
|
|
10125
10084
|
}
|
|
10126
|
-
if (!this.config.previewMode && result.prNumber != null && replayConflictInfo?.hasConflicts === true && replayResult != null) {
|
|
10127
|
-
await this.postWebEditorFallbackComment(
|
|
10128
|
-
octokit,
|
|
10129
|
-
owner,
|
|
10130
|
-
repo,
|
|
10131
|
-
result.prNumber,
|
|
10132
|
-
prBranch,
|
|
10133
|
-
baseBranch,
|
|
10134
|
-
replayResult
|
|
10135
|
-
);
|
|
10136
|
-
}
|
|
10137
10085
|
return result;
|
|
10138
10086
|
}
|
|
10139
10087
|
async executePushMode(repository) {
|
|
@@ -10179,198 +10127,17 @@ var GithubStep = class extends BaseStep {
|
|
|
10179
10127
|
if (replayResult == null) {
|
|
10180
10128
|
return void 0;
|
|
10181
10129
|
}
|
|
10182
|
-
const hasConflicts = (replayResult.patchesWithConflicts ?? 0) > 0;
|
|
10183
10130
|
const previousGenerationSha = replayResult.previousGenerationSha;
|
|
10184
10131
|
const currentGenerationSha = replayResult.currentGenerationSha;
|
|
10185
10132
|
if (previousGenerationSha != null && currentGenerationSha != null) {
|
|
10186
10133
|
return {
|
|
10187
10134
|
previousGenerationSha,
|
|
10188
|
-
currentGenerationSha
|
|
10189
|
-
hasConflicts,
|
|
10190
|
-
baseBranchHead: replayResult.baseBranchHead
|
|
10135
|
+
currentGenerationSha
|
|
10191
10136
|
};
|
|
10192
10137
|
}
|
|
10193
10138
|
return void 0;
|
|
10194
10139
|
}
|
|
10195
|
-
async togglePrDraftState(octokit, existingPR, replayConflictInfo) {
|
|
10196
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10197
|
-
if (hasConflicts && !existingPR.isDraft) {
|
|
10198
|
-
await this.convertPrToDraft(octokit, existingPR.nodeId, existingPR.number);
|
|
10199
|
-
} else if (!hasConflicts && existingPR.isDraft) {
|
|
10200
|
-
await this.markPrReady(octokit, existingPR.nodeId, existingPR.number);
|
|
10201
|
-
}
|
|
10202
|
-
}
|
|
10203
|
-
async convertPrToDraft(octokit, nodeId, prNumber) {
|
|
10204
|
-
try {
|
|
10205
|
-
await octokit.graphql(
|
|
10206
|
-
`mutation($id: ID!) {
|
|
10207
|
-
convertPullRequestToDraft(input: {pullRequestId: $id}) {
|
|
10208
|
-
pullRequest { isDraft }
|
|
10209
|
-
}
|
|
10210
|
-
}`,
|
|
10211
|
-
{ id: nodeId }
|
|
10212
|
-
);
|
|
10213
|
-
this.logger.info(`Converted PR #${prNumber} to draft due to replay conflicts`);
|
|
10214
|
-
} catch (error) {
|
|
10215
|
-
this.logger.debug(
|
|
10216
|
-
`Could not convert PR to draft: ${error instanceof Error ? error.message : String(error)}`
|
|
10217
|
-
);
|
|
10218
|
-
}
|
|
10219
|
-
}
|
|
10220
|
-
async markPrReady(octokit, nodeId, prNumber) {
|
|
10221
|
-
try {
|
|
10222
|
-
await octokit.graphql(
|
|
10223
|
-
`mutation($id: ID!) {
|
|
10224
|
-
markPullRequestReadyForReview(input: {pullRequestId: $id}) {
|
|
10225
|
-
pullRequest { isDraft }
|
|
10226
|
-
}
|
|
10227
|
-
}`,
|
|
10228
|
-
{ id: nodeId }
|
|
10229
|
-
);
|
|
10230
|
-
this.logger.info(`Marked PR #${prNumber} as ready (conflicts resolved)`);
|
|
10231
|
-
} catch (error) {
|
|
10232
|
-
this.logger.debug(`Could not mark PR as ready: ${error instanceof Error ? error.message : String(error)}`);
|
|
10233
|
-
}
|
|
10234
|
-
}
|
|
10235
|
-
async postReplayConflictStatus(octokit, owner, repo, sha, replayConflictInfo, replayResult) {
|
|
10236
|
-
if (replayResult == null || !replayResult.executed) {
|
|
10237
|
-
return;
|
|
10238
|
-
}
|
|
10239
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10240
|
-
const conflictFileCount = (replayResult.conflictDetails ?? []).reduce(
|
|
10241
|
-
(sum, detail) => sum + detail.files.length,
|
|
10242
|
-
0
|
|
10243
|
-
);
|
|
10244
|
-
const sanitizedName = this.config.generatorName?.replace(/\//g, "--");
|
|
10245
|
-
const context = sanitizedName != null ? `fern / sdk customizations / ${sanitizedName}` : "fern / sdk customizations";
|
|
10246
|
-
try {
|
|
10247
|
-
await octokit.repos.createCommitStatus({
|
|
10248
|
-
owner,
|
|
10249
|
-
repo,
|
|
10250
|
-
sha,
|
|
10251
|
-
state: hasConflicts ? "failure" : "success",
|
|
10252
|
-
context,
|
|
10253
|
-
description: hasConflicts ? `${conflictFileCount} file(s) need manual conflict resolution \u2014 see PR description` : "All customizations applied"
|
|
10254
|
-
});
|
|
10255
|
-
this.logger.debug(`Posted ${hasConflicts ? "failing" : "passing"} commit status (${context})`);
|
|
10256
|
-
} catch (error) {
|
|
10257
|
-
this.logger.debug(
|
|
10258
|
-
`Could not post commit status: ${error instanceof Error ? error.message : String(error)}`
|
|
10259
|
-
);
|
|
10260
|
-
}
|
|
10261
|
-
}
|
|
10262
|
-
async postWebEditorFallbackComment(octokit, owner, repo, prNumber, branchName, baseBranch, replayResult) {
|
|
10263
|
-
try {
|
|
10264
|
-
const webEditorLikelyDisabled = await this.isWebEditorLikelyDisabled(
|
|
10265
|
-
octokit,
|
|
10266
|
-
owner,
|
|
10267
|
-
repo,
|
|
10268
|
-
prNumber,
|
|
10269
|
-
replayResult
|
|
10270
|
-
);
|
|
10271
|
-
if (!webEditorLikelyDisabled) {
|
|
10272
|
-
this.logger.debug(`PR #${prNumber}: web conflict editor appears usable, skipping fallback comment`);
|
|
10273
|
-
return;
|
|
10274
|
-
}
|
|
10275
|
-
const comment = buildWebEditorFallbackComment(branchName, baseBranch);
|
|
10276
|
-
await octokit.issues.createComment({
|
|
10277
|
-
owner,
|
|
10278
|
-
repo,
|
|
10279
|
-
issue_number: prNumber,
|
|
10280
|
-
body: comment
|
|
10281
|
-
});
|
|
10282
|
-
this.logger.info(`Posted web-editor fallback comment on PR #${prNumber}`);
|
|
10283
|
-
} catch (error) {
|
|
10284
|
-
this.logger.debug(
|
|
10285
|
-
`Could not post web-editor fallback comment: ${error instanceof Error ? error.message : String(error)}`
|
|
10286
|
-
);
|
|
10287
|
-
}
|
|
10288
|
-
}
|
|
10289
|
-
async isWebEditorLikelyDisabled(octokit, owner, repo, prNumber, replayResult) {
|
|
10290
|
-
const conflictDetails = replayResult.conflictDetails ?? [];
|
|
10291
|
-
const allConflictFiles = conflictDetails.flatMap((d) => d.files);
|
|
10292
|
-
const totalConflictFiles = allConflictFiles.length;
|
|
10293
|
-
const hasFileDeletionConflict = allConflictFiles.some((f) => {
|
|
10294
|
-
const status = (f.status ?? "").toLowerCase();
|
|
10295
|
-
const reason = (f.conflictReason ?? "").toLowerCase();
|
|
10296
|
-
return status === "skipped" || reason.includes("delete") || reason.includes("removed");
|
|
10297
|
-
});
|
|
10298
|
-
if (hasFileDeletionConflict) {
|
|
10299
|
-
this.logger.debug(`PR #${prNumber}: detected file deletion conflict, web editor likely disabled`);
|
|
10300
|
-
return true;
|
|
10301
|
-
}
|
|
10302
|
-
if (totalConflictFiles >= WEB_EDITOR_FILE_THRESHOLD) {
|
|
10303
|
-
this.logger.debug(
|
|
10304
|
-
`PR #${prNumber}: ${totalConflictFiles} conflicting files exceeds threshold (${WEB_EDITOR_FILE_THRESHOLD}), web editor likely disabled`
|
|
10305
|
-
);
|
|
10306
|
-
return true;
|
|
10307
|
-
}
|
|
10308
|
-
const totalHunks = (replayResult.conflicts ?? []).reduce(
|
|
10309
|
-
(sum, fileConflict) => sum + fileConflict.conflicts.length,
|
|
10310
|
-
0
|
|
10311
|
-
);
|
|
10312
|
-
if (totalHunks >= WEB_EDITOR_HUNK_THRESHOLD) {
|
|
10313
|
-
this.logger.debug(
|
|
10314
|
-
`PR #${prNumber}: ${totalHunks} conflict hunks exceeds threshold (${WEB_EDITOR_HUNK_THRESHOLD}), web editor likely disabled`
|
|
10315
|
-
);
|
|
10316
|
-
return true;
|
|
10317
|
-
}
|
|
10318
|
-
const mergeableState = await this.pollMergeableState(octokit, owner, repo, prNumber);
|
|
10319
|
-
if (mergeableState.mergeable === null) {
|
|
10320
|
-
this.logger.debug(`PR #${prNumber}: mergeable state still null after polling, web editor likely disabled`);
|
|
10321
|
-
return true;
|
|
10322
|
-
}
|
|
10323
|
-
return false;
|
|
10324
|
-
}
|
|
10325
|
-
async pollMergeableState(octokit, owner, repo, prNumber) {
|
|
10326
|
-
for (let attempt = 0; attempt < MERGEABLE_POLL_MAX_ATTEMPTS; attempt++) {
|
|
10327
|
-
const { data: pr } = await octokit.pulls.get({
|
|
10328
|
-
owner,
|
|
10329
|
-
repo,
|
|
10330
|
-
pull_number: prNumber
|
|
10331
|
-
});
|
|
10332
|
-
if (pr.mergeable != null) {
|
|
10333
|
-
return {
|
|
10334
|
-
mergeable: pr.mergeable,
|
|
10335
|
-
mergeableState: pr.mergeable_state
|
|
10336
|
-
};
|
|
10337
|
-
}
|
|
10338
|
-
if (attempt < MERGEABLE_POLL_MAX_ATTEMPTS - 1) {
|
|
10339
|
-
await sleep(MERGEABLE_POLL_DELAY_MS);
|
|
10340
|
-
}
|
|
10341
|
-
}
|
|
10342
|
-
return { mergeable: null, mergeableState: "unknown" };
|
|
10343
|
-
}
|
|
10344
10140
|
};
|
|
10345
|
-
var WEB_EDITOR_FILE_THRESHOLD = 20;
|
|
10346
|
-
var WEB_EDITOR_HUNK_THRESHOLD = 50;
|
|
10347
|
-
var MERGEABLE_POLL_MAX_ATTEMPTS = 3;
|
|
10348
|
-
var MERGEABLE_POLL_DELAY_MS = 1500;
|
|
10349
|
-
function buildWebEditorFallbackComment(branchName, baseBranch) {
|
|
10350
|
-
const lines = [
|
|
10351
|
-
`> **Note:** These conflicts may be too complex for GitHub's web editor. To resolve locally:`,
|
|
10352
|
-
`>`,
|
|
10353
|
-
`> 1. Check out this branch:`,
|
|
10354
|
-
`> \`\`\`sh`,
|
|
10355
|
-
`> git fetch origin && git checkout ${branchName}`,
|
|
10356
|
-
`> \`\`\``,
|
|
10357
|
-
`> 2. Merge the base branch:`,
|
|
10358
|
-
`> \`\`\`sh`,
|
|
10359
|
-
`> git merge origin/${baseBranch}`,
|
|
10360
|
-
`> \`\`\``,
|
|
10361
|
-
`> 3. Resolve conflicts in your editor (VS Code, IntelliJ, etc. have built-in merge tools)`,
|
|
10362
|
-
`> 4. Commit and push:`,
|
|
10363
|
-
`> \`\`\`sh`,
|
|
10364
|
-
`> git add -A && git commit -m "resolve conflicts" && git push`,
|
|
10365
|
-
`> \`\`\``,
|
|
10366
|
-
`>`,
|
|
10367
|
-
`> **Label guide:** In your editor's merge view, "current" = new generated code, "incoming" = your customization.`
|
|
10368
|
-
];
|
|
10369
|
-
return lines.join("\n");
|
|
10370
|
-
}
|
|
10371
|
-
function sleep(ms) {
|
|
10372
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
10373
|
-
}
|
|
10374
10141
|
|
|
10375
10142
|
// src/replay/replay-run.ts
|
|
10376
10143
|
var import_replay = require("@fern-api/replay");
|
|
@@ -10579,8 +10346,7 @@ var ReplayStep = class extends BaseStep {
|
|
|
10579
10346
|
flow: "first-generation",
|
|
10580
10347
|
patchesDetected: 0,
|
|
10581
10348
|
patchesApplied: 0,
|
|
10582
|
-
patchesWithConflicts: 0
|
|
10583
|
-
conflicts: []
|
|
10349
|
+
patchesWithConflicts: 0
|
|
10584
10350
|
};
|
|
10585
10351
|
}
|
|
10586
10352
|
const report = result.report;
|
|
@@ -10598,22 +10364,12 @@ var ReplayStep = class extends BaseStep {
|
|
|
10598
10364
|
patchesRepointed: report.patchesRepointed,
|
|
10599
10365
|
patchesContentRebased: report.patchesContentRebased,
|
|
10600
10366
|
patchesKeptAsUserOwned: report.patchesKeptAsUserOwned,
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
ours: conflict.ours,
|
|
10607
|
-
theirs: conflict.theirs
|
|
10608
|
-
})) ?? []
|
|
10609
|
-
})),
|
|
10610
|
-
conflictDetails: report.conflictDetails?.map((detail) => ({
|
|
10611
|
-
patchId: detail.patchId,
|
|
10612
|
-
patchMessage: detail.patchMessage,
|
|
10613
|
-
reason: detail.reason,
|
|
10614
|
-
files: detail.files.map((f) => ({
|
|
10367
|
+
unresolvedPatches: report.unresolvedPatches?.map((info) => ({
|
|
10368
|
+
patchId: info.patchId,
|
|
10369
|
+
patchMessage: info.patchMessage,
|
|
10370
|
+
files: info.files,
|
|
10371
|
+
conflictDetails: info.conflictDetails.map((f) => ({
|
|
10615
10372
|
file: f.file,
|
|
10616
|
-
status: f.status,
|
|
10617
10373
|
conflictReason: f.conflictReason
|
|
10618
10374
|
}))
|
|
10619
10375
|
})),
|
|
@@ -10738,7 +10494,10 @@ Base generation: ${bootstrapResult.generationCommit.sha.slice(0, 7)}`;
|
|
|
10738
10494
|
`user.name=${import_replay2.FERN_BOT_NAME}`,
|
|
10739
10495
|
"-c",
|
|
10740
10496
|
`user.email=${import_replay2.FERN_BOT_EMAIL}`,
|
|
10497
|
+
"-c",
|
|
10498
|
+
"commit.gpgsign=false",
|
|
10741
10499
|
"commit",
|
|
10500
|
+
"--no-verify",
|
|
10742
10501
|
"-m",
|
|
10743
10502
|
commitMessage
|
|
10744
10503
|
]);
|
|
@@ -10874,6 +10633,14 @@ function buildPrBody(result) {
|
|
|
10874
10633
|
lines.push("Once merged, your customizations will be automatically preserved on future `fern generate` runs.");
|
|
10875
10634
|
return lines.join("\n");
|
|
10876
10635
|
}
|
|
10636
|
+
|
|
10637
|
+
// src/replay/replay-resolve.ts
|
|
10638
|
+
var import_replay3 = require("@fern-api/replay");
|
|
10639
|
+
async function replayResolve(params) {
|
|
10640
|
+
return (0, import_replay3.resolve)(params.outputDir, {
|
|
10641
|
+
checkMarkers: params.checkMarkers
|
|
10642
|
+
});
|
|
10643
|
+
}
|
|
10877
10644
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10878
10645
|
0 && (module.exports = {
|
|
10879
10646
|
PostGenerationPipeline,
|
|
@@ -10889,5 +10656,6 @@ function buildPrBody(result) {
|
|
|
10889
10656
|
githubRelease,
|
|
10890
10657
|
logReplaySummary,
|
|
10891
10658
|
replayInit,
|
|
10659
|
+
replayResolve,
|
|
10892
10660
|
replayRun
|
|
10893
10661
|
});
|