@fern-api/generator-cli 0.10.0 → 0.10.1

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 +150 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42211,7 +42211,8 @@ var AutoVersionStep = class extends BaseStep {
42211
42211
  };
42212
42212
  }
42213
42213
  async finalizeWithBump(params) {
42214
- const { service, language, mappedMagicVersion, previousVersion, analysis } = params;
42214
+ const { service, language, mappedMagicVersion, previousVersion } = params;
42215
+ const analysis = this.withChangelogFallback(params.analysis);
42215
42216
  const newVersion = incrementVersion(previousVersion, analysis.versionBump);
42216
42217
  this.logger.info(`AutoVersionStep: ${analysis.versionBump} bump: ${previousVersion} \u2192 ${newVersion}`);
42217
42218
  await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, newVersion);
@@ -42265,7 +42266,8 @@ var AutoVersionStep = class extends BaseStep {
42265
42266
  * the bumped version but does NOT commit — GithubStep handles the commit.
42266
42267
  */
42267
42268
  async finalizeWithBumpNonReplay(params) {
42268
- const { service, language, mappedMagicVersion, previousVersion, analysis } = params;
42269
+ const { service, language, mappedMagicVersion, previousVersion } = params;
42270
+ const analysis = this.withChangelogFallback(params.analysis);
42269
42271
  const newVersion = incrementVersion(previousVersion, analysis.versionBump);
42270
42272
  this.logger.info(
42271
42273
  `AutoVersionStep (non-replay): ${analysis.versionBump} bump: ${previousVersion} \u2192 ${newVersion}`
@@ -42572,6 +42574,26 @@ ${body}` : `${COMMIT_MARKER} ${subject}`;
42572
42574
  }
42573
42575
  await (0, import_promises16.writeFile)(changelogPath, prependChangelogBlock({ existingContent: existing, version: version2, entry }), "utf-8");
42574
42576
  }
42577
+ /**
42578
+ * MAJOR/MINOR bumps must ship a changelog entry. FAI occasionally returns a
42579
+ * bump with a populated `pr_description` / `version_bump_reason` but an empty
42580
+ * `changelog_entry`; reuse that text rather than writing a version-only block.
42581
+ */
42582
+ withChangelogFallback(analysis) {
42583
+ const fallback = resolveChangelogEntryFallbackWithSource(analysis);
42584
+ if (fallback == null) {
42585
+ if (analysis.versionBump !== "PATCH" && !hasText(analysis.changelogEntry)) {
42586
+ this.logger.warn(
42587
+ `AutoVersionStep: FAI returned a ${analysis.versionBump} bump without a changelog entry and no fallback text.`
42588
+ );
42589
+ }
42590
+ return analysis;
42591
+ }
42592
+ this.logger.warn(
42593
+ `AutoVersionStep: FAI returned a ${analysis.versionBump} bump without a changelog entry; using the ${fallback.source} instead.`
42594
+ );
42595
+ return { ...analysis, changelogEntry: fallback.text };
42596
+ }
42575
42597
  brandMessage(message) {
42576
42598
  if (this.config.isWhitelabel) {
42577
42599
  return message;
@@ -42602,10 +42624,7 @@ ${body}` : `${COMMIT_MARKER} ${subject}`;
42602
42624
  }
42603
42625
  async analyzeChunks(chunks, language, previousVersion) {
42604
42626
  const { client, VersionBump: VersionBump4 } = await this.loadBaml();
42605
- let bestBump = VersionBump4.NO_CHANGE;
42606
- let bestMessage = "";
42607
- let bestVersionBumpReason;
42608
- const changelogEntries = [];
42627
+ const chunkAnalyses = [];
42609
42628
  for (let i2 = 0; i2 < chunks.length; i2++) {
42610
42629
  const chunk = chunks[i2];
42611
42630
  if (!chunk) {
@@ -42621,20 +42640,22 @@ ${body}` : `${COMMIT_MARKER} ${subject}`;
42621
42640
  if (analysis.version_bump === VersionBump4.NO_CHANGE) {
42622
42641
  continue;
42623
42642
  }
42624
- const prev = bestBump;
42625
- bestBump = maxVersionBump(bestBump, analysis.version_bump);
42626
- if (bestBump !== prev) {
42627
- bestMessage = analysis.message;
42628
- bestVersionBumpReason = analysis.version_bump_reason;
42629
- }
42630
- const entry = analysis.changelog_entry?.trim();
42631
- if (entry) {
42632
- changelogEntries.push(entry);
42633
- }
42643
+ chunkAnalyses.push({
42644
+ versionBump: analysis.version_bump,
42645
+ message: analysis.message,
42646
+ changelogEntry: analysis.changelog_entry,
42647
+ versionBumpReason: analysis.version_bump_reason
42648
+ });
42634
42649
  }
42635
- if (bestBump === VersionBump4.NO_CHANGE) {
42650
+ if (chunkAnalyses.length === 0) {
42636
42651
  return null;
42637
42652
  }
42653
+ const { bestBump, bestMessage, bestVersionBumpReason, changelogEntries, usedBumpReasonAsEntry } = aggregateChunkAnalyses(chunkAnalyses);
42654
+ if (usedBumpReasonAsEntry) {
42655
+ this.logger.warn(
42656
+ `AutoVersionStep: no chunk produced a changelog entry for the ${bestBump} bump; using its version bump reason instead.`
42657
+ );
42658
+ }
42638
42659
  if (changelogEntries.length <= 1) {
42639
42660
  return {
42640
42661
  versionBump: bestBump,
@@ -42750,6 +42771,57 @@ ${body}` : `${COMMIT_MARKER} ${subject}`;
42750
42771
  return adapter;
42751
42772
  }
42752
42773
  };
42774
+ function aggregateChunkAnalyses(chunkAnalyses) {
42775
+ let bestBump = "NO_CHANGE";
42776
+ let bestMessage = "";
42777
+ let bestVersionBumpReason;
42778
+ const entries2 = [];
42779
+ for (const analysis of chunkAnalyses) {
42780
+ const prev = bestBump;
42781
+ bestBump = maxVersionBump(bestBump, analysis.versionBump);
42782
+ if (bestBump !== prev) {
42783
+ bestMessage = analysis.message;
42784
+ bestVersionBumpReason = analysis.versionBumpReason;
42785
+ }
42786
+ const text = analysis.changelogEntry?.trim();
42787
+ if (text) {
42788
+ entries2.push({ bump: analysis.versionBump, text });
42789
+ }
42790
+ }
42791
+ const changelogEntries = entries2.map((entry) => entry.text);
42792
+ const hasEntryAtBestBump = entries2.some((entry) => entry.bump === bestBump);
42793
+ const usedBumpReasonAsEntry = bestBump !== "PATCH" && !hasEntryAtBestBump && hasText(bestVersionBumpReason);
42794
+ if (usedBumpReasonAsEntry && bestVersionBumpReason != null) {
42795
+ changelogEntries.unshift(bestVersionBumpReason.trim());
42796
+ }
42797
+ return { bestBump, bestMessage, bestVersionBumpReason, changelogEntries, usedBumpReasonAsEntry };
42798
+ }
42799
+ function hasText(value) {
42800
+ return value != null && value.trim().length > 0;
42801
+ }
42802
+ function commitMessageBody(message) {
42803
+ const trimmed2 = message.trimEnd();
42804
+ const withoutTrailer = trimmed2.endsWith(FERN_TRAILER) ? trimmed2.slice(0, -FERN_TRAILER.length) : trimmed2;
42805
+ const [, ...rest] = withoutTrailer.split("\n");
42806
+ const body = rest.join("\n").trim();
42807
+ return body.length > 0 ? body : void 0;
42808
+ }
42809
+ function resolveChangelogEntryFallbackWithSource(analysis) {
42810
+ if (hasText(analysis.changelogEntry) || analysis.versionBump === "PATCH") {
42811
+ return void 0;
42812
+ }
42813
+ const candidates = [
42814
+ ["PR description", analysis.prDescription],
42815
+ ["version bump reason", analysis.versionBumpReason],
42816
+ ["commit message body", commitMessageBody(analysis.message)]
42817
+ ];
42818
+ for (const [source, text] of candidates) {
42819
+ if (hasText(text)) {
42820
+ return { source, text: text.trim() };
42821
+ }
42822
+ }
42823
+ return void 0;
42824
+ }
42753
42825
  var FAI_VERSION_BUMPS = ["MAJOR", "MINOR", "PATCH", "NO_CHANGE"];
42754
42826
  function isFaiAnalyzeResponse(value) {
42755
42827
  if (typeof value !== "object" || value == null) {
@@ -43921,6 +43993,66 @@ var ReplayStep = class extends BaseStep {
43921
43993
  }
43922
43994
  };
43923
43995
 
43996
+ // ../cli/generation/local-generation/docker-utils/lib/dockerHubOatLogin.js
43997
+ var DOCKERHUB_OAT_ENV_VAR = "DOCKERHUB_OAT";
43998
+ var DOCKERHUB_OAT_USERNAME_ENV_VAR = "DOCKERHUB_OAT_USERNAME";
43999
+ var DEFAULT_DOCKERHUB_OAT_USERNAME = "fernenterprise";
44000
+ var DOCKER_HUB_HOSTS = /* @__PURE__ */ new Set([
44001
+ "docker.io",
44002
+ "index.docker.io",
44003
+ "registry-1.docker.io",
44004
+ "registry.hub.docker.com"
44005
+ ]);
44006
+ function getDockerHubNamespace(imageName) {
44007
+ const parts = imageName.split("/");
44008
+ const [first2] = parts;
44009
+ if (first2 == null || parts.length < 2) {
44010
+ return void 0;
44011
+ }
44012
+ const host = first2.toLowerCase();
44013
+ const isRegistryHost = host.includes(".") || host.includes(":") || host === "localhost";
44014
+ if (!isRegistryHost) {
44015
+ return first2;
44016
+ }
44017
+ if (!DOCKER_HUB_HOSTS.has(host)) {
44018
+ return void 0;
44019
+ }
44020
+ return parts.length >= 3 ? parts[1] : void 0;
44021
+ }
44022
+ function resolveDockerHubOatLogin(imageName, env3 = process.env) {
44023
+ const token = env3[DOCKERHUB_OAT_ENV_VAR]?.trim();
44024
+ if (token == null || token === "") {
44025
+ return void 0;
44026
+ }
44027
+ const username = env3[DOCKERHUB_OAT_USERNAME_ENV_VAR]?.trim() || DEFAULT_DOCKERHUB_OAT_USERNAME;
44028
+ if (getDockerHubNamespace(imageName) !== username) {
44029
+ return void 0;
44030
+ }
44031
+ return { username, token };
44032
+ }
44033
+ async function ensureDockerHubOatLogin({ imageName, runner, logger, signal, env: env3 = process.env }) {
44034
+ const credentials = resolveDockerHubOatLogin(imageName, env3);
44035
+ if (credentials == null) {
44036
+ return;
44037
+ }
44038
+ logger?.debug(`Logging in to Docker Hub as ${credentials.username} with ${DOCKERHUB_OAT_ENV_VAR} to pull ${imageName}`);
44039
+ const { exitCode, stdout, stderr } = await loggingExeca(logger, runner ?? "docker", ["login", "--username", credentials.username, "--password-stdin"], {
44040
+ input: credentials.token,
44041
+ reject: false,
44042
+ doNotPipeOutput: true,
44043
+ secrets: [credentials.token],
44044
+ signal
44045
+ });
44046
+ if (exitCode !== 0) {
44047
+ if (signal?.aborted === true) {
44048
+ throw new Error(`Docker Hub login as ${credentials.username} was cancelled.`);
44049
+ }
44050
+ throw new Error(`Failed to log in to Docker Hub as ${credentials.username} using ${DOCKERHUB_OAT_ENV_VAR}. Check that the token is a valid organization access token for ${credentials.username} with pull access to ${imageName}.
44051
+ ${stdout}
44052
+ ${stderr}`);
44053
+ }
44054
+ }
44055
+
43924
44056
  // ../cli/generation/local-generation/docker-utils/lib/runDocker.js
43925
44057
  var import_promises18 = require("fs/promises");
43926
44058
  var import_tmp_promise3 = __toESM(require_tmp_promise(), 1);
@@ -43955,6 +44087,7 @@ async function pullImage(imageName, runner, signal) {
43955
44087
  }
43956
44088
  async function startContainer({ logger, imageName, runner }) {
43957
44089
  const containerRunner = runner ?? "docker";
44090
+ await ensureDockerHubOatLogin({ imageName, runner, logger });
43958
44091
  const tryStart = async () => {
43959
44092
  const { stdout, exitCode, stderr } = await loggingExeca(logger, containerRunner, ["run", "--user", "root", "-dit", "--entrypoint", "/bin/sh", imageName], {
43960
44093
  reject: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",