@fern-api/generator-cli 0.6.10 → 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 +116 -354
- package/dist/cli.js +274 -323
- 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
|
});
|
|
@@ -8456,16 +8454,25 @@ var ReadmeGenerator = class {
|
|
|
8456
8454
|
this.apiName = this.readmeConfig.apiName ?? this.organizationPascalCase;
|
|
8457
8455
|
}
|
|
8458
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 }) {
|
|
8459
8468
|
const blocks = await this.generateBlocks();
|
|
8460
8469
|
const mergedBlocks = await this.mergeBlocks({ blocks });
|
|
8461
|
-
const writer = new StreamWriter(output);
|
|
8462
8470
|
await this.writeHeader({ writer });
|
|
8463
8471
|
await this.writeTableOfContents({ writer, blocks: mergedBlocks });
|
|
8464
8472
|
await this.writeBlocks({
|
|
8465
8473
|
writer,
|
|
8466
8474
|
blocks: mergedBlocks
|
|
8467
8475
|
});
|
|
8468
|
-
await writer.end();
|
|
8469
8476
|
}
|
|
8470
8477
|
async generateBlocks() {
|
|
8471
8478
|
const blocks = [];
|
|
@@ -9337,28 +9344,14 @@ async function generateReadmeToStream(params) {
|
|
|
9337
9344
|
}
|
|
9338
9345
|
async function generateReadme(params) {
|
|
9339
9346
|
const { originalReadmeContent, readmeConfig } = params;
|
|
9340
|
-
const
|
|
9341
|
-
|
|
9342
|
-
write(chunk, _encoding, callback) {
|
|
9343
|
-
if (typeof chunk === "string") {
|
|
9344
|
-
content.push(chunk);
|
|
9345
|
-
} else {
|
|
9346
|
-
content.push(Buffer.from(chunk).toString("utf8"));
|
|
9347
|
-
}
|
|
9348
|
-
callback();
|
|
9349
|
-
}
|
|
9350
|
-
});
|
|
9351
|
-
await generateReadmeToStream({
|
|
9352
|
-
originalReadmeContent,
|
|
9347
|
+
const generator = new ReadmeGenerator({
|
|
9348
|
+
readmeParser: new ReadmeParser(),
|
|
9353
9349
|
readmeConfig,
|
|
9354
|
-
|
|
9350
|
+
originalReadme: originalReadmeContent
|
|
9355
9351
|
});
|
|
9356
|
-
return
|
|
9352
|
+
return generator.generateReadmeToString();
|
|
9357
9353
|
}
|
|
9358
9354
|
|
|
9359
|
-
// src/api/generate-reference.ts
|
|
9360
|
-
var import_node_stream2 = require("stream");
|
|
9361
|
-
|
|
9362
9355
|
// src/reference/ReferenceGenerator.ts
|
|
9363
9356
|
var ReferenceGenerator = class {
|
|
9364
9357
|
referenceConfig;
|
|
@@ -9369,6 +9362,16 @@ var ReferenceGenerator = class {
|
|
|
9369
9362
|
}
|
|
9370
9363
|
async generate({ output }) {
|
|
9371
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 }) {
|
|
9372
9375
|
await writer.writeLine("# Reference");
|
|
9373
9376
|
if (this.referenceConfig.rootSection != null) {
|
|
9374
9377
|
await this.writeRootSection({
|
|
@@ -9379,7 +9382,6 @@ var ReferenceGenerator = class {
|
|
|
9379
9382
|
for (const section of this.referenceConfig.sections) {
|
|
9380
9383
|
await this.writeSection({ section, writer });
|
|
9381
9384
|
}
|
|
9382
|
-
await writer.end();
|
|
9383
9385
|
}
|
|
9384
9386
|
async writeRootSection({
|
|
9385
9387
|
section,
|
|
@@ -9480,22 +9482,8 @@ async function generateReferenceToStream(params) {
|
|
|
9480
9482
|
}
|
|
9481
9483
|
async function generateReference(params) {
|
|
9482
9484
|
const { referenceConfig } = params;
|
|
9483
|
-
const
|
|
9484
|
-
|
|
9485
|
-
write(chunk, _encoding, callback) {
|
|
9486
|
-
if (typeof chunk === "string") {
|
|
9487
|
-
content.push(chunk);
|
|
9488
|
-
} else {
|
|
9489
|
-
content.push(Buffer.from(chunk).toString("utf8"));
|
|
9490
|
-
}
|
|
9491
|
-
callback();
|
|
9492
|
-
}
|
|
9493
|
-
});
|
|
9494
|
-
await generateReferenceToStream({
|
|
9495
|
-
referenceConfig,
|
|
9496
|
-
outputStream: sink
|
|
9497
|
-
});
|
|
9498
|
-
return content.join("");
|
|
9485
|
+
const generator = new ReferenceGenerator({ referenceConfig });
|
|
9486
|
+
return generator.generateToString();
|
|
9499
9487
|
}
|
|
9500
9488
|
|
|
9501
9489
|
// ../commons/path-utils/lib/convertToOsPath.js
|
|
@@ -9689,41 +9677,19 @@ var import_path3 = require("path");
|
|
|
9689
9677
|
|
|
9690
9678
|
// src/pipeline/github/createReplayBranch.ts
|
|
9691
9679
|
async function createReplayBranch(repository, branchName, commitMessage, replayConflictInfo, logger) {
|
|
9692
|
-
if (replayConflictInfo
|
|
9693
|
-
const parentSha = replayConflictInfo.previousGenerationSha;
|
|
9694
|
-
const lockfileRestoreSha = replayConflictInfo.baseBranchHead ?? replayConflictInfo.previousGenerationSha;
|
|
9695
|
-
logger.debug(`Creating divergent PR branch with generation tree (parent: ${parentSha.substring(0, 7)})`);
|
|
9696
|
-
const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
|
|
9697
|
-
const syntheticCommitSha = await repository.commitTree(
|
|
9698
|
-
genTreeHash,
|
|
9699
|
-
parentSha,
|
|
9700
|
-
`[fern-generated] ${commitMessage ?? "Update SDK"}`
|
|
9701
|
-
);
|
|
9702
|
-
await repository.createBranchFromCommit(branchName, syntheticCommitSha);
|
|
9703
|
-
try {
|
|
9704
|
-
await repository.restoreFilesFromCommit(lockfileRestoreSha, ".fern/replay.lock");
|
|
9705
|
-
await repository.commitAllChanges(`[fern-generated] ${commitMessage ?? "Update SDK"}`);
|
|
9706
|
-
} catch (error) {
|
|
9707
|
-
logger.debug(
|
|
9708
|
-
`Could not restore lockfile from base branch: ${error instanceof Error ? error.message : String(error)}`
|
|
9709
|
-
);
|
|
9710
|
-
}
|
|
9711
|
-
return syntheticCommitSha;
|
|
9712
|
-
} else if (replayConflictInfo != null) {
|
|
9713
|
-
const parentSha = replayConflictInfo.previousGenerationSha;
|
|
9714
|
-
logger.debug(`Creating linear PR branch from HEAD (no conflicts)`);
|
|
9715
|
-
await repository.createBranchFromHead(branchName);
|
|
9716
|
-
const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
|
|
9717
|
-
const tagCommitSha = await repository.commitTree(
|
|
9718
|
-
genTreeHash,
|
|
9719
|
-
parentSha,
|
|
9720
|
-
`[fern-generated] ${commitMessage ?? "Update SDK"}`
|
|
9721
|
-
);
|
|
9722
|
-
return tagCommitSha;
|
|
9723
|
-
} else {
|
|
9680
|
+
if (replayConflictInfo == null) {
|
|
9724
9681
|
await repository.createBranchFromHead(branchName);
|
|
9725
9682
|
return void 0;
|
|
9726
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;
|
|
9727
9693
|
}
|
|
9728
9694
|
|
|
9729
9695
|
// src/pipeline/github/findExistingUpdatablePR.ts
|
|
@@ -9818,11 +9784,11 @@ function formatConflictReason(reason) {
|
|
|
9818
9784
|
case "same-line-edit":
|
|
9819
9785
|
return "The new generation changed the same lines you edited";
|
|
9820
9786
|
case "new-file-both":
|
|
9821
|
-
return "
|
|
9787
|
+
return "Both the generator and your customization created this file";
|
|
9822
9788
|
case "base-generation-mismatch":
|
|
9823
|
-
return "
|
|
9789
|
+
return "This customization was from a previous generation";
|
|
9824
9790
|
case "patch-apply-failed":
|
|
9825
|
-
return "
|
|
9791
|
+
return "The customization could not be applied to the new code";
|
|
9826
9792
|
default:
|
|
9827
9793
|
return "Your edit overlaps with changes in the new generation";
|
|
9828
9794
|
}
|
|
@@ -9831,7 +9797,7 @@ function patchDescription(detail) {
|
|
|
9831
9797
|
if (detail.patchMessage && detail.patchMessage !== "update") {
|
|
9832
9798
|
return detail.patchMessage;
|
|
9833
9799
|
}
|
|
9834
|
-
const firstFile = detail.files[0]
|
|
9800
|
+
const firstFile = detail.files[0];
|
|
9835
9801
|
return firstFile != null ? `changes in ${firstFile}` : "customization";
|
|
9836
9802
|
}
|
|
9837
9803
|
function logReplaySummary(result, logger) {
|
|
@@ -9840,10 +9806,10 @@ function logReplaySummary(result, logger) {
|
|
|
9840
9806
|
}
|
|
9841
9807
|
const applied = result.patchesApplied ?? 0;
|
|
9842
9808
|
const absorbed = result.patchesAbsorbed ?? 0;
|
|
9843
|
-
const
|
|
9809
|
+
const unresolvedCount = result.unresolvedPatches?.length ?? 0;
|
|
9844
9810
|
const preserved = applied - absorbed;
|
|
9845
9811
|
logger.debug(
|
|
9846
|
-
`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}`
|
|
9847
9813
|
);
|
|
9848
9814
|
if (preserved > 0) {
|
|
9849
9815
|
const absorbedNote = absorbed > 0 ? ` (some customizations now part of generated code)` : "";
|
|
@@ -9851,14 +9817,17 @@ function logReplaySummary(result, logger) {
|
|
|
9851
9817
|
} else if (absorbed > 0) {
|
|
9852
9818
|
logger.info(`Replay: customizations now part of generated code`);
|
|
9853
9819
|
}
|
|
9854
|
-
if (
|
|
9855
|
-
const totalFiles = (result.
|
|
9820
|
+
if (unresolvedCount > 0) {
|
|
9821
|
+
const totalFiles = (result.unresolvedPatches ?? []).reduce(
|
|
9822
|
+
(sum, patch) => sum + patch.conflictDetails.length,
|
|
9823
|
+
0
|
|
9824
|
+
);
|
|
9856
9825
|
logger.warn(
|
|
9857
|
-
`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\``
|
|
9858
9827
|
);
|
|
9859
|
-
for (const
|
|
9860
|
-
logger.warn(` "${patchDescription(
|
|
9861
|
-
for (const file of
|
|
9828
|
+
for (const patch of result.unresolvedPatches ?? []) {
|
|
9829
|
+
logger.warn(` "${patchDescription(patch)}":`);
|
|
9830
|
+
for (const file of patch.conflictDetails) {
|
|
9862
9831
|
logger.warn(` ${file.file} \u2014 ${formatConflictReason(file.conflictReason)}`);
|
|
9863
9832
|
}
|
|
9864
9833
|
}
|
|
@@ -9873,70 +9842,66 @@ function formatReplayPrBody(result, options) {
|
|
|
9873
9842
|
}
|
|
9874
9843
|
const applied = result.patchesApplied ?? 0;
|
|
9875
9844
|
const absorbed = result.patchesAbsorbed ?? 0;
|
|
9876
|
-
const
|
|
9845
|
+
const unresolvedPatches = result.unresolvedPatches ?? [];
|
|
9846
|
+
const hasUnresolved = unresolvedPatches.length > 0;
|
|
9877
9847
|
const preserved = applied - absorbed;
|
|
9878
|
-
if (preserved === 0 &&
|
|
9848
|
+
if (preserved === 0 && !hasUnresolved) {
|
|
9879
9849
|
return void 0;
|
|
9880
9850
|
}
|
|
9881
9851
|
const parts = [];
|
|
9882
|
-
if (preserved > 0 &&
|
|
9852
|
+
if (preserved > 0 && !hasUnresolved) {
|
|
9883
9853
|
parts.push(`\u2705 Customizations automatically preserved in this update.`);
|
|
9884
9854
|
} else if (preserved > 0) {
|
|
9855
|
+
const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
|
|
9885
9856
|
parts.push(
|
|
9886
|
-
`\u2705 Customizations automatically preserved, but ${plural(
|
|
9857
|
+
`\u2705 Customizations automatically preserved, but ${plural(totalFiles, "file")} ${totalFiles === 1 ? "needs" : "need"} your attention.`
|
|
9887
9858
|
);
|
|
9888
9859
|
}
|
|
9889
|
-
if (
|
|
9890
|
-
const
|
|
9891
|
-
const totalFiles = allFiles.length;
|
|
9860
|
+
if (hasUnresolved) {
|
|
9861
|
+
const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
|
|
9892
9862
|
parts.push(`
|
|
9893
|
-
###
|
|
9863
|
+
### Action required: ${plural(totalFiles, "file")} with unresolved customization conflicts
|
|
9894
9864
|
`);
|
|
9895
9865
|
parts.push(
|
|
9896
|
-
`
|
|
9897
|
-
`
|
|
9898
|
-
);
|
|
9899
|
-
parts.push(
|
|
9900
|
-
`> \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:
|
|
9901
9867
|
`
|
|
9902
9868
|
);
|
|
9903
9869
|
parts.push(`| File | Your customization | Why it conflicted |`);
|
|
9904
9870
|
parts.push(`|------|-------------------|-------------------|`);
|
|
9905
|
-
for (const
|
|
9906
|
-
const description = patchDescription(
|
|
9907
|
-
for (const
|
|
9908
|
-
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)} |`);
|
|
9909
9875
|
}
|
|
9910
9876
|
}
|
|
9911
9877
|
const branch = options?.branchName ?? "<branch-name>";
|
|
9878
|
+
const repoUri = options?.repoUri;
|
|
9879
|
+
const repoName = repoUri?.split("/").pop() ?? "<repo>";
|
|
9912
9880
|
parts.push(`
|
|
9913
|
-
#### How to
|
|
9914
|
-
`);
|
|
9915
|
-
parts.push(`**Option A: Resolve in GitHub (recommended for simple conflicts)**
|
|
9916
|
-
`);
|
|
9917
|
-
parts.push(`1. Click the **Resolve conflicts** button below`);
|
|
9918
|
-
parts.push(`2. For each file, choose which code to keep:`);
|
|
9919
|
-
parts.push(` - \`<<<<<<< HEAD\` / "Accept current changes" = **new generated code**`);
|
|
9920
|
-
parts.push(` - \`>>>>>>> main\` / "Accept incoming changes" = **your customization**`);
|
|
9921
|
-
parts.push(` - Or manually combine both, then delete the conflict markers`);
|
|
9922
|
-
parts.push(`3. Click **Mark as resolved** for each file, then **Commit merge**
|
|
9923
|
-
`);
|
|
9924
|
-
parts.push(`**Option B: Resolve locally**
|
|
9881
|
+
#### How to resolve
|
|
9925
9882
|
`);
|
|
9926
|
-
parts.push(`1.
|
|
9883
|
+
parts.push(`1. Check out this branch:`);
|
|
9927
9884
|
parts.push(` \`\`\`sh`);
|
|
9928
|
-
parts.push(` git fetch origin && git checkout ${branch}`);
|
|
9885
|
+
parts.push(` git fetch origin && git checkout -b ${branch} origin/${branch}`);
|
|
9929
9886
|
parts.push(` \`\`\``);
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
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
|
|
9938
9900
|
`);
|
|
9939
|
-
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
|
+
);
|
|
9940
9905
|
}
|
|
9941
9906
|
return parts.join("\n");
|
|
9942
9907
|
}
|
|
@@ -10072,16 +10037,9 @@ var GithubStep = class extends BaseStep {
|
|
|
10072
10037
|
}
|
|
10073
10038
|
}
|
|
10074
10039
|
}
|
|
10075
|
-
if (!this.config.previewMode) {
|
|
10076
|
-
const headSha = await repository.getHeadSha();
|
|
10077
|
-
await this.postReplayConflictStatus(octokit, owner, repo, headSha, replayConflictInfo, replayResult);
|
|
10078
|
-
if (isUpdatingExistingPR && existingPR != null) {
|
|
10079
|
-
await this.togglePrDraftState(octokit, existingPR, replayConflictInfo);
|
|
10080
|
-
}
|
|
10081
|
-
}
|
|
10082
10040
|
const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
|
|
10083
10041
|
const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage);
|
|
10084
|
-
const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch });
|
|
10042
|
+
const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch, repoUri: this.config.uri });
|
|
10085
10043
|
const enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody;
|
|
10086
10044
|
if (isUpdatingExistingPR && existingPR != null) {
|
|
10087
10045
|
this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`);
|
|
@@ -10103,7 +10061,6 @@ var GithubStep = class extends BaseStep {
|
|
|
10103
10061
|
}
|
|
10104
10062
|
} else {
|
|
10105
10063
|
const head = `${owner}:${prBranch}`;
|
|
10106
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10107
10064
|
try {
|
|
10108
10065
|
const { data: pullRequest } = await octokit.pulls.create({
|
|
10109
10066
|
owner,
|
|
@@ -10111,8 +10068,7 @@ var GithubStep = class extends BaseStep {
|
|
|
10111
10068
|
title: prTitle,
|
|
10112
10069
|
body: enrichedBody,
|
|
10113
10070
|
head,
|
|
10114
|
-
base: baseBranch
|
|
10115
|
-
draft: hasConflicts
|
|
10071
|
+
base: baseBranch
|
|
10116
10072
|
});
|
|
10117
10073
|
this.logger.info(`Created pull request: ${pullRequest.html_url}`);
|
|
10118
10074
|
result.prUrl = pullRequest.html_url;
|
|
@@ -10126,17 +10082,6 @@ var GithubStep = class extends BaseStep {
|
|
|
10126
10082
|
}
|
|
10127
10083
|
}
|
|
10128
10084
|
}
|
|
10129
|
-
if (!this.config.previewMode && result.prNumber != null && replayConflictInfo?.hasConflicts === true && replayResult != null) {
|
|
10130
|
-
await this.postWebEditorFallbackComment(
|
|
10131
|
-
octokit,
|
|
10132
|
-
owner,
|
|
10133
|
-
repo,
|
|
10134
|
-
result.prNumber,
|
|
10135
|
-
prBranch,
|
|
10136
|
-
baseBranch,
|
|
10137
|
-
replayResult
|
|
10138
|
-
);
|
|
10139
|
-
}
|
|
10140
10085
|
return result;
|
|
10141
10086
|
}
|
|
10142
10087
|
async executePushMode(repository) {
|
|
@@ -10182,198 +10127,17 @@ var GithubStep = class extends BaseStep {
|
|
|
10182
10127
|
if (replayResult == null) {
|
|
10183
10128
|
return void 0;
|
|
10184
10129
|
}
|
|
10185
|
-
const hasConflicts = (replayResult.patchesWithConflicts ?? 0) > 0;
|
|
10186
10130
|
const previousGenerationSha = replayResult.previousGenerationSha;
|
|
10187
10131
|
const currentGenerationSha = replayResult.currentGenerationSha;
|
|
10188
10132
|
if (previousGenerationSha != null && currentGenerationSha != null) {
|
|
10189
10133
|
return {
|
|
10190
10134
|
previousGenerationSha,
|
|
10191
|
-
currentGenerationSha
|
|
10192
|
-
hasConflicts,
|
|
10193
|
-
baseBranchHead: replayResult.baseBranchHead
|
|
10135
|
+
currentGenerationSha
|
|
10194
10136
|
};
|
|
10195
10137
|
}
|
|
10196
10138
|
return void 0;
|
|
10197
10139
|
}
|
|
10198
|
-
async togglePrDraftState(octokit, existingPR, replayConflictInfo) {
|
|
10199
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10200
|
-
if (hasConflicts && !existingPR.isDraft) {
|
|
10201
|
-
await this.convertPrToDraft(octokit, existingPR.nodeId, existingPR.number);
|
|
10202
|
-
} else if (!hasConflicts && existingPR.isDraft) {
|
|
10203
|
-
await this.markPrReady(octokit, existingPR.nodeId, existingPR.number);
|
|
10204
|
-
}
|
|
10205
|
-
}
|
|
10206
|
-
async convertPrToDraft(octokit, nodeId, prNumber) {
|
|
10207
|
-
try {
|
|
10208
|
-
await octokit.graphql(
|
|
10209
|
-
`mutation($id: ID!) {
|
|
10210
|
-
convertPullRequestToDraft(input: {pullRequestId: $id}) {
|
|
10211
|
-
pullRequest { isDraft }
|
|
10212
|
-
}
|
|
10213
|
-
}`,
|
|
10214
|
-
{ id: nodeId }
|
|
10215
|
-
);
|
|
10216
|
-
this.logger.info(`Converted PR #${prNumber} to draft due to replay conflicts`);
|
|
10217
|
-
} catch (error) {
|
|
10218
|
-
this.logger.debug(
|
|
10219
|
-
`Could not convert PR to draft: ${error instanceof Error ? error.message : String(error)}`
|
|
10220
|
-
);
|
|
10221
|
-
}
|
|
10222
|
-
}
|
|
10223
|
-
async markPrReady(octokit, nodeId, prNumber) {
|
|
10224
|
-
try {
|
|
10225
|
-
await octokit.graphql(
|
|
10226
|
-
`mutation($id: ID!) {
|
|
10227
|
-
markPullRequestReadyForReview(input: {pullRequestId: $id}) {
|
|
10228
|
-
pullRequest { isDraft }
|
|
10229
|
-
}
|
|
10230
|
-
}`,
|
|
10231
|
-
{ id: nodeId }
|
|
10232
|
-
);
|
|
10233
|
-
this.logger.info(`Marked PR #${prNumber} as ready (conflicts resolved)`);
|
|
10234
|
-
} catch (error) {
|
|
10235
|
-
this.logger.debug(`Could not mark PR as ready: ${error instanceof Error ? error.message : String(error)}`);
|
|
10236
|
-
}
|
|
10237
|
-
}
|
|
10238
|
-
async postReplayConflictStatus(octokit, owner, repo, sha, replayConflictInfo, replayResult) {
|
|
10239
|
-
if (replayResult == null || !replayResult.executed) {
|
|
10240
|
-
return;
|
|
10241
|
-
}
|
|
10242
|
-
const hasConflicts = replayConflictInfo?.hasConflicts === true;
|
|
10243
|
-
const conflictFileCount = (replayResult.conflictDetails ?? []).reduce(
|
|
10244
|
-
(sum, detail) => sum + detail.files.length,
|
|
10245
|
-
0
|
|
10246
|
-
);
|
|
10247
|
-
const sanitizedName = this.config.generatorName?.replace(/\//g, "--");
|
|
10248
|
-
const context = sanitizedName != null ? `fern / sdk customizations / ${sanitizedName}` : "fern / sdk customizations";
|
|
10249
|
-
try {
|
|
10250
|
-
await octokit.repos.createCommitStatus({
|
|
10251
|
-
owner,
|
|
10252
|
-
repo,
|
|
10253
|
-
sha,
|
|
10254
|
-
state: hasConflicts ? "failure" : "success",
|
|
10255
|
-
context,
|
|
10256
|
-
description: hasConflicts ? `${conflictFileCount} file(s) need manual conflict resolution \u2014 see PR description` : "All customizations applied"
|
|
10257
|
-
});
|
|
10258
|
-
this.logger.debug(`Posted ${hasConflicts ? "failing" : "passing"} commit status (${context})`);
|
|
10259
|
-
} catch (error) {
|
|
10260
|
-
this.logger.debug(
|
|
10261
|
-
`Could not post commit status: ${error instanceof Error ? error.message : String(error)}`
|
|
10262
|
-
);
|
|
10263
|
-
}
|
|
10264
|
-
}
|
|
10265
|
-
async postWebEditorFallbackComment(octokit, owner, repo, prNumber, branchName, baseBranch, replayResult) {
|
|
10266
|
-
try {
|
|
10267
|
-
const webEditorLikelyDisabled = await this.isWebEditorLikelyDisabled(
|
|
10268
|
-
octokit,
|
|
10269
|
-
owner,
|
|
10270
|
-
repo,
|
|
10271
|
-
prNumber,
|
|
10272
|
-
replayResult
|
|
10273
|
-
);
|
|
10274
|
-
if (!webEditorLikelyDisabled) {
|
|
10275
|
-
this.logger.debug(`PR #${prNumber}: web conflict editor appears usable, skipping fallback comment`);
|
|
10276
|
-
return;
|
|
10277
|
-
}
|
|
10278
|
-
const comment = buildWebEditorFallbackComment(branchName, baseBranch);
|
|
10279
|
-
await octokit.issues.createComment({
|
|
10280
|
-
owner,
|
|
10281
|
-
repo,
|
|
10282
|
-
issue_number: prNumber,
|
|
10283
|
-
body: comment
|
|
10284
|
-
});
|
|
10285
|
-
this.logger.info(`Posted web-editor fallback comment on PR #${prNumber}`);
|
|
10286
|
-
} catch (error) {
|
|
10287
|
-
this.logger.debug(
|
|
10288
|
-
`Could not post web-editor fallback comment: ${error instanceof Error ? error.message : String(error)}`
|
|
10289
|
-
);
|
|
10290
|
-
}
|
|
10291
|
-
}
|
|
10292
|
-
async isWebEditorLikelyDisabled(octokit, owner, repo, prNumber, replayResult) {
|
|
10293
|
-
const conflictDetails = replayResult.conflictDetails ?? [];
|
|
10294
|
-
const allConflictFiles = conflictDetails.flatMap((d) => d.files);
|
|
10295
|
-
const totalConflictFiles = allConflictFiles.length;
|
|
10296
|
-
const hasFileDeletionConflict = allConflictFiles.some((f) => {
|
|
10297
|
-
const status = (f.status ?? "").toLowerCase();
|
|
10298
|
-
const reason = (f.conflictReason ?? "").toLowerCase();
|
|
10299
|
-
return status === "skipped" || reason.includes("delete") || reason.includes("removed");
|
|
10300
|
-
});
|
|
10301
|
-
if (hasFileDeletionConflict) {
|
|
10302
|
-
this.logger.debug(`PR #${prNumber}: detected file deletion conflict, web editor likely disabled`);
|
|
10303
|
-
return true;
|
|
10304
|
-
}
|
|
10305
|
-
if (totalConflictFiles >= WEB_EDITOR_FILE_THRESHOLD) {
|
|
10306
|
-
this.logger.debug(
|
|
10307
|
-
`PR #${prNumber}: ${totalConflictFiles} conflicting files exceeds threshold (${WEB_EDITOR_FILE_THRESHOLD}), web editor likely disabled`
|
|
10308
|
-
);
|
|
10309
|
-
return true;
|
|
10310
|
-
}
|
|
10311
|
-
const totalHunks = (replayResult.conflicts ?? []).reduce(
|
|
10312
|
-
(sum, fileConflict) => sum + fileConflict.conflicts.length,
|
|
10313
|
-
0
|
|
10314
|
-
);
|
|
10315
|
-
if (totalHunks >= WEB_EDITOR_HUNK_THRESHOLD) {
|
|
10316
|
-
this.logger.debug(
|
|
10317
|
-
`PR #${prNumber}: ${totalHunks} conflict hunks exceeds threshold (${WEB_EDITOR_HUNK_THRESHOLD}), web editor likely disabled`
|
|
10318
|
-
);
|
|
10319
|
-
return true;
|
|
10320
|
-
}
|
|
10321
|
-
const mergeableState = await this.pollMergeableState(octokit, owner, repo, prNumber);
|
|
10322
|
-
if (mergeableState.mergeable === null) {
|
|
10323
|
-
this.logger.debug(`PR #${prNumber}: mergeable state still null after polling, web editor likely disabled`);
|
|
10324
|
-
return true;
|
|
10325
|
-
}
|
|
10326
|
-
return false;
|
|
10327
|
-
}
|
|
10328
|
-
async pollMergeableState(octokit, owner, repo, prNumber) {
|
|
10329
|
-
for (let attempt = 0; attempt < MERGEABLE_POLL_MAX_ATTEMPTS; attempt++) {
|
|
10330
|
-
const { data: pr } = await octokit.pulls.get({
|
|
10331
|
-
owner,
|
|
10332
|
-
repo,
|
|
10333
|
-
pull_number: prNumber
|
|
10334
|
-
});
|
|
10335
|
-
if (pr.mergeable != null) {
|
|
10336
|
-
return {
|
|
10337
|
-
mergeable: pr.mergeable,
|
|
10338
|
-
mergeableState: pr.mergeable_state
|
|
10339
|
-
};
|
|
10340
|
-
}
|
|
10341
|
-
if (attempt < MERGEABLE_POLL_MAX_ATTEMPTS - 1) {
|
|
10342
|
-
await sleep(MERGEABLE_POLL_DELAY_MS);
|
|
10343
|
-
}
|
|
10344
|
-
}
|
|
10345
|
-
return { mergeable: null, mergeableState: "unknown" };
|
|
10346
|
-
}
|
|
10347
10140
|
};
|
|
10348
|
-
var WEB_EDITOR_FILE_THRESHOLD = 20;
|
|
10349
|
-
var WEB_EDITOR_HUNK_THRESHOLD = 50;
|
|
10350
|
-
var MERGEABLE_POLL_MAX_ATTEMPTS = 3;
|
|
10351
|
-
var MERGEABLE_POLL_DELAY_MS = 1500;
|
|
10352
|
-
function buildWebEditorFallbackComment(branchName, baseBranch) {
|
|
10353
|
-
const lines = [
|
|
10354
|
-
`> **Note:** These conflicts may be too complex for GitHub's web editor. To resolve locally:`,
|
|
10355
|
-
`>`,
|
|
10356
|
-
`> 1. Check out this branch:`,
|
|
10357
|
-
`> \`\`\`sh`,
|
|
10358
|
-
`> git fetch origin && git checkout ${branchName}`,
|
|
10359
|
-
`> \`\`\``,
|
|
10360
|
-
`> 2. Merge the base branch:`,
|
|
10361
|
-
`> \`\`\`sh`,
|
|
10362
|
-
`> git merge origin/${baseBranch}`,
|
|
10363
|
-
`> \`\`\``,
|
|
10364
|
-
`> 3. Resolve conflicts in your editor (VS Code, IntelliJ, etc. have built-in merge tools)`,
|
|
10365
|
-
`> 4. Commit and push:`,
|
|
10366
|
-
`> \`\`\`sh`,
|
|
10367
|
-
`> git add -A && git commit -m "resolve conflicts" && git push`,
|
|
10368
|
-
`> \`\`\``,
|
|
10369
|
-
`>`,
|
|
10370
|
-
`> **Label guide:** In your editor's merge view, "current" = new generated code, "incoming" = your customization.`
|
|
10371
|
-
];
|
|
10372
|
-
return lines.join("\n");
|
|
10373
|
-
}
|
|
10374
|
-
function sleep(ms) {
|
|
10375
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
10376
|
-
}
|
|
10377
10141
|
|
|
10378
10142
|
// src/replay/replay-run.ts
|
|
10379
10143
|
var import_replay = require("@fern-api/replay");
|
|
@@ -10582,8 +10346,7 @@ var ReplayStep = class extends BaseStep {
|
|
|
10582
10346
|
flow: "first-generation",
|
|
10583
10347
|
patchesDetected: 0,
|
|
10584
10348
|
patchesApplied: 0,
|
|
10585
|
-
patchesWithConflicts: 0
|
|
10586
|
-
conflicts: []
|
|
10349
|
+
patchesWithConflicts: 0
|
|
10587
10350
|
};
|
|
10588
10351
|
}
|
|
10589
10352
|
const report = result.report;
|
|
@@ -10601,22 +10364,12 @@ var ReplayStep = class extends BaseStep {
|
|
|
10601
10364
|
patchesRepointed: report.patchesRepointed,
|
|
10602
10365
|
patchesContentRebased: report.patchesContentRebased,
|
|
10603
10366
|
patchesKeptAsUserOwned: report.patchesKeptAsUserOwned,
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
ours: conflict.ours,
|
|
10610
|
-
theirs: conflict.theirs
|
|
10611
|
-
})) ?? []
|
|
10612
|
-
})),
|
|
10613
|
-
conflictDetails: report.conflictDetails?.map((detail) => ({
|
|
10614
|
-
patchId: detail.patchId,
|
|
10615
|
-
patchMessage: detail.patchMessage,
|
|
10616
|
-
reason: detail.reason,
|
|
10617
|
-
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) => ({
|
|
10618
10372
|
file: f.file,
|
|
10619
|
-
status: f.status,
|
|
10620
10373
|
conflictReason: f.conflictReason
|
|
10621
10374
|
}))
|
|
10622
10375
|
})),
|
|
@@ -10880,6 +10633,14 @@ function buildPrBody(result) {
|
|
|
10880
10633
|
lines.push("Once merged, your customizations will be automatically preserved on future `fern generate` runs.");
|
|
10881
10634
|
return lines.join("\n");
|
|
10882
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
|
+
}
|
|
10883
10644
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10884
10645
|
0 && (module.exports = {
|
|
10885
10646
|
PostGenerationPipeline,
|
|
@@ -10895,5 +10656,6 @@ function buildPrBody(result) {
|
|
|
10895
10656
|
githubRelease,
|
|
10896
10657
|
logReplaySummary,
|
|
10897
10658
|
replayInit,
|
|
10659
|
+
replayResolve,
|
|
10898
10660
|
replayRun
|
|
10899
10661
|
});
|