@fern-api/generator-cli 0.9.14 → 0.9.16

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 +2906 -295
  2. package/dist/cli.js +112 -41
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -35152,21 +35152,23 @@ function stringifyValidationError(error) {
35152
35152
  }
35153
35153
 
35154
35154
  // src/configuration/sdk/core/schemas/builders/schema-utils/JsonError.ts
35155
- var JsonError = class _JsonError extends Error {
35155
+ var JsonError = class extends Error {
35156
35156
  constructor(errors) {
35157
35157
  super(errors.map(stringifyValidationError).join("; "));
35158
35158
  this.errors = errors;
35159
- Object.setPrototypeOf(this, _JsonError.prototype);
35159
+ Object.setPrototypeOf(this, new.target.prototype);
35160
+ this.name = this.constructor.name;
35160
35161
  }
35161
35162
  errors;
35162
35163
  };
35163
35164
 
35164
35165
  // src/configuration/sdk/core/schemas/builders/schema-utils/ParseError.ts
35165
- var ParseError = class _ParseError extends Error {
35166
+ var ParseError = class extends Error {
35166
35167
  constructor(errors) {
35167
35168
  super(errors.map(stringifyValidationError).join("; "));
35168
35169
  this.errors = errors;
35169
- Object.setPrototypeOf(this, _ParseError.prototype);
35170
+ Object.setPrototypeOf(this, new.target.prototype);
35171
+ this.name = this.constructor.name;
35170
35172
  }
35171
35173
  errors;
35172
35174
  };
@@ -39176,14 +39178,22 @@ var AutoVersionStep = class extends BaseStep {
39176
39178
  async handleNormalFlow(params) {
39177
39179
  const { prepared, service, language, mappedMagicVersion, previousGenerationSha } = params;
39178
39180
  const rawDiff = this.gitDiff(previousGenerationSha, prepared.currentGenerationSha);
39179
- if (rawDiff.trim().length === 0) {
39180
- this.logger.info("AutoVersionStep: empty diff between generations; skipping autoversion.");
39181
- return { executed: true, success: true };
39182
- }
39183
39181
  const previousVersion = await this.resolvePreviousVersion({ service, rawDiff, mappedMagicVersion });
39184
39182
  if (previousVersion == null) {
39185
39183
  return await this.handleFirstGeneration({ service, language, mappedMagicVersion });
39186
39184
  }
39185
+ if (rawDiff.trim().length === 0) {
39186
+ this.logger.info(
39187
+ `AutoVersionStep: empty diff between generations; rewriting placeholder to ${previousVersion}.`
39188
+ );
39189
+ return await this.finalizeNoChange({
39190
+ service,
39191
+ language,
39192
+ mappedMagicVersion,
39193
+ previousVersion,
39194
+ reason: "no diff between generations"
39195
+ });
39196
+ }
39187
39197
  const cleanedDiff = service.cleanDiffForAI(rawDiff, mappedMagicVersion);
39188
39198
  const rawBytes = Buffer.byteLength(rawDiff, "utf-8");
39189
39199
  const cleanedBytes = Buffer.byteLength(cleanedDiff, "utf-8");
@@ -39191,8 +39201,16 @@ var AutoVersionStep = class extends BaseStep {
39191
39201
  `AutoVersionStep: raw=${formatSizeKB(rawBytes)}KB (${countFilesInDiff(rawDiff)} files), cleaned=${formatSizeKB(cleanedBytes)}KB (${countFilesInDiff(cleanedDiff)} files)`
39192
39202
  );
39193
39203
  if (cleanedDiff.trim().length === 0) {
39194
- this.logger.info("AutoVersionStep: cleaned diff is empty; no semantic changes.");
39195
- return { executed: true, success: true };
39204
+ this.logger.info(
39205
+ `AutoVersionStep: cleaned diff is empty; no semantic changes \u2014 rewriting placeholder to ${previousVersion}.`
39206
+ );
39207
+ return await this.finalizeNoChange({
39208
+ service,
39209
+ language,
39210
+ mappedMagicVersion,
39211
+ previousVersion,
39212
+ reason: "no semantic changes"
39213
+ });
39196
39214
  }
39197
39215
  if (cleanedBytes > MAX_RAW_DIFF_BYTES) {
39198
39216
  this.logger.warn(
@@ -39222,13 +39240,14 @@ var AutoVersionStep = class extends BaseStep {
39222
39240
  analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
39223
39241
  }
39224
39242
  if (analysis == null) {
39225
- this.logger.info("AutoVersionStep: FAI returned NO_CHANGE; skipping autoversion commit.");
39226
- return {
39227
- executed: true,
39228
- success: true,
39243
+ this.logger.info(`AutoVersionStep: FAI returned NO_CHANGE; rewriting placeholder to ${previousVersion}.`);
39244
+ return await this.finalizeNoChange({
39245
+ service,
39246
+ language,
39247
+ mappedMagicVersion,
39229
39248
  previousVersion,
39230
- versionBump: "NO_CHANGE"
39231
- };
39249
+ reason: "FAI returned NO_CHANGE"
39250
+ });
39232
39251
  }
39233
39252
  return await this.finalizeWithBump({
39234
39253
  service,
@@ -39238,6 +39257,38 @@ var AutoVersionStep = class extends BaseStep {
39238
39257
  analysis
39239
39258
  });
39240
39259
  }
39260
+ /**
39261
+ * Terminal path for runs that determine no semver bump is needed. The
39262
+ * current working tree still contains `0.0.0-fern-placeholder` from this
39263
+ * run's generator output, so we must rewrite it to `previousVersion`
39264
+ * before exiting — otherwise the SDK ships with the placeholder embedded
39265
+ * in manifests (package.json, pyproject.toml, .fern/metadata.json) and
39266
+ * user-agent strings (e.g. `X-Fern-SDK-Version`). Commits the rewrite as
39267
+ * `[fern-autoversion]` so the replay + github steps see a stable base.
39268
+ */
39269
+ async finalizeNoChange(params) {
39270
+ const { service, language, mappedMagicVersion, previousVersion, reason } = params;
39271
+ if (!isValidSemver(previousVersion)) {
39272
+ const errorMessage = `AutoVersionStep: resolved previousVersion ${JSON.stringify(previousVersion)} is not a valid semver string. Refusing to rewrite placeholder to avoid shell injection.`;
39273
+ this.logger.error(errorMessage);
39274
+ return { executed: true, success: false, errorMessage };
39275
+ }
39276
+ await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, previousVersion);
39277
+ if (language === "go") {
39278
+ await service.addGoMajorVersionSuffix(this.outputDir, previousVersion);
39279
+ }
39280
+ const commitMessage = this.brandMessage(`SDK regeneration (no semver change: ${reason})`);
39281
+ const commitSha = this.commitAutoversion(commitMessage);
39282
+ return {
39283
+ executed: true,
39284
+ success: true,
39285
+ version: previousVersion,
39286
+ previousVersion,
39287
+ versionBump: "NO_CHANGE",
39288
+ commitMessage,
39289
+ commitSha
39290
+ };
39291
+ }
39241
39292
  async finalizeWithBump(params) {
39242
39293
  const { service, language, mappedMagicVersion, previousVersion, analysis } = params;
39243
39294
  const newVersion = incrementVersion(previousVersion, analysis.versionBump);
@@ -40159,8 +40210,10 @@ var GithubStep = class extends BaseStep {
40159
40210
  name = "github";
40160
40211
  async execute(context) {
40161
40212
  const replayResult = context.previousStepResults.replay;
40213
+ const autoVersionResult = context.previousStepResults.autoVersion;
40162
40214
  const skipCommit = this.config.skipCommit ?? this.deriveSkipCommit(replayResult);
40163
40215
  const replayConflictInfo = this.config.replayConflictInfo ?? this.deriveReplayConflictInfo(replayResult);
40216
+ const resolvedPrFields = this.resolvePrFields(autoVersionResult);
40164
40217
  try {
40165
40218
  this.logger.debug("Starting GitHub self-hosted flow in directory: " + this.outputDir);
40166
40219
  const repository = ClonedRepository.createAtPath(this.outputDir);
@@ -40179,10 +40232,11 @@ var GithubStep = class extends BaseStep {
40179
40232
  newPrBranch,
40180
40233
  skipCommit,
40181
40234
  replayConflictInfo,
40182
- replayResult
40235
+ replayResult,
40236
+ resolvedPrFields
40183
40237
  );
40184
40238
  case "push":
40185
- return await this.executePushMode(repository);
40239
+ return await this.executePushMode(repository, resolvedPrFields);
40186
40240
  default: {
40187
40241
  const exhaustive = mode;
40188
40242
  throw new Error(`Unexpected GitHub mode: ${String(exhaustive)}`);
@@ -40198,7 +40252,7 @@ var GithubStep = class extends BaseStep {
40198
40252
  };
40199
40253
  }
40200
40254
  }
40201
- async executePullRequestMode(repository, newPrBranch, skipCommit, replayConflictInfo, replayResult) {
40255
+ async executePullRequestMode(repository, newPrBranch, skipCommit, replayConflictInfo, replayResult, resolved) {
40202
40256
  const baseBranch = this.config.branch ?? await repository.getDefaultBranch();
40203
40257
  const octokit = new Octokit2({ auth: this.config.token });
40204
40258
  const { owner, repo } = parseRepository(this.config.uri);
@@ -40232,7 +40286,7 @@ var GithubStep = class extends BaseStep {
40232
40286
  generationBaseSha = await createReplayBranch(
40233
40287
  repository,
40234
40288
  prBranch,
40235
- this.config.commitMessage,
40289
+ resolved.commitMessage,
40236
40290
  replayConflictInfo,
40237
40291
  this.logger
40238
40292
  );
@@ -40254,8 +40308,7 @@ var GithubStep = class extends BaseStep {
40254
40308
  if (!skipCommit) {
40255
40309
  await this.ensureFernignore();
40256
40310
  this.logger.debug("Committing changes...");
40257
- const finalCommitMessage2 = this.config.commitMessage ?? "SDK Generation";
40258
- await repository.commitAllChanges(finalCommitMessage2);
40311
+ await repository.commitAllChanges(resolved.commitMessage);
40259
40312
  this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
40260
40313
  }
40261
40314
  if (shouldCheckNoDiff(this.config)) {
@@ -40295,22 +40348,21 @@ var GithubStep = class extends BaseStep {
40295
40348
  }
40296
40349
  }
40297
40350
  }
40298
- const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
40299
40351
  const headSha = await repository.getHeadSha();
40300
- const changelogUrl = this.config.changelogEntry ? `https://github.com/${this.config.uri}/blob/${headSha}/changelog.md` : void 0;
40352
+ const changelogUrl = resolved.changelogEntry ? `https://github.com/${this.config.uri}/blob/${headSha}/changelog.md` : void 0;
40301
40353
  const { prTitle, prBody } = parseCommitMessageForPR(
40302
- finalCommitMessage,
40303
- this.config.changelogEntry,
40304
- this.config.prDescription,
40305
- this.config.versionBumpReason,
40306
- this.config.previousVersion,
40307
- this.config.newVersion,
40308
- this.config.versionBump,
40354
+ resolved.commitMessage,
40355
+ resolved.changelogEntry,
40356
+ resolved.prDescription,
40357
+ resolved.versionBumpReason,
40358
+ resolved.previousVersion,
40359
+ resolved.newVersion,
40360
+ resolved.versionBump,
40309
40361
  changelogUrl
40310
40362
  );
40311
40363
  const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch, repoUri: this.config.uri });
40312
40364
  let enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody;
40313
- enrichedBody = enrichPrBodyForAutomation(enrichedBody, this.config);
40365
+ enrichedBody = enrichPrBodyForAutomation(enrichedBody, this.config, resolved);
40314
40366
  if (isUpdatingExistingPR && existingPR != null) {
40315
40367
  this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`);
40316
40368
  result.prUrl = existingPR.htmlUrl;
@@ -40341,7 +40393,7 @@ var GithubStep = class extends BaseStep {
40341
40393
  this.logger.info(`Created pull request: ${pullRequest.html_url}`);
40342
40394
  result.prUrl = pullRequest.html_url;
40343
40395
  result.prNumber = pullRequest.number;
40344
- if (shouldEnableAutomerge(this.config) && pullRequest.node_id) {
40396
+ if (shouldEnableAutomerge(this.config, resolved) && pullRequest.node_id) {
40345
40397
  try {
40346
40398
  await octokit.graphql(
40347
40399
  `mutation($pullRequestId: ID!) {
@@ -40369,7 +40421,7 @@ var GithubStep = class extends BaseStep {
40369
40421
  }
40370
40422
  return result;
40371
40423
  }
40372
- async executePushMode(repository) {
40424
+ async executePushMode(repository, resolved) {
40373
40425
  const baseBranch = this.config.branch ?? await repository.getDefaultBranch();
40374
40426
  if (this.config.branch != null) {
40375
40427
  this.logger.debug(`Checking out branch ${this.config.branch}`);
@@ -40377,8 +40429,7 @@ var GithubStep = class extends BaseStep {
40377
40429
  }
40378
40430
  await this.ensureFernignore();
40379
40431
  this.logger.debug("Committing changes...");
40380
- const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
40381
- await repository.commitAllChanges(finalCommitMessage);
40432
+ await repository.commitAllChanges(resolved.commitMessage);
40382
40433
  this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
40383
40434
  if (shouldCheckNoDiff(this.config)) {
40384
40435
  const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
@@ -40441,14 +40492,33 @@ var GithubStep = class extends BaseStep {
40441
40492
  }
40442
40493
  return void 0;
40443
40494
  }
40495
+ resolvePrFields(autoVersion) {
40496
+ return resolvePrFields(this.config, autoVersion);
40497
+ }
40444
40498
  };
40445
- function enrichPrBodyForAutomation(body, config) {
40499
+ function resolvePrFields(config, autoVersion) {
40500
+ const autoVersionBreaking = autoVersion?.versionBump === "MAJOR";
40501
+ return {
40502
+ commitMessage: config.commitMessage ?? autoVersion?.commitMessage ?? "SDK Generation",
40503
+ changelogEntry: config.changelogEntry ?? autoVersion?.changelogEntry,
40504
+ prDescription: config.prDescription ?? autoVersion?.prDescription,
40505
+ versionBumpReason: config.versionBumpReason ?? autoVersion?.versionBumpReason,
40506
+ previousVersion: config.previousVersion ?? autoVersion?.previousVersion,
40507
+ newVersion: config.newVersion ?? autoVersion?.version,
40508
+ versionBump: config.versionBump ?? autoVersion?.versionBump,
40509
+ hasBreakingChanges: config.hasBreakingChanges ?? autoVersionBreaking,
40510
+ breakingChangesSummary: config.breakingChangesSummary ?? autoVersion?.prDescription
40511
+ };
40512
+ }
40513
+ function enrichPrBodyForAutomation(body, config, breaking = {}) {
40446
40514
  if (!config.automationMode) {
40447
40515
  return body;
40448
40516
  }
40517
+ const hasBreakingChanges = breaking.hasBreakingChanges ?? config.hasBreakingChanges;
40518
+ const breakingChangesSummary = breaking.breakingChangesSummary ?? config.breakingChangesSummary;
40449
40519
  let result = body;
40450
- if (config.hasBreakingChanges && config.breakingChangesSummary) {
40451
- result += "\n\n---\n\n## \u26A0\uFE0F Breaking Changes\n\nThis release contains breaking changes that require manual review:\n\n" + config.breakingChangesSummary;
40520
+ if (hasBreakingChanges && breakingChangesSummary) {
40521
+ result += "\n\n---\n\n## \u26A0\uFE0F Breaking Changes\n\nThis release contains breaking changes that require manual review:\n\n" + breakingChangesSummary;
40452
40522
  }
40453
40523
  if (config.runId) {
40454
40524
  result += `
@@ -40459,8 +40529,9 @@ _Fern Run ID: \`${config.runId}\`_`;
40459
40529
  }
40460
40530
  return result;
40461
40531
  }
40462
- function shouldEnableAutomerge(config) {
40463
- return config.automationMode === true && config.autoMerge === true && config.hasBreakingChanges !== true;
40532
+ function shouldEnableAutomerge(config, breaking = {}) {
40533
+ const hasBreakingChanges = breaking.hasBreakingChanges ?? config.hasBreakingChanges;
40534
+ return config.automationMode === true && config.autoMerge === true && hasBreakingChanges !== true;
40464
40535
  }
40465
40536
  function shouldCheckNoDiff(config) {
40466
40537
  return config.skipIfNoDiff === true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",