@fern-api/generator-cli 0.9.47 → 0.9.48

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.
Files changed (3) hide show
  1. package/dist/api.js +29 -7
  2. package/dist/cli.js +166 -131
  3. package/package.json +1 -1
package/dist/api.js CHANGED
@@ -11855,6 +11855,7 @@ function resolve2(...paths) {
11855
11855
 
11856
11856
  // src/github/GitHub.ts
11857
11857
  var import_rest = require("@octokit/rest");
11858
+ var import_promises2 = require("fs/promises");
11858
11859
  var GitHub = class {
11859
11860
  githubConfig;
11860
11861
  constructor({
@@ -11881,9 +11882,10 @@ var GitHub = class {
11881
11882
  await repository.pull(branch);
11882
11883
  }
11883
11884
  const fernIgnoreFiles = await this.getFernignoreFiles(repository);
11885
+ const changelogFiles = await this.getChangelogFilesToPreserve(repository, sourceDirectory);
11884
11886
  await repository.overwriteLocalContents(sourceDirectory);
11885
11887
  await repository.add(".");
11886
- await this.restoreFiles(repository, fernIgnoreFiles);
11888
+ await this.restoreFiles(repository, [...fernIgnoreFiles, ...changelogFiles]);
11887
11889
  await repository.commit("SDK Generation");
11888
11890
  if (isEmptyRepo) {
11889
11891
  await repository.pushUpstream(branch);
@@ -11920,9 +11922,10 @@ var GitHub = class {
11920
11922
  const prBranch = `fern-bot/${formattedDate}`;
11921
11923
  await repository.checkout(prBranch);
11922
11924
  const fernIgnoreFiles = await this.getFernignoreFiles(repository);
11925
+ const changelogFiles = await this.getChangelogFilesToPreserve(repository, sourceDirectory);
11923
11926
  await repository.overwriteLocalContents(sourceDirectory);
11924
11927
  await repository.add(".");
11925
- await this.restoreFiles(repository, fernIgnoreFiles);
11928
+ await this.restoreFiles(repository, [...fernIgnoreFiles, ...changelogFiles]);
11926
11929
  await repository.commit("SDK Generation");
11927
11930
  await repository.push();
11928
11931
  const apiBaseUrl = getGithubApiBaseUrl(this.githubConfig.uri);
@@ -11966,6 +11969,25 @@ var GitHub = class {
11966
11969
  const tracked = await repository.listTrackedFiles();
11967
11970
  return expandFernignorePatterns(fernignore, tracked);
11968
11971
  }
11972
+ /**
11973
+ * Returns tracked changelog files (e.g. `changelog.md`) that should be restored after
11974
+ * `overwriteLocalContents`, so existing changelog entries are never wiped when the
11975
+ * generated output doesn't include a changelog of its own.
11976
+ */
11977
+ async getChangelogFilesToPreserve(repository, sourceDirectory) {
11978
+ const tracked = await repository.listTrackedFiles();
11979
+ const trackedChangelogs = tracked.filter((file) => file.toLowerCase() === "changelog.md");
11980
+ if (trackedChangelogs.length === 0) {
11981
+ return [];
11982
+ }
11983
+ try {
11984
+ const sourceFiles = await (0, import_promises2.readdir)(sourceDirectory);
11985
+ const sourceHasChangelog = sourceFiles.some((file) => file.toLowerCase() === "changelog.md");
11986
+ return sourceHasChangelog ? [] : trackedChangelogs;
11987
+ } catch {
11988
+ return trackedChangelogs;
11989
+ }
11990
+ }
11969
11991
  async restoreFiles(repository, files) {
11970
11992
  if (files.length === 0) {
11971
11993
  return;
@@ -12009,7 +12031,7 @@ var import_path4 = require("path");
12009
12031
  var import_tmp_promise2 = __toESM(require("tmp-promise"));
12010
12032
 
12011
12033
  // src/replay/fernignore.ts
12012
- var import_promises2 = require("fs/promises");
12034
+ var import_promises3 = require("fs/promises");
12013
12035
  var import_path3 = require("path");
12014
12036
  var REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml", ".gitattributes"];
12015
12037
  var GITATTRIBUTES_ENTRIES = [".fern/replay.lock linguist-generated=true"];
@@ -12017,14 +12039,14 @@ async function ensureReplayFernignoreEntries(outputDir) {
12017
12039
  const fernignorePath = (0, import_path3.join)(outputDir, ".fernignore");
12018
12040
  let content = "";
12019
12041
  try {
12020
- content = await (0, import_promises2.readFile)(fernignorePath, "utf-8");
12042
+ content = await (0, import_promises3.readFile)(fernignorePath, "utf-8");
12021
12043
  } catch {
12022
12044
  }
12023
12045
  const lines = content.split("\n");
12024
12046
  const toAdd = REPLAY_FERNIGNORE_ENTRIES.filter((entry) => !lines.some((line) => line.trim() === entry));
12025
12047
  if (toAdd.length > 0) {
12026
12048
  const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
12027
- await (0, import_promises2.writeFile)(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
12049
+ await (0, import_promises3.writeFile)(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
12028
12050
  return true;
12029
12051
  }
12030
12052
  return false;
@@ -12033,14 +12055,14 @@ async function ensureGitattributesEntries(outputDir) {
12033
12055
  const gitattributesPath = (0, import_path3.join)(outputDir, ".gitattributes");
12034
12056
  let content = "";
12035
12057
  try {
12036
- content = await (0, import_promises2.readFile)(gitattributesPath, "utf-8");
12058
+ content = await (0, import_promises3.readFile)(gitattributesPath, "utf-8");
12037
12059
  } catch {
12038
12060
  }
12039
12061
  const lines = content.split("\n");
12040
12062
  const toAdd = GITATTRIBUTES_ENTRIES.filter((entry) => !lines.some((line) => line.trim() === entry));
12041
12063
  if (toAdd.length > 0) {
12042
12064
  const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
12043
- await (0, import_promises2.writeFile)(gitattributesPath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
12065
+ await (0, import_promises3.writeFile)(gitattributesPath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
12044
12066
  }
12045
12067
  }
12046
12068
 
package/dist/cli.js CHANGED
@@ -22482,7 +22482,7 @@ function reconstructFromGhostPatch(fileDiff, ours) {
22482
22482
  async function applyPatchToContent(base, patchContent, filePath) {
22483
22483
  const fileDiff = extractFileDiff(patchContent, filePath);
22484
22484
  if (!fileDiff) return null;
22485
- const tempDir = await (0, import_promises3.mkdtemp)((0, import_path7.join)((0, import_os.tmpdir)(), "replay-migrate-"));
22485
+ const tempDir = await (0, import_promises4.mkdtemp)((0, import_path7.join)((0, import_os.tmpdir)(), "replay-migrate-"));
22486
22486
  const tempGit = new GitClient(tempDir);
22487
22487
  try {
22488
22488
  await tempGit.exec(["init"]);
@@ -22490,16 +22490,16 @@ async function applyPatchToContent(base, patchContent, filePath) {
22490
22490
  await tempGit.exec(["config", "user.name", "Fern Replay Migration"]);
22491
22491
  await tempGit.exec(["config", "commit.gpgSign", "false"]);
22492
22492
  const tempFilePath = (0, import_path7.join)(tempDir, filePath);
22493
- await (0, import_promises3.mkdir)((0, import_path7.dirname)(tempFilePath), { recursive: true });
22494
- await (0, import_promises3.writeFile)(tempFilePath, base);
22493
+ await (0, import_promises4.mkdir)((0, import_path7.dirname)(tempFilePath), { recursive: true });
22494
+ await (0, import_promises4.writeFile)(tempFilePath, base);
22495
22495
  await tempGit.exec(["add", filePath]);
22496
22496
  await tempGit.exec(["commit", "-m", `base for ${filePath}`, "--allow-empty"]);
22497
22497
  await tempGit.execWithInput(["apply", "--allow-empty"], fileDiff);
22498
- return await (0, import_promises3.readFile)(tempFilePath, "utf-8");
22498
+ return await (0, import_promises4.readFile)(tempFilePath, "utf-8");
22499
22499
  } catch {
22500
22500
  return null;
22501
22501
  } finally {
22502
- await (0, import_promises3.rm)(tempDir, { recursive: true, force: true }).catch(() => {
22502
+ await (0, import_promises4.rm)(tempDir, { recursive: true, force: true }).catch(() => {
22503
22503
  });
22504
22504
  }
22505
22505
  }
@@ -22820,7 +22820,7 @@ async function resolveInputs(args) {
22820
22820
  }
22821
22821
  }
22822
22822
  const oursPath = (0, import_path11.join)(outputDir, resolvedPath);
22823
- const ours = await (0, import_promises5.readFile)(oursPath, "utf-8").catch(() => null);
22823
+ const ours = await (0, import_promises6.readFile)(oursPath, "utf-8").catch(() => null);
22824
22824
  let ghostTheirs = null;
22825
22825
  let baseTreeUnreachable = false;
22826
22826
  if (!base && !renameSourcePath) {
@@ -23067,14 +23067,14 @@ async function runMergeAndRecord(args) {
23067
23067
  }
23068
23068
  if (plan.kind === "new-file-only-user") {
23069
23069
  accumulator.recordMerge(resolvedPath, plan.theirs, patch.base_generation);
23070
- await (0, import_promises6.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23071
- await (0, import_promises6.writeFile)(oursPath, plan.theirs);
23070
+ await (0, import_promises7.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23071
+ await (0, import_promises7.writeFile)(oursPath, plan.theirs);
23072
23072
  return { file: resolvedPath, status: "merged", reason: "new-file" };
23073
23073
  }
23074
23074
  if (plan.kind === "new-file-both") {
23075
23075
  const merged2 = threeWayMerge("", ours, plan.theirs);
23076
- await (0, import_promises6.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23077
- await (0, import_promises6.writeFile)(oursPath, merged2.content);
23076
+ await (0, import_promises7.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23077
+ await (0, import_promises7.writeFile)(oursPath, merged2.content);
23078
23078
  const unreachableFlag = plan.baseTreeUnreachable ? { baseTreeUnreachable: true } : {};
23079
23079
  if (merged2.hasConflicts) {
23080
23080
  return {
@@ -23090,8 +23090,8 @@ async function runMergeAndRecord(args) {
23090
23090
  return { file: resolvedPath, status: "merged", ...unreachableFlag };
23091
23091
  }
23092
23092
  const merged = threeWayMerge(plan.mergeBase, ours, plan.theirs);
23093
- await (0, import_promises6.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23094
- await (0, import_promises6.writeFile)(oursPath, merged.content);
23093
+ await (0, import_promises7.mkdir)((0, import_path12.dirname)(oursPath), { recursive: true });
23094
+ await (0, import_promises7.writeFile)(oursPath, merged.content);
23095
23095
  if (merged.hasConflicts) {
23096
23096
  accumulator.recordConflict(resolvedPath, plan.theirs, patch.base_generation);
23097
23097
  return {
@@ -23357,17 +23357,17 @@ function overlayRegions(currentGenLines, locInCurrentGen, workingTreeLines, locI
23357
23357
  }
23358
23358
  async function unifiedDiff(beforeContent, afterContent, filePath) {
23359
23359
  if (beforeContent === afterContent) return "";
23360
- const tmp4 = await (0, import_promises8.mkdtemp)((0, import_path15.join)((0, import_os3.tmpdir)(), "fern-replay-pp-"));
23360
+ const tmp4 = await (0, import_promises9.mkdtemp)((0, import_path15.join)((0, import_os3.tmpdir)(), "fern-replay-pp-"));
23361
23361
  try {
23362
23362
  const beforePath = (0, import_path15.join)(tmp4, "before");
23363
23363
  const afterPath = (0, import_path15.join)(tmp4, "after");
23364
- await (0, import_promises8.writeFile)(beforePath, beforeContent, "utf-8");
23365
- await (0, import_promises8.writeFile)(afterPath, afterContent, "utf-8");
23364
+ await (0, import_promises9.writeFile)(beforePath, beforeContent, "utf-8");
23365
+ await (0, import_promises9.writeFile)(afterPath, afterContent, "utf-8");
23366
23366
  const raw = await runGitDiffNoIndex(beforePath, afterPath);
23367
23367
  if (!raw.trim()) return "";
23368
23368
  return rewriteDiffHeaders(raw, filePath);
23369
23369
  } finally {
23370
- await (0, import_promises8.rm)(tmp4, { recursive: true, force: true });
23370
+ await (0, import_promises9.rm)(tmp4, { recursive: true, force: true });
23371
23371
  }
23372
23372
  }
23373
23373
  function runGitDiffNoIndex(beforePath, afterPath) {
@@ -24060,7 +24060,7 @@ async function resolve7(outputDir, options) {
24060
24060
  const result = await computePerPatchDiffWithFallback(
24061
24061
  patch,
24062
24062
  (f) => git.showFile(currentGen, f),
24063
- (f) => (0, import_promises9.readFile)((0, import_path18.join)(outputDir, f), "utf-8").catch(() => null),
24063
+ (f) => (0, import_promises10.readFile)((0, import_path18.join)(outputDir, f), "utf-8").catch(() => null),
24064
24064
  (files) => git.exec(["diff", currentGen, "--", ...files]).catch(() => null)
24065
24065
  );
24066
24066
  if (result === null) {
@@ -24136,7 +24136,7 @@ async function resolve7(outputDir, options) {
24136
24136
  const theirsSnapshot = {};
24137
24137
  for (const file of filesForSnapshot) {
24138
24138
  if (isBinaryFile(file)) continue;
24139
- const content = await (0, import_promises9.readFile)((0, import_path18.join)(outputDir, file), "utf-8").catch(() => null);
24139
+ const content = await (0, import_promises10.readFile)((0, import_path18.join)(outputDir, file), "utf-8").catch(() => null);
24140
24140
  if (content != null) {
24141
24141
  theirsSnapshot[file] = content;
24142
24142
  }
@@ -24260,12 +24260,12 @@ function status(outputDir) {
24260
24260
  legacyDismissedCount
24261
24261
  };
24262
24262
  }
24263
- var import_promises3, import_os, import_path7, import_fs6, import_path8, import_yaml, import_crypto, import_path9, import_promises4, import_os2, import_path10, import_promises5, import_path11, import_promises6, import_path12, import_fs7, import_promises7, import_path13, import_fs8, import_path14, import_promises8, import_os3, import_path15, import_child_process3, import_crypto2, import_fs9, import_path16, import_yaml2, import_crypto3, import_fs10, import_path17, import_fs11, import_promises9, import_path18, __defProp3, __getOwnPropNames3, __esm3, __export3, GitClient_exports, GitClient, init_GitClient, HybridReconstruction_exports, init_HybridReconstruction, PatchApplyTheirs_exports, init_PatchApplyTheirs, CREDENTIAL_PATTERNS, SENSITIVE_FILE_PATTERNS, FERN_BOT_NAME, FERN_BOT_EMAIL, FERN_BOT_LOGIN, FERN_SUPPORT_NAMES, LOCKFILE_HEADER, LockfileNotFoundError, LockfileManager, BINARY_EXTENSIONS, INFRASTRUCTURE_FILES, ReplayDetector, MergeAccumulator, CONFLICT_OPENER, CONFLICT_SEPARATOR, CONFLICT_CLOSER, ReplayApplicator, ReplayCommitter, FileOwnership, FirstGenerationFlow, SkipApplicationFlow, NoPatchesFlow, NormalRegenerationFlow, ReplayService, FernignoreMigrator, REPLAY_FERNIGNORE_ENTRIES, GITATTRIBUTES_ENTRIES, EMPTY_RESULT;
24263
+ var import_promises4, import_os, import_path7, import_fs6, import_path8, import_yaml, import_crypto, import_path9, import_promises5, import_os2, import_path10, import_promises6, import_path11, import_promises7, import_path12, import_fs7, import_promises8, import_path13, import_fs8, import_path14, import_promises9, import_os3, import_path15, import_child_process3, import_crypto2, import_fs9, import_path16, import_yaml2, import_crypto3, import_fs10, import_path17, import_fs11, import_promises10, import_path18, __defProp3, __getOwnPropNames3, __esm3, __export3, GitClient_exports, GitClient, init_GitClient, HybridReconstruction_exports, init_HybridReconstruction, PatchApplyTheirs_exports, init_PatchApplyTheirs, CREDENTIAL_PATTERNS, SENSITIVE_FILE_PATTERNS, FERN_BOT_NAME, FERN_BOT_EMAIL, FERN_BOT_LOGIN, FERN_SUPPORT_NAMES, LOCKFILE_HEADER, LockfileNotFoundError, LockfileManager, BINARY_EXTENSIONS, INFRASTRUCTURE_FILES, ReplayDetector, MergeAccumulator, CONFLICT_OPENER, CONFLICT_SEPARATOR, CONFLICT_CLOSER, ReplayApplicator, ReplayCommitter, FileOwnership, FirstGenerationFlow, SkipApplicationFlow, NoPatchesFlow, NormalRegenerationFlow, ReplayService, FernignoreMigrator, REPLAY_FERNIGNORE_ENTRIES, GITATTRIBUTES_ENTRIES, EMPTY_RESULT;
24264
24264
  var init_dist3 = __esm({
24265
24265
  "../../node_modules/.pnpm/@fern-api+replay@0.19.0/node_modules/@fern-api/replay/dist/index.js"() {
24266
24266
  "use strict";
24267
24267
  init_esm4();
24268
- import_promises3 = require("fs/promises");
24268
+ import_promises4 = require("fs/promises");
24269
24269
  import_os = require("os");
24270
24270
  import_path7 = require("path");
24271
24271
  import_fs6 = require("fs");
@@ -24276,21 +24276,21 @@ var init_dist3 = __esm({
24276
24276
  init_esm3();
24277
24277
  import_path9 = require("path");
24278
24278
  init_diff3();
24279
- import_promises4 = require("fs/promises");
24279
+ import_promises5 = require("fs/promises");
24280
24280
  import_os2 = require("os");
24281
24281
  import_path10 = require("path");
24282
24282
  init_esm3();
24283
- import_promises5 = require("fs/promises");
24284
- import_path11 = require("path");
24285
24283
  import_promises6 = require("fs/promises");
24284
+ import_path11 = require("path");
24285
+ import_promises7 = require("fs/promises");
24286
24286
  import_path12 = require("path");
24287
24287
  import_fs7 = require("fs");
24288
- import_promises7 = require("fs/promises");
24288
+ import_promises8 = require("fs/promises");
24289
24289
  import_path13 = require("path");
24290
24290
  init_esm3();
24291
24291
  import_fs8 = require("fs");
24292
24292
  import_path14 = require("path");
24293
- import_promises8 = require("fs/promises");
24293
+ import_promises9 = require("fs/promises");
24294
24294
  import_os3 = require("os");
24295
24295
  import_path15 = require("path");
24296
24296
  import_child_process3 = require("child_process");
@@ -24305,7 +24305,7 @@ var init_dist3 = __esm({
24305
24305
  import_path17 = require("path");
24306
24306
  init_esm3();
24307
24307
  import_fs11 = require("fs");
24308
- import_promises9 = require("fs/promises");
24308
+ import_promises10 = require("fs/promises");
24309
24309
  import_path18 = require("path");
24310
24310
  __defProp3 = Object.defineProperty;
24311
24311
  __getOwnPropNames3 = Object.getOwnPropertyNames;
@@ -25579,9 +25579,9 @@ var init_dist3 = __esm({
25579
25579
  if (laterFiles.has(fileResult.file) || laterFiles.has(originalPath)) {
25580
25580
  const filePath = (0, import_path10.join)(this.outputDir, fileResult.file);
25581
25581
  try {
25582
- const content = await (0, import_promises4.readFile)(filePath, "utf-8");
25582
+ const content = await (0, import_promises5.readFile)(filePath, "utf-8");
25583
25583
  const stripped = stripConflictMarkers(content);
25584
- await (0, import_promises4.writeFile)(filePath, stripped);
25584
+ await (0, import_promises5.writeFile)(filePath, stripped);
25585
25585
  } catch {
25586
25586
  }
25587
25587
  }
@@ -25601,7 +25601,7 @@ var init_dist3 = __esm({
25601
25601
  async populateAccumulatorForPatch(patch, baseGen, currentTreeHash) {
25602
25602
  const fileResults = [];
25603
25603
  if (!baseGen) return fileResults;
25604
- const tempDir = await (0, import_promises4.mkdtemp)((0, import_path10.join)((0, import_os2.tmpdir)(), "replay-acc-"));
25604
+ const tempDir = await (0, import_promises5.mkdtemp)((0, import_path10.join)((0, import_os2.tmpdir)(), "replay-acc-"));
25605
25605
  const { GitClient: GitClient2 } = await Promise.resolve().then(() => (init_GitClient(), GitClient_exports));
25606
25606
  const tempGit = new GitClient2(tempDir);
25607
25607
  await tempGit.exec(["init"]);
@@ -25615,7 +25615,7 @@ var init_dist3 = __esm({
25615
25615
  const snapshotForFile = patch.theirs_snapshot?.[resolvedPath] ?? patch.theirs_snapshot?.[filePath];
25616
25616
  const fileIsShared = this.accumulator.isShared(resolvedPath) || this.accumulator.isShared(filePath);
25617
25617
  const theirs = !fileIsShared && snapshotForFile != null ? snapshotForFile : await this.applyPatchToContent(base, patch.patch_content, filePath, tempGit, tempDir);
25618
- const finalOnDisk = await (0, import_promises4.readFile)((0, import_path10.join)(this.outputDir, resolvedPath), "utf-8").catch(() => null);
25618
+ const finalOnDisk = await (0, import_promises5.readFile)((0, import_path10.join)(this.outputDir, resolvedPath), "utf-8").catch(() => null);
25619
25619
  const effectiveTheirs = theirs ?? finalOnDisk;
25620
25620
  if (effectiveTheirs != null) {
25621
25621
  this.accumulator.recordMerge(resolvedPath, effectiveTheirs, patch.base_generation);
@@ -25632,7 +25632,7 @@ var init_dist3 = __esm({
25632
25632
  }
25633
25633
  }
25634
25634
  } finally {
25635
- await (0, import_promises4.rm)(tempDir, { recursive: true }).catch(() => {
25635
+ await (0, import_promises5.rm)(tempDir, { recursive: true }).catch(() => {
25636
25636
  });
25637
25637
  }
25638
25638
  return fileResults;
@@ -25662,7 +25662,7 @@ var init_dist3 = __esm({
25662
25662
  const snapshots = /* @__PURE__ */ new Map();
25663
25663
  for (const filePath of patch.files) {
25664
25664
  const fullPath = (0, import_path10.join)(this.outputDir, filePath);
25665
- snapshots.set(filePath, await (0, import_promises4.readFile)(fullPath, "utf-8").catch(() => null));
25665
+ snapshots.set(filePath, await (0, import_promises5.readFile)(fullPath, "utf-8").catch(() => null));
25666
25666
  }
25667
25667
  try {
25668
25668
  await this.git.execWithInput(["apply", "--3way"], patch.patch_content);
@@ -25678,9 +25678,9 @@ var init_dist3 = __esm({
25678
25678
  for (const [filePath, content] of snapshots) {
25679
25679
  const fullPath = (0, import_path10.join)(this.outputDir, filePath);
25680
25680
  if (content != null) {
25681
- await (0, import_promises4.writeFile)(fullPath, content);
25681
+ await (0, import_promises5.writeFile)(fullPath, content);
25682
25682
  } else {
25683
- await (0, import_promises4.unlink)(fullPath).catch(() => {
25683
+ await (0, import_promises5.unlink)(fullPath).catch(() => {
25684
25684
  });
25685
25685
  }
25686
25686
  }
@@ -25691,7 +25691,7 @@ var init_dist3 = __esm({
25691
25691
  async applyWithThreeWayMerge(patch) {
25692
25692
  const fileResults = [];
25693
25693
  const resolvedFiles = {};
25694
- const tempDir = await (0, import_promises4.mkdtemp)((0, import_path10.join)((0, import_os2.tmpdir)(), "replay-"));
25694
+ const tempDir = await (0, import_promises5.mkdtemp)((0, import_path10.join)((0, import_os2.tmpdir)(), "replay-"));
25695
25695
  const { GitClient: GitClient2 } = await Promise.resolve().then(() => (init_GitClient(), GitClient_exports));
25696
25696
  const tempGit = new GitClient2(tempDir);
25697
25697
  await tempGit.exec(["init"]);
@@ -25714,7 +25714,7 @@ var init_dist3 = __esm({
25714
25714
  fileResults.push(result);
25715
25715
  }
25716
25716
  } finally {
25717
- await (0, import_promises4.rm)(tempDir, { recursive: true }).catch(() => {
25717
+ await (0, import_promises5.rm)(tempDir, { recursive: true }).catch(() => {
25718
25718
  });
25719
25719
  }
25720
25720
  const conflictFiles = fileResults.filter((r2) => r2.status === "conflict");
@@ -25820,8 +25820,8 @@ var init_dist3 = __esm({
25820
25820
  try {
25821
25821
  if (sourceFilePath) {
25822
25822
  const tempSourcePath = (0, import_path10.join)(tempDir, sourceFilePath);
25823
- await (0, import_promises4.mkdir)((0, import_path10.dirname)(tempSourcePath), { recursive: true });
25824
- await (0, import_promises4.writeFile)(tempSourcePath, base);
25823
+ await (0, import_promises5.mkdir)((0, import_path10.dirname)(tempSourcePath), { recursive: true });
25824
+ await (0, import_promises5.writeFile)(tempSourcePath, base);
25825
25825
  await tempGit.exec(["add", sourceFilePath]);
25826
25826
  await tempGit.exec([
25827
25827
  "commit",
@@ -25831,15 +25831,15 @@ var init_dist3 = __esm({
25831
25831
  ]);
25832
25832
  await tempGit.execWithInput(["apply", "--allow-empty"], fileDiff);
25833
25833
  const tempTargetPath = (0, import_path10.join)(tempDir, filePath);
25834
- return await (0, import_promises4.readFile)(tempTargetPath, "utf-8");
25834
+ return await (0, import_promises5.readFile)(tempTargetPath, "utf-8");
25835
25835
  }
25836
25836
  const tempFilePath = (0, import_path10.join)(tempDir, filePath);
25837
- await (0, import_promises4.mkdir)((0, import_path10.dirname)(tempFilePath), { recursive: true });
25838
- await (0, import_promises4.writeFile)(tempFilePath, base);
25837
+ await (0, import_promises5.mkdir)((0, import_path10.dirname)(tempFilePath), { recursive: true });
25838
+ await (0, import_promises5.writeFile)(tempFilePath, base);
25839
25839
  await tempGit.exec(["add", filePath]);
25840
25840
  await tempGit.exec(["commit", "-m", `base for ${filePath}`, "--allow-empty"]);
25841
25841
  await tempGit.execWithInput(["apply", "--allow-empty"], fileDiff);
25842
- return await (0, import_promises4.readFile)(tempFilePath, "utf-8");
25842
+ return await (0, import_promises5.readFile)(tempFilePath, "utf-8");
25843
25843
  } catch {
25844
25844
  return null;
25845
25845
  }
@@ -25856,8 +25856,8 @@ var init_dist3 = __esm({
25856
25856
  if (!fileDiff) return false;
25857
25857
  try {
25858
25858
  const tempFilePath = (0, import_path10.join)(tempDir, filePath);
25859
- await (0, import_promises4.mkdir)((0, import_path10.dirname)(tempFilePath), { recursive: true });
25860
- await (0, import_promises4.writeFile)(tempFilePath, content);
25859
+ await (0, import_promises5.mkdir)((0, import_path10.dirname)(tempFilePath), { recursive: true });
25860
+ await (0, import_promises5.writeFile)(tempFilePath, content);
25861
25861
  await tempGit.exec(["add", filePath]);
25862
25862
  await tempGit.exec(["commit", "-m", `check already-applied ${filePath}`, "--allow-empty"]);
25863
25863
  await tempGit.execWithInput(["apply", "--reverse", "--check", "--allow-empty"], fileDiff);
@@ -26941,7 +26941,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
26941
26941
  for (const file of files) {
26942
26942
  if (isBinaryFile(file)) continue;
26943
26943
  try {
26944
- const content = await (0, import_promises7.readFile)((0, import_path13.join)(this.outputDir, file), "utf-8");
26944
+ const content = await (0, import_promises8.readFile)((0, import_path13.join)(this.outputDir, file), "utf-8");
26945
26945
  snapshot[file] = content;
26946
26946
  } catch {
26947
26947
  }
@@ -30360,7 +30360,7 @@ function resolve(...paths) {
30360
30360
 
30361
30361
  // src/cli.ts
30362
30362
  var import_fs17 = __toESM(require("fs"));
30363
- var import_promises18 = require("fs/promises");
30363
+ var import_promises19 = require("fs/promises");
30364
30364
  var import_path26 = __toESM(require("path"));
30365
30365
 
30366
30366
  // ../../node_modules/.pnpm/yargs@17.7.2/node_modules/yargs/build/lib/yerror.js
@@ -36120,6 +36120,7 @@ var Octokit2 = Octokit.plugin(requestLog, legacyRestEndpointMethods, paginateRes
36120
36120
  );
36121
36121
 
36122
36122
  // src/github/GitHub.ts
36123
+ var import_promises3 = require("fs/promises");
36123
36124
  var GitHub = class {
36124
36125
  githubConfig;
36125
36126
  constructor({
@@ -36146,9 +36147,10 @@ var GitHub = class {
36146
36147
  await repository.pull(branch);
36147
36148
  }
36148
36149
  const fernIgnoreFiles = await this.getFernignoreFiles(repository);
36150
+ const changelogFiles = await this.getChangelogFilesToPreserve(repository, sourceDirectory);
36149
36151
  await repository.overwriteLocalContents(sourceDirectory);
36150
36152
  await repository.add(".");
36151
- await this.restoreFiles(repository, fernIgnoreFiles);
36153
+ await this.restoreFiles(repository, [...fernIgnoreFiles, ...changelogFiles]);
36152
36154
  await repository.commit("SDK Generation");
36153
36155
  if (isEmptyRepo) {
36154
36156
  await repository.pushUpstream(branch);
@@ -36185,9 +36187,10 @@ var GitHub = class {
36185
36187
  const prBranch = `fern-bot/${formattedDate}`;
36186
36188
  await repository.checkout(prBranch);
36187
36189
  const fernIgnoreFiles = await this.getFernignoreFiles(repository);
36190
+ const changelogFiles = await this.getChangelogFilesToPreserve(repository, sourceDirectory);
36188
36191
  await repository.overwriteLocalContents(sourceDirectory);
36189
36192
  await repository.add(".");
36190
- await this.restoreFiles(repository, fernIgnoreFiles);
36193
+ await this.restoreFiles(repository, [...fernIgnoreFiles, ...changelogFiles]);
36191
36194
  await repository.commit("SDK Generation");
36192
36195
  await repository.push();
36193
36196
  const apiBaseUrl = getGithubApiBaseUrl(this.githubConfig.uri);
@@ -36231,6 +36234,25 @@ var GitHub = class {
36231
36234
  const tracked = await repository.listTrackedFiles();
36232
36235
  return expandFernignorePatterns(fernignore, tracked);
36233
36236
  }
36237
+ /**
36238
+ * Returns tracked changelog files (e.g. `changelog.md`) that should be restored after
36239
+ * `overwriteLocalContents`, so existing changelog entries are never wiped when the
36240
+ * generated output doesn't include a changelog of its own.
36241
+ */
36242
+ async getChangelogFilesToPreserve(repository, sourceDirectory) {
36243
+ const tracked = await repository.listTrackedFiles();
36244
+ const trackedChangelogs = tracked.filter((file) => file.toLowerCase() === "changelog.md");
36245
+ if (trackedChangelogs.length === 0) {
36246
+ return [];
36247
+ }
36248
+ try {
36249
+ const sourceFiles = await (0, import_promises3.readdir)(sourceDirectory);
36250
+ const sourceHasChangelog = sourceFiles.some((file) => file.toLowerCase() === "changelog.md");
36251
+ return sourceHasChangelog ? [] : trackedChangelogs;
36252
+ } catch {
36253
+ return trackedChangelogs;
36254
+ }
36255
+ }
36234
36256
  async restoreFiles(repository, files) {
36235
36257
  if (files.length === 0) {
36236
36258
  return;
@@ -40114,7 +40136,7 @@ var import_path20 = require("path");
40114
40136
  var import_tmp_promise2 = __toESM(require_tmp_promise());
40115
40137
 
40116
40138
  // src/replay/fernignore.ts
40117
- var import_promises10 = require("fs/promises");
40139
+ var import_promises11 = require("fs/promises");
40118
40140
  var import_path19 = require("path");
40119
40141
  var REPLAY_FERNIGNORE_ENTRIES2 = [".fern/replay.lock", ".fern/replay.yml", ".gitattributes"];
40120
40142
  var GITATTRIBUTES_ENTRIES2 = [".fern/replay.lock linguist-generated=true"];
@@ -40122,14 +40144,14 @@ async function ensureReplayFernignoreEntries(outputDir) {
40122
40144
  const fernignorePath = (0, import_path19.join)(outputDir, ".fernignore");
40123
40145
  let content = "";
40124
40146
  try {
40125
- content = await (0, import_promises10.readFile)(fernignorePath, "utf-8");
40147
+ content = await (0, import_promises11.readFile)(fernignorePath, "utf-8");
40126
40148
  } catch {
40127
40149
  }
40128
40150
  const lines = content.split("\n");
40129
40151
  const toAdd = REPLAY_FERNIGNORE_ENTRIES2.filter((entry) => !lines.some((line) => line.trim() === entry));
40130
40152
  if (toAdd.length > 0) {
40131
40153
  const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
40132
- await (0, import_promises10.writeFile)(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
40154
+ await (0, import_promises11.writeFile)(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
40133
40155
  return true;
40134
40156
  }
40135
40157
  return false;
@@ -40138,14 +40160,14 @@ async function ensureGitattributesEntries2(outputDir) {
40138
40160
  const gitattributesPath = (0, import_path19.join)(outputDir, ".gitattributes");
40139
40161
  let content = "";
40140
40162
  try {
40141
- content = await (0, import_promises10.readFile)(gitattributesPath, "utf-8");
40163
+ content = await (0, import_promises11.readFile)(gitattributesPath, "utf-8");
40142
40164
  } catch {
40143
40165
  }
40144
40166
  const lines = content.split("\n");
40145
40167
  const toAdd = GITATTRIBUTES_ENTRIES2.filter((entry) => !lines.some((line) => line.trim() === entry));
40146
40168
  if (toAdd.length > 0) {
40147
40169
  const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
40148
- await (0, import_promises10.writeFile)(gitattributesPath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
40170
+ await (0, import_promises11.writeFile)(gitattributesPath, content + suffix + toAdd.join("\n") + "\n", "utf-8");
40149
40171
  }
40150
40172
  }
40151
40173
 
@@ -40429,29 +40451,29 @@ function replayStatus(params) {
40429
40451
  }
40430
40452
 
40431
40453
  // src/configuration/loadGitHubConfig.ts
40432
- var import_promises11 = require("fs/promises");
40454
+ var import_promises12 = require("fs/promises");
40433
40455
  async function loadGitHubConfig({
40434
40456
  absolutePathToConfig
40435
40457
  }) {
40436
- const rawContents = await (0, import_promises11.readFile)(absolutePathToConfig, "utf8");
40458
+ const rawContents = await (0, import_promises12.readFile)(absolutePathToConfig, "utf8");
40437
40459
  return JSON.parse(rawContents);
40438
40460
  }
40439
40461
 
40440
40462
  // src/configuration/loadReadmeConfig.ts
40441
- var import_promises12 = require("fs/promises");
40463
+ var import_promises13 = require("fs/promises");
40442
40464
  async function loadReadmeConfig({
40443
40465
  absolutePathToConfig
40444
40466
  }) {
40445
- const rawContents = await (0, import_promises12.readFile)(absolutePathToConfig, "utf8");
40467
+ const rawContents = await (0, import_promises13.readFile)(absolutePathToConfig, "utf8");
40446
40468
  return JSON.parse(rawContents);
40447
40469
  }
40448
40470
 
40449
40471
  // src/configuration/loadReferenceConfig.ts
40450
- var import_promises13 = require("fs/promises");
40472
+ var import_promises14 = require("fs/promises");
40451
40473
  async function loadReferenceConfig({
40452
40474
  absolutePathToConfig
40453
40475
  }) {
40454
- const rawContents = await (0, import_promises13.readFile)(absolutePathToConfig, "utf8");
40476
+ const rawContents = await (0, import_promises14.readFile)(absolutePathToConfig, "utf8");
40455
40477
  return JSON.parse(rawContents);
40456
40478
  }
40457
40479
 
@@ -40478,7 +40500,7 @@ var FERN_BOT_LOGIN2 = "fern-api[bot]";
40478
40500
  // src/pipeline/steps/AutoVersionStep.ts
40479
40501
  var import_child_process4 = require("child_process");
40480
40502
  var import_fs15 = require("fs");
40481
- var import_promises15 = require("fs/promises");
40503
+ var import_promises16 = require("fs/promises");
40482
40504
  var import_path23 = require("path");
40483
40505
 
40484
40506
  // ../commons/logging-execa/lib/loggingExeca.js
@@ -40538,7 +40560,7 @@ async function loggingExeca(logger, executable, args = [], { doNotPipeOutput = f
40538
40560
 
40539
40561
  // src/autoversion/AutoVersioningService.ts
40540
40562
  var import_fs14 = require("fs");
40541
- var import_promises14 = require("fs/promises");
40563
+ var import_promises15 = require("fs/promises");
40542
40564
  var import_path22 = require("path");
40543
40565
  var import_semver = __toESM(require_semver2());
40544
40566
  var AutoVersioningException = class extends Error {
@@ -41293,12 +41315,12 @@ var AutoVersioningService = class _AutoVersioningService {
41293
41315
  if (await this.isBinaryFile(filePath)) {
41294
41316
  return false;
41295
41317
  }
41296
- const content = await (0, import_promises14.readFile)(filePath, "utf-8");
41318
+ const content = await (0, import_promises15.readFile)(filePath, "utf-8");
41297
41319
  if (!content.includes(mappedMagicVersion)) {
41298
41320
  return false;
41299
41321
  }
41300
41322
  const updated = content.split(mappedMagicVersion).join(finalVersion);
41301
- await (0, import_promises14.writeFile)(filePath, updated, "utf-8");
41323
+ await (0, import_promises15.writeFile)(filePath, updated, "utf-8");
41302
41324
  return true;
41303
41325
  };
41304
41326
  const results = await this.processInParallel(files, replaceInFile);
@@ -41306,7 +41328,7 @@ var AutoVersioningService = class _AutoVersioningService {
41306
41328
  this.logger.debug(`Placeholder version replaced successfully in ${replacedCount} file(s)`);
41307
41329
  }
41308
41330
  async isBinaryFile(filePath) {
41309
- const fileHandle = await (0, import_promises14.open)(filePath, "r");
41331
+ const fileHandle = await (0, import_promises15.open)(filePath, "r");
41310
41332
  try {
41311
41333
  const header = Buffer.alloc(8192);
41312
41334
  const { bytesRead } = await fileHandle.read(header, 0, 8192, 0);
@@ -41373,7 +41395,7 @@ var AutoVersioningService = class _AutoVersioningService {
41373
41395
  * Reads the module path from a go.mod file.
41374
41396
  */
41375
41397
  async readGoModulePath(goModPath) {
41376
- const content = await (0, import_promises14.readFile)(goModPath, "utf-8");
41398
+ const content = await (0, import_promises15.readFile)(goModPath, "utf-8");
41377
41399
  for (const line of content.split("\n")) {
41378
41400
  const trimmed2 = line.trim();
41379
41401
  if (trimmed2.startsWith("module ")) {
@@ -41386,7 +41408,7 @@ var AutoVersioningService = class _AutoVersioningService {
41386
41408
  * Adds a version suffix to the module line in go.mod.
41387
41409
  */
41388
41410
  async addSuffixToGoMod(goModPath, modulePath, suffix) {
41389
- const content = await (0, import_promises14.readFile)(goModPath, "utf-8");
41411
+ const content = await (0, import_promises15.readFile)(goModPath, "utf-8");
41390
41412
  const lines = content.split("\n");
41391
41413
  let changed = false;
41392
41414
  for (let i2 = 0; i2 < lines.length; i2++) {
@@ -41398,7 +41420,7 @@ var AutoVersioningService = class _AutoVersioningService {
41398
41420
  }
41399
41421
  }
41400
41422
  if (changed) {
41401
- await (0, import_promises14.writeFile)(goModPath, lines.join("\n"), "utf-8");
41423
+ await (0, import_promises15.writeFile)(goModPath, lines.join("\n"), "utf-8");
41402
41424
  this.logger.debug(`Updated go.mod: added ${suffix} to module path`);
41403
41425
  }
41404
41426
  }
@@ -41407,10 +41429,10 @@ var AutoVersioningService = class _AutoVersioningService {
41407
41429
  */
41408
41430
  async walkDirectory(dir, filter2) {
41409
41431
  const results = [];
41410
- const entries2 = await (0, import_promises14.readdir)(dir);
41432
+ const entries2 = await (0, import_promises15.readdir)(dir);
41411
41433
  for (const entry of entries2) {
41412
41434
  const fullPath = (0, import_path22.join)(dir, entry);
41413
- const entryStat = await (0, import_promises14.lstat)(fullPath);
41435
+ const entryStat = await (0, import_promises15.lstat)(fullPath);
41414
41436
  if (entryStat.isSymbolicLink()) {
41415
41437
  continue;
41416
41438
  }
@@ -41449,7 +41471,7 @@ var AutoVersioningService = class _AutoVersioningService {
41449
41471
  * literals in a single .go file.
41450
41472
  */
41451
41473
  async addSuffixToGoFile(filePath, modulePath, suffix) {
41452
- const content = await (0, import_promises14.readFile)(filePath, "utf-8");
41474
+ const content = await (0, import_promises15.readFile)(filePath, "utf-8");
41453
41475
  const lines = content.split("\n");
41454
41476
  let changed = false;
41455
41477
  let inImportBlock = false;
@@ -41475,7 +41497,7 @@ var AutoVersioningService = class _AutoVersioningService {
41475
41497
  }
41476
41498
  }
41477
41499
  if (changed) {
41478
- await (0, import_promises14.writeFile)(filePath, lines.join("\n"), "utf-8");
41500
+ await (0, import_promises15.writeFile)(filePath, lines.join("\n"), "utf-8");
41479
41501
  this.logger.debug(`Updated Go imports in ${filePath}`);
41480
41502
  }
41481
41503
  }
@@ -41502,7 +41524,7 @@ var AutoVersioningService = class _AutoVersioningService {
41502
41524
  * preceded by an install command.
41503
41525
  */
41504
41526
  async addSuffixToMarkdownFile(filePath, modulePath, suffix) {
41505
- const content = await (0, import_promises14.readFile)(filePath, "utf-8");
41527
+ const content = await (0, import_promises15.readFile)(filePath, "utf-8");
41506
41528
  if (!content.includes(modulePath)) {
41507
41529
  return;
41508
41530
  }
@@ -41514,7 +41536,7 @@ var AutoVersioningService = class _AutoVersioningService {
41514
41536
  );
41515
41537
  updated = updated.replace(installCommandPattern, `$1${modulePath}${suffix}`);
41516
41538
  if (updated !== content) {
41517
- await (0, import_promises14.writeFile)(filePath, updated, "utf-8");
41539
+ await (0, import_promises15.writeFile)(filePath, updated, "utf-8");
41518
41540
  this.logger.debug(`Updated Go module path in ${filePath}`);
41519
41541
  }
41520
41542
  }
@@ -41560,6 +41582,41 @@ var AutoVersioningService = class _AutoVersioningService {
41560
41582
  }
41561
41583
  };
41562
41584
 
41585
+ // src/autoversion/changelogUtils.ts
41586
+ function prependChangelogBlock({
41587
+ existingContent,
41588
+ version: version2,
41589
+ entry,
41590
+ date: date2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
41591
+ }) {
41592
+ const header = version2 != null ? `## [${version2}] - ${date2}
41593
+ ` : `## ${date2}
41594
+ `;
41595
+ const trimmedEntry = entry.trim();
41596
+ const newBlock = trimmedEntry.length > 0 ? `${header}${trimmedEntry}
41597
+
41598
+ ` : `${header}
41599
+ `;
41600
+ if (existingContent.trim().length === 0) {
41601
+ return `# Changelog
41602
+
41603
+ ${newBlock}`;
41604
+ }
41605
+ if (existingContent.startsWith("# Changelog")) {
41606
+ const newlineIdx = existingContent.indexOf("\n");
41607
+ const headerLine = newlineIdx >= 0 ? existingContent.slice(0, newlineIdx) : existingContent;
41608
+ const remainder = (newlineIdx >= 0 ? existingContent.slice(newlineIdx + 1) : "").replace(/^\s*\n/, "");
41609
+ return `${headerLine}
41610
+
41611
+ ${newBlock}${remainder}`;
41612
+ }
41613
+ return `${newBlock}${existingContent}`;
41614
+ }
41615
+ function changelogContainsVersion(content, version2) {
41616
+ const escaped = version2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
41617
+ return new RegExp(`^## \\[${escaped}\\]`, "m").test(content);
41618
+ }
41619
+
41563
41620
  // src/autoversion/VersionUtils.ts
41564
41621
  var import_semver2 = __toESM(require_semver2());
41565
41622
  var MAGIC_VERSION = "0.0.0-fern-placeholder";
@@ -42237,32 +42294,11 @@ ${body}` : `${COMMIT_MARKER} ${subject}`;
42237
42294
  async prependChangelogEntry(params) {
42238
42295
  const { version: version2, entry } = params;
42239
42296
  const changelogPath = (0, import_path23.join)(this.outputDir, "changelog.md");
42240
- const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
42241
- const header = `## [${version2}] - ${now}
42242
- `;
42243
- const newBlock = `${header}${entry.trim()}
42244
-
42245
- `;
42246
42297
  let existing = "";
42247
42298
  if ((0, import_fs15.existsSync)(changelogPath)) {
42248
- existing = await (0, import_promises15.readFile)(changelogPath, "utf-8");
42299
+ existing = await (0, import_promises16.readFile)(changelogPath, "utf-8");
42249
42300
  }
42250
- let output;
42251
- if (existing.trim().length === 0) {
42252
- output = `# Changelog
42253
-
42254
- ${newBlock}`;
42255
- } else if (existing.startsWith("# Changelog")) {
42256
- const newlineIdx = existing.indexOf("\n");
42257
- const headerLine = newlineIdx >= 0 ? existing.slice(0, newlineIdx) : existing;
42258
- const remainder = (newlineIdx >= 0 ? existing.slice(newlineIdx + 1) : "").replace(/^\s*\n/, "");
42259
- output = `${headerLine}
42260
-
42261
- ${newBlock}${remainder}`;
42262
- } else {
42263
- output = `${newBlock}${existing}`;
42264
- }
42265
- await (0, import_promises15.writeFile)(changelogPath, output, "utf-8");
42301
+ await (0, import_promises16.writeFile)(changelogPath, prependChangelogBlock({ existingContent: existing, version: version2, entry }), "utf-8");
42266
42302
  }
42267
42303
  brandMessage(message) {
42268
42304
  if (this.config.isWhitelabel) {
@@ -42536,7 +42572,7 @@ function tryRevParse(cwd2, rev) {
42536
42572
  }
42537
42573
 
42538
42574
  // src/pipeline/steps/GithubStep.ts
42539
- var import_promises16 = require("fs/promises");
42575
+ var import_promises17 = require("fs/promises");
42540
42576
  var import_path24 = require("path");
42541
42577
 
42542
42578
  // src/pipeline/github/createReplayBranch.ts
@@ -43295,39 +43331,50 @@ var GithubStep = class extends BaseStep {
43295
43331
  this.logger.debug("Checking for .fernignore file...");
43296
43332
  const fernignorePath = (0, import_path24.join)(this.outputDir, ".fernignore");
43297
43333
  try {
43298
- await (0, import_promises16.access)(fernignorePath);
43334
+ await (0, import_promises17.access)(fernignorePath);
43299
43335
  this.logger.debug(".fernignore already exists");
43300
43336
  } catch {
43301
43337
  this.logger.debug("Creating .fernignore file...");
43302
- await (0, import_promises16.writeFile)(fernignorePath, "# Specify files that shouldn't be modified by Fern\n", "utf-8");
43338
+ await (0, import_promises17.writeFile)(fernignorePath, "# Specify files that shouldn't be modified by Fern\n", "utf-8");
43303
43339
  }
43304
43340
  }
43305
43341
  /**
43306
- * Guarantees `changelog.md` is present in the output directory whenever a changelog entry
43307
- * exists for this run, so the file lands in the committed tree that GithubStep pushes and the
43342
+ * Guarantees `changelog.md` records this run whenever there is a changelog entry or a
43343
+ * resolved version, so the file lands in the committed tree that GithubStep pushes and the
43308
43344
  * "See full changelog" link resolves. AutoVersionStep normally writes this file via
43309
- * `prependChangelogEntry`; this is a safety net for paths where that write never reached the
43310
- * directory GithubStep commits (e.g. non-replay self-hosted generation), which previously left
43311
- * the link pointing at a non-existent blob (404).
43345
+ * `prependChangelogEntry`; this covers the paths where that write never reached the
43346
+ * directory GithubStep commits (e.g. non-replay self-hosted generation) and explicitly
43347
+ * versioned runs (`--version X.Y.Z`), which record a version-only entry with an empty
43348
+ * description. Existing entries are always preserved — the new block is prepended.
43312
43349
  *
43313
- * Returns `true` when it created the file, `false` when the file already existed (or there is
43314
- * no changelog entry to write).
43350
+ * Returns `true` when it created or modified the file, `false` when the version's entry was
43351
+ * already recorded (e.g. by AutoVersionStep) or there is nothing to write.
43315
43352
  */
43316
43353
  async ensureChangelogFile(resolved) {
43317
- const entry = resolved.changelogEntry?.trim();
43318
- if (entry == null || entry.length === 0) {
43354
+ const entry = resolved.changelogEntry?.trim() ?? "";
43355
+ const version2 = resolved.newVersion ?? resolved.previousVersion;
43356
+ if (entry.length === 0 && resolved.newVersion == null) {
43319
43357
  return false;
43320
43358
  }
43321
43359
  const changelogPath = (0, import_path24.join)(this.outputDir, "changelog.md");
43360
+ let existing;
43322
43361
  try {
43323
- await (0, import_promises16.access)(changelogPath);
43324
- return false;
43362
+ existing = await (0, import_promises17.readFile)(changelogPath, "utf-8");
43325
43363
  } catch {
43326
- const version2 = resolved.newVersion ?? resolved.previousVersion;
43327
- await (0, import_promises16.writeFile)(changelogPath, buildChangelogFileContents(entry, version2), "utf-8");
43328
- this.logger.debug(`Wrote changelog.md${version2 != null ? ` for ${version2}` : ""}.`);
43329
- return true;
43364
+ existing = void 0;
43365
+ }
43366
+ if (existing != null) {
43367
+ if (version2 == null || changelogContainsVersion(existing, version2)) {
43368
+ return false;
43369
+ }
43330
43370
  }
43371
+ await (0, import_promises17.writeFile)(
43372
+ changelogPath,
43373
+ prependChangelogBlock({ existingContent: existing ?? "", version: version2, entry }),
43374
+ "utf-8"
43375
+ );
43376
+ this.logger.debug(`Wrote changelog.md${version2 != null ? ` for ${version2}` : ""}.`);
43377
+ return true;
43331
43378
  }
43332
43379
  deriveSkipCommit(replayResult) {
43333
43380
  if (replayResult == null) {
@@ -43412,18 +43459,6 @@ function resolveBranchAction({
43412
43459
  }
43413
43460
  return "create-from-head";
43414
43461
  }
43415
- function buildChangelogFileContents(entry, version2) {
43416
- const trimmed2 = entry.trim();
43417
- const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
43418
- const header = version2 != null ? `## [${version2}] - ${now}
43419
- ` : `## ${now}
43420
- `;
43421
- return `# Changelog
43422
-
43423
- ${header}${trimmed2}
43424
-
43425
- `;
43426
- }
43427
43462
  function resolveChangelogUrl({
43428
43463
  changelogEntry,
43429
43464
  changelogCommitted,
@@ -43599,7 +43634,7 @@ var ReplayStep = class extends BaseStep {
43599
43634
  };
43600
43635
 
43601
43636
  // ../cli/generation/local-generation/docker-utils/lib/runDocker.js
43602
- var import_promises17 = require("fs/promises");
43637
+ var import_promises18 = require("fs/promises");
43603
43638
  var import_tmp_promise3 = __toESM(require_tmp_promise(), 1);
43604
43639
  async function pullImage(imageName, runner, signal) {
43605
43640
  await loggingExeca(void 0, runner ?? "docker", ["pull", imageName], {
@@ -43647,7 +43682,7 @@ async function execInContainer({ logger, containerId, command, runner, envVars =
43647
43682
  const logs = stdout + stderr;
43648
43683
  if (writeLogsToFile) {
43649
43684
  const tmpFile = await import_tmp_promise3.default.file();
43650
- await (0, import_promises17.writeFile)(tmpFile.path, logs);
43685
+ await (0, import_promises18.writeFile)(tmpFile.path, logs);
43651
43686
  logger.info(`Generator logs here: ${tmpFile.path}`);
43652
43687
  }
43653
43688
  const resolvedExitCode = exitCode ?? 1;
@@ -44017,7 +44052,7 @@ void yargs_default(hideBin(process.argv)).scriptName(process.env.CLI_NAME ?? "ge
44017
44052
  const readmeConfig = await loadReadmeConfig({
44018
44053
  absolutePathToConfig: resolve(wd, argv.config)
44019
44054
  });
44020
- const originalReadmeContent = argv.originalReadme != null ? await (0, import_promises18.readFile)(argv.originalReadme, "utf8") : void 0;
44055
+ const originalReadmeContent = argv.originalReadme != null ? await (0, import_promises19.readFile)(argv.originalReadme, "utf8") : void 0;
44021
44056
  await generateReadmeToStream({
44022
44057
  originalReadmeContent,
44023
44058
  readmeConfig,
@@ -44145,7 +44180,7 @@ void yargs_default(hideBin(process.argv)).scriptName(process.env.CLI_NAME ?? "ge
44145
44180
  try {
44146
44181
  const wd = cwd();
44147
44182
  const configValue = argv.config;
44148
- const configContent = configValue === STDIN_MARKER ? await readStdin() : configValue.trimStart().startsWith("{") ? configValue : await (0, import_promises18.readFile)(resolve(wd, configValue), "utf-8");
44183
+ const configContent = configValue === STDIN_MARKER ? await readStdin() : configValue.trimStart().startsWith("{") ? configValue : await (0, import_promises19.readFile)(resolve(wd, configValue), "utf-8");
44149
44184
  let config;
44150
44185
  try {
44151
44186
  config = JSON.parse(configContent);
@@ -44441,7 +44476,7 @@ async function createWriteStream(outputPath) {
44441
44476
  }
44442
44477
  async function createWriteStreamFromFile(filepath) {
44443
44478
  if (!await doesPathExist(filepath)) {
44444
- await (0, import_promises18.mkdir)(import_path26.default.dirname(filepath), { recursive: true });
44479
+ await (0, import_promises19.mkdir)(import_path26.default.dirname(filepath), { recursive: true });
44445
44480
  }
44446
44481
  return import_fs17.default.createWriteStream(filepath);
44447
44482
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.9.47",
3
+ "version": "0.9.48",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",