@fern-api/generator-cli 0.9.32 → 0.9.33

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 (2) hide show
  1. package/dist/cli.js +128 -16
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -22126,7 +22126,7 @@ var init_diff3 = __esm({
22126
22126
  }
22127
22127
  });
22128
22128
 
22129
- // ../../node_modules/.pnpm/@fern-api+replay@0.16.0/node_modules/@fern-api/replay/dist/index.js
22129
+ // ../../node_modules/.pnpm/@fern-api+replay@0.16.1/node_modules/@fern-api/replay/dist/index.js
22130
22130
  var dist_exports = {};
22131
22131
  __export(dist_exports, {
22132
22132
  CREDENTIAL_PATTERNS: () => CREDENTIAL_PATTERNS,
@@ -23123,12 +23123,23 @@ async function computePerPatchDiff(patch, getCurrentGenContent, getWorkingTreeCo
23123
23123
  unlocatableFiles.push(file);
23124
23124
  continue;
23125
23125
  }
23126
- const locInCurrentGen = locInCurrentGenRaw.map(
23127
- (l) => resolveSpan(l, currentGenLines, "old")
23128
- );
23129
- const locInWorkingTree = locInWorkingTreeRaw.map(
23130
- (l) => resolveSpan(l, workingTreeLines, "new")
23131
- );
23126
+ const locInCurrentGen = [];
23127
+ const locInWorkingTree = [];
23128
+ let spanResolutionFailed = false;
23129
+ for (let i2 = 0; i2 < locInCurrentGenRaw.length; i2++) {
23130
+ const cg = resolveSpan(locInCurrentGenRaw[i2], currentGenLines, "old");
23131
+ const wt = resolveSpan(locInWorkingTreeRaw[i2], workingTreeLines, "new");
23132
+ if (cg === null || wt === null) {
23133
+ spanResolutionFailed = true;
23134
+ break;
23135
+ }
23136
+ locInCurrentGen.push(cg);
23137
+ locInWorkingTree.push(wt);
23138
+ }
23139
+ if (spanResolutionFailed) {
23140
+ unlocatableFiles.push(file);
23141
+ continue;
23142
+ }
23132
23143
  const overlay = overlayRegions(
23133
23144
  currentGenLines,
23134
23145
  locInCurrentGen,
@@ -23190,11 +23201,11 @@ function resolveSpan(loc, oursLines, prefer) {
23190
23201
  if (prefer === "old") {
23191
23202
  if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
23192
23203
  if (newFits) return { ...loc, oursSpan: hunk.newCount };
23193
- } else {
23194
- if (newFits) return { ...loc, oursSpan: hunk.newCount };
23195
- if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
23204
+ return loc;
23196
23205
  }
23197
- return loc;
23206
+ if (newFits) return { ...loc, oursSpan: hunk.newCount };
23207
+ if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
23208
+ return null;
23198
23209
  }
23199
23210
  function sequenceMatches(needle, haystack, offset) {
23200
23211
  if (offset + needle.length > haystack.length) return false;
@@ -23947,7 +23958,7 @@ function status(outputDir) {
23947
23958
  }
23948
23959
  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_promises8, import_os3, import_path14, import_child_process3, import_crypto2, import_fs8, import_path15, import_yaml2, import_crypto3, import_fs9, import_path16, import_fs10, import_promises9, import_path17, __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;
23949
23960
  var init_dist3 = __esm({
23950
- "../../node_modules/.pnpm/@fern-api+replay@0.16.0/node_modules/@fern-api/replay/dist/index.js"() {
23961
+ "../../node_modules/.pnpm/@fern-api+replay@0.16.1/node_modules/@fern-api/replay/dist/index.js"() {
23951
23962
  "use strict";
23952
23963
  init_esm4();
23953
23964
  import_promises3 = require("fs/promises");
@@ -23955,6 +23966,7 @@ var init_dist3 = __esm({
23955
23966
  import_path7 = require("path");
23956
23967
  import_fs6 = require("fs");
23957
23968
  import_path8 = require("path");
23969
+ init_esm3();
23958
23970
  import_yaml = __toESM(require_dist3(), 1);
23959
23971
  import_crypto = require("crypto");
23960
23972
  import_path9 = require("path");
@@ -24187,10 +24199,23 @@ var init_dist3 = __esm({
24187
24199
  LockfileManager = class {
24188
24200
  outputDir;
24189
24201
  lock = null;
24202
+ fernignorePatterns = [];
24190
24203
  warnings = [];
24191
24204
  constructor(outputDir) {
24192
24205
  this.outputDir = outputDir;
24193
24206
  }
24207
+ /**
24208
+ * Inform the lockfile of the customer's `.fernignore` patterns so that
24209
+ * `save()` can suppress its "Patch X removed from lockfile" warning when
24210
+ * every file in the stripped patch is already `.fernignore`-protected.
24211
+ * The strip itself still happens (credentials never persist); only the
24212
+ * customer-visible warning is suppressed in that case, because the
24213
+ * protected files survive the generator wipe regardless of whether the
24214
+ * lockfile tracks them, so there's nothing for the customer to action.
24215
+ */
24216
+ setFernignorePatterns(patterns) {
24217
+ this.fernignorePatterns = patterns;
24218
+ }
24194
24219
  get lockfilePath() {
24195
24220
  return (0, import_path8.join)(this.outputDir, ".fern", "replay.lock");
24196
24221
  }
@@ -24259,9 +24284,14 @@ var init_dist3 = __esm({
24259
24284
  if (sensitiveFiles.length > 0) {
24260
24285
  reasons.push(`sensitive files: ${sensitiveFiles.join(", ")}`);
24261
24286
  }
24262
- this.warnings.push(
24263
- `Patch ${patch.id} removed from lockfile: ${reasons.join("; ")}. This prevents credential exposure in committed lockfiles.`
24287
+ const allProtected = patch.files.length > 0 && patch.files.every(
24288
+ (f) => this.fernignorePatterns.some((p2) => f === p2 || minimatch(f, p2))
24264
24289
  );
24290
+ if (!allProtected) {
24291
+ this.warnings.push(
24292
+ `Patch ${patch.id} removed from lockfile: ${reasons.join("; ")}. This prevents credential exposure in committed lockfiles.`
24293
+ );
24294
+ }
24265
24295
  } else {
24266
24296
  cleanPatches.push(patch);
24267
24297
  }
@@ -25665,6 +25695,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
25665
25695
  };
25666
25696
  }
25667
25697
  async apply(_prep) {
25698
+ this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
25668
25699
  this.service.lockManager.save();
25669
25700
  return firstGenerationReport();
25670
25701
  }
@@ -25711,6 +25742,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
25711
25742
  };
25712
25743
  }
25713
25744
  async apply(_prep, options) {
25745
+ this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
25714
25746
  this.service.lockManager.save();
25715
25747
  const credentialWarnings = [...this.service.lockManager.warnings];
25716
25748
  if (!options?.stageOnly) {
@@ -25832,6 +25864,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
25832
25864
  this.service.lockManager.addPatch(patch);
25833
25865
  }
25834
25866
  }
25867
+ this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
25835
25868
  this.service.lockManager.save();
25836
25869
  this.service.warnings.push(...this.service.lockManager.warnings);
25837
25870
  if (newPatches.length > 0) {
@@ -25992,6 +26025,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
25992
26025
  }
25993
26026
  }
25994
26027
  }
26028
+ this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
25995
26029
  this.service.lockManager.save();
25996
26030
  this.service.warnings.push(...this.service.lockManager.warnings);
25997
26031
  if (options?.stageOnly) {
@@ -26676,6 +26710,11 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
26676
26710
  (files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
26677
26711
  );
26678
26712
  if (ppResult === null) continue;
26713
+ if (ppResult.hadFallback) {
26714
+ this.warnings.push(
26715
+ `Patch ${patch.id}: couldn't fully isolate ${ppResult.fallbackFiles.join(", ")} \u2014 customization preserved, attribution may have merged with another patch.`
26716
+ );
26717
+ }
26679
26718
  const diff = ppResult.diff;
26680
26719
  if (!diff.trim()) {
26681
26720
  if (await allPatchFilesUserOwned(patch)) continue;
@@ -26763,6 +26802,11 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
26763
26802
  (files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
26764
26803
  );
26765
26804
  if (ppResult === null) continue;
26805
+ if (ppResult.hadFallback) {
26806
+ this.warnings.push(
26807
+ `Patch ${patch.id}: couldn't fully isolate ${ppResult.fallbackFiles.join(", ")} \u2014 customization preserved, attribution may have merged with another patch.`
26808
+ );
26809
+ }
26766
26810
  const diff = ppResult.diff;
26767
26811
  if (!diff.trim()) {
26768
26812
  if (await allPatchFilesUserOwned(patch)) {
@@ -26857,6 +26901,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
26857
26901
  }
26858
26902
  }
26859
26903
  }
26904
+ /** @internal Used by Flow implementations in `src/flows/`. */
26860
26905
  readFernignorePatterns() {
26861
26906
  const fernignorePath = (0, import_path13.join)(this.outputDir, ".fernignore");
26862
26907
  if (!(0, import_fs7.existsSync)(fernignorePath)) return [];
@@ -41858,6 +41903,8 @@ var GithubStep = class extends BaseStep {
41858
41903
  );
41859
41904
  case "push":
41860
41905
  return await this.executePushMode(repository, resolvedPrFields);
41906
+ case "commit-and-release":
41907
+ return await this.executeCommitAndReleaseMode(repository, resolvedPrFields);
41861
41908
  default: {
41862
41909
  const exhaustive = mode;
41863
41910
  throw new Error(`Unexpected GitHub mode: ${String(exhaustive)}`);
@@ -42084,6 +42131,71 @@ var GithubStep = class extends BaseStep {
42084
42131
  }
42085
42132
  return result;
42086
42133
  }
42134
+ async executeCommitAndReleaseMode(repository, resolved) {
42135
+ const baseBranch = this.config.branch ?? await repository.getDefaultBranch();
42136
+ if (this.config.branch != null) {
42137
+ this.logger.debug(`Checking out branch ${this.config.branch}`);
42138
+ await repository.checkout(this.config.branch);
42139
+ }
42140
+ await this.ensureFernignore();
42141
+ this.logger.debug("Committing changes...");
42142
+ await repository.commitAllChanges(resolved.commitMessage);
42143
+ this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
42144
+ if (shouldCheckNoDiff(this.config)) {
42145
+ const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
42146
+ if (noDiff) {
42147
+ this.logger.info("No changes detected after generation \u2014 skipping commit-and-release");
42148
+ return { executed: true, success: true, skippedNoDiff: true };
42149
+ }
42150
+ }
42151
+ const result = {
42152
+ executed: true,
42153
+ success: true
42154
+ };
42155
+ if (!this.config.previewMode) {
42156
+ const octokit = this.createOctokit();
42157
+ const { owner, repo, remote } = parseRepository(this.config.uri);
42158
+ await pushSignedCommit({
42159
+ repository,
42160
+ octokit,
42161
+ owner,
42162
+ repo,
42163
+ branch: baseBranch,
42164
+ force: false,
42165
+ rebaseOnConflict: true,
42166
+ author: this.config.author,
42167
+ logger: this.logger
42168
+ });
42169
+ const pushedBranch = await repository.getCurrentBranch();
42170
+ result.branchUrl = `https://${remote}/${owner}/${repo}/tree/${pushedBranch}`;
42171
+ this.logger.info(`Pushed branch: ${result.branchUrl}`);
42172
+ if (resolved.newVersion == null) {
42173
+ this.logger.warn(
42174
+ "No new version available \u2014 skipping release tag creation. Pass --version <semver> or --version AUTO to fern generate to enable release tagging."
42175
+ );
42176
+ } else {
42177
+ try {
42178
+ const tagName = resolved.newVersion;
42179
+ const headSha = await repository.getHeadSha();
42180
+ await octokit.repos.createRelease({
42181
+ owner,
42182
+ repo,
42183
+ tag_name: tagName,
42184
+ target_commitish: headSha,
42185
+ name: tagName,
42186
+ body: resolved.changelogEntry ?? resolved.commitMessage,
42187
+ draft: false,
42188
+ prerelease: false
42189
+ });
42190
+ this.logger.info(`Created GitHub release ${tagName}`);
42191
+ result.releaseUrl = `https://${remote}/${owner}/${repo}/releases/tag/${tagName}`;
42192
+ } catch (error) {
42193
+ this.logger.warn(`Could not create GitHub release: ${extractErrorMessage(error)}`);
42194
+ }
42195
+ }
42196
+ }
42197
+ return result;
42198
+ }
42087
42199
  createOctokit() {
42088
42200
  const opts = { auth: this.config.token };
42089
42201
  if (this.config.apiBaseUrl != null) {
@@ -42528,9 +42640,9 @@ var PostGenerationPipeline = class {
42528
42640
  this.logger = logger;
42529
42641
  let replayEnabled = config.replay?.enabled ?? false;
42530
42642
  let autoVersionEnabled = config.autoVersion?.enabled ?? false;
42531
- if (replayEnabled && config.github?.mode === "push") {
42643
+ if (replayEnabled && (config.github?.mode === "push" || config.github?.mode === "commit-and-release")) {
42532
42644
  this.logger.warn(
42533
- "Replay is not supported with GitHub push mode. Disabling replay to prevent force push to base branch."
42645
+ `Replay is not supported with GitHub ${config.github.mode} mode. Disabling replay to prevent push to base branch.`
42534
42646
  );
42535
42647
  replayEnabled = false;
42536
42648
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.9.32",
3
+ "version": "0.9.33",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "@boundaryml/baml": "^0.219.0",
26
- "@fern-api/replay": "0.16.0",
26
+ "@fern-api/replay": "0.16.1",
27
27
  "@octokit/rest": "^22.0.1",
28
28
  "es-toolkit": "^1.45.1",
29
29
  "semver": "^7.6.3",