@fern-api/generator-cli 0.9.35 → 0.9.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +334 -45
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3822,9 +3822,9 @@ var init_esm3 = __esm({
|
|
|
3822
3822
|
}
|
|
3823
3823
|
});
|
|
3824
3824
|
|
|
3825
|
-
// ../../node_modules/.pnpm/tmp@0.2.
|
|
3825
|
+
// ../../node_modules/.pnpm/tmp@0.2.6/node_modules/tmp/lib/tmp.js
|
|
3826
3826
|
var require_tmp = __commonJS({
|
|
3827
|
-
"../../node_modules/.pnpm/tmp@0.2.
|
|
3827
|
+
"../../node_modules/.pnpm/tmp@0.2.6/node_modules/tmp/lib/tmp.js"(exports2, module2) {
|
|
3828
3828
|
"use strict";
|
|
3829
3829
|
var fs3 = require("fs");
|
|
3830
3830
|
var os = require("os");
|
|
@@ -4075,13 +4075,20 @@ var require_tmp = __commonJS({
|
|
|
4075
4075
|
].join("");
|
|
4076
4076
|
return path5.join(tmpDir, opts.dir, name);
|
|
4077
4077
|
}
|
|
4078
|
+
function _assertPath(path6) {
|
|
4079
|
+
if (path6.includes("..")) {
|
|
4080
|
+
throw new Error("Relative value not allowed");
|
|
4081
|
+
}
|
|
4082
|
+
return path6;
|
|
4083
|
+
}
|
|
4078
4084
|
function _assertOptionsBase(options) {
|
|
4079
4085
|
if (!_isUndefined(options.name)) {
|
|
4080
4086
|
const name = options.name;
|
|
4081
4087
|
if (path5.isAbsolute(name)) throw new Error(`name option must not contain an absolute path, found "${name}".`);
|
|
4082
4088
|
const basename2 = path5.basename(name);
|
|
4083
|
-
if (basename2 === ".." || basename2 === "." || basename2 !== name)
|
|
4089
|
+
if (basename2 === ".." || basename2 === "." || basename2 !== name) {
|
|
4084
4090
|
throw new Error(`name option must not contain a path, found "${name}".`);
|
|
4091
|
+
}
|
|
4085
4092
|
}
|
|
4086
4093
|
if (!_isUndefined(options.template) && !options.template.match(TEMPLATE_PATTERN)) {
|
|
4087
4094
|
throw new Error(`Invalid template, found "${options.template}".`);
|
|
@@ -4094,15 +4101,16 @@ var require_tmp = __commonJS({
|
|
|
4094
4101
|
options.detachDescriptor = !!options.detachDescriptor;
|
|
4095
4102
|
options.discardDescriptor = !!options.discardDescriptor;
|
|
4096
4103
|
options.unsafeCleanup = !!options.unsafeCleanup;
|
|
4097
|
-
options.prefix = _isUndefined(options.prefix) ? "" : options.prefix;
|
|
4098
|
-
options.postfix = _isUndefined(options.postfix) ? "" : options.postfix;
|
|
4104
|
+
options.prefix = _isUndefined(options.prefix) ? "" : _assertPath(options.prefix);
|
|
4105
|
+
options.postfix = _isUndefined(options.postfix) ? "" : _assertPath(options.postfix);
|
|
4106
|
+
options.template = _isUndefined(options.template) ? void 0 : _assertPath(options.template);
|
|
4099
4107
|
}
|
|
4100
4108
|
function _getRelativePath(option, name, tmpDir, cb) {
|
|
4101
4109
|
if (_isUndefined(name)) return cb(null);
|
|
4102
4110
|
_resolvePath(name, tmpDir, function(err, resolvedPath) {
|
|
4103
4111
|
if (err) return cb(err);
|
|
4104
4112
|
const relativePath = path5.relative(tmpDir, resolvedPath);
|
|
4105
|
-
if (
|
|
4113
|
+
if (relativePath.startsWith("..") || path5.isAbsolute(relativePath)) {
|
|
4106
4114
|
return cb(new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`));
|
|
4107
4115
|
}
|
|
4108
4116
|
cb(null, relativePath);
|
|
@@ -4112,7 +4120,7 @@ var require_tmp = __commonJS({
|
|
|
4112
4120
|
if (_isUndefined(name)) return;
|
|
4113
4121
|
const resolvedPath = _resolvePathSync(name, tmpDir);
|
|
4114
4122
|
const relativePath = path5.relative(tmpDir, resolvedPath);
|
|
4115
|
-
if (
|
|
4123
|
+
if (relativePath.startsWith("..") || path5.isAbsolute(relativePath)) {
|
|
4116
4124
|
throw new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`);
|
|
4117
4125
|
}
|
|
4118
4126
|
return relativePath;
|
|
@@ -41173,32 +41181,30 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41173
41181
|
name = "autoVersion";
|
|
41174
41182
|
async execute(context) {
|
|
41175
41183
|
const prepared = context.previousStepResults.generationCommit?.preparedReplay;
|
|
41176
|
-
if (
|
|
41177
|
-
|
|
41178
|
-
"AutoVersionStep:
|
|
41179
|
-
|
|
41180
|
-
|
|
41181
|
-
|
|
41182
|
-
|
|
41183
|
-
|
|
41184
|
-
|
|
41185
|
-
|
|
41186
|
-
|
|
41187
|
-
|
|
41188
|
-
|
|
41189
|
-
|
|
41190
|
-
|
|
41184
|
+
if (prepared) {
|
|
41185
|
+
if (prepared.flow === "skip-application") {
|
|
41186
|
+
this.logger.info("AutoVersionStep: replay flow is skip-application; skipping autoversion.");
|
|
41187
|
+
return { executed: true, success: true };
|
|
41188
|
+
}
|
|
41189
|
+
const language = this.config.language;
|
|
41190
|
+
const mappedMagicVersion = mapMagicVersionForLanguage(MAGIC_VERSION, language);
|
|
41191
|
+
const service = new AutoVersioningService({ logger: this.toTaskLogger() });
|
|
41192
|
+
if (prepared.previousGenerationSha == null) {
|
|
41193
|
+
return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: true });
|
|
41194
|
+
}
|
|
41195
|
+
return await this.handleNormalFlow({
|
|
41196
|
+
prepared,
|
|
41197
|
+
service,
|
|
41198
|
+
language,
|
|
41199
|
+
mappedMagicVersion,
|
|
41200
|
+
previousGenerationSha: prepared.previousGenerationSha
|
|
41201
|
+
});
|
|
41191
41202
|
}
|
|
41192
|
-
|
|
41193
|
-
|
|
41194
|
-
service,
|
|
41195
|
-
language,
|
|
41196
|
-
mappedMagicVersion,
|
|
41197
|
-
previousGenerationSha: prepared.previousGenerationSha
|
|
41198
|
-
});
|
|
41203
|
+
this.logger.info("AutoVersionStep: running in non-replay mode (git diff HEAD).");
|
|
41204
|
+
return await this.handleNonReplayFlow();
|
|
41199
41205
|
}
|
|
41200
41206
|
async handleFirstGeneration(params) {
|
|
41201
|
-
const { service, language, mappedMagicVersion } = params;
|
|
41207
|
+
const { service, language, mappedMagicVersion, commit } = params;
|
|
41202
41208
|
const initialVersion = this.config.baseVersion ?? (mappedMagicVersion.startsWith("v") ? "v0.0.1" : "0.0.1");
|
|
41203
41209
|
if (!isValidSemver(initialVersion)) {
|
|
41204
41210
|
const errorMessage = `AutoVersionStep: baseVersion ${JSON.stringify(initialVersion)} is not a valid semver string (expected e.g. "1.2.3" or "v1.2.3"). Refusing to run to avoid shell injection into the placeholder-rewrite step.`;
|
|
@@ -41215,7 +41221,7 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41215
41221
|
await service.addGoMajorVersionSuffix(this.outputDir, initialVersion);
|
|
41216
41222
|
}
|
|
41217
41223
|
const commitMessage = this.brandMessage("Initial SDK generation");
|
|
41218
|
-
const commitSha = this.commitAutoversion(commitMessage);
|
|
41224
|
+
const commitSha = commit ? this.commitAutoversion(commitMessage) : void 0;
|
|
41219
41225
|
return {
|
|
41220
41226
|
executed: true,
|
|
41221
41227
|
success: true,
|
|
@@ -41234,7 +41240,7 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41234
41240
|
baseVersion: this.config.baseVersion
|
|
41235
41241
|
});
|
|
41236
41242
|
if (previousVersion == null) {
|
|
41237
|
-
return await this.handleFirstGeneration({ service, language, mappedMagicVersion });
|
|
41243
|
+
return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: true });
|
|
41238
41244
|
}
|
|
41239
41245
|
if (rawDiff.trim().length === 0) {
|
|
41240
41246
|
this.logger.info(
|
|
@@ -41278,17 +41284,21 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41278
41284
|
analysis: { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") }
|
|
41279
41285
|
});
|
|
41280
41286
|
}
|
|
41281
|
-
const chunks = service.chunkDiff(cleanedDiff, MAX_AI_DIFF_BYTES);
|
|
41282
|
-
const cappedChunks = chunks.slice(0, MAX_CHUNKS);
|
|
41283
|
-
const skippedChunks = chunks.length - cappedChunks.length;
|
|
41284
|
-
if (chunks.length > 1) {
|
|
41285
|
-
this.logger.info(
|
|
41286
|
-
`AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${MAX_CHUNKS}, skipping ${skippedChunks})` : "")
|
|
41287
|
-
);
|
|
41288
|
-
}
|
|
41289
41287
|
let analysis;
|
|
41290
41288
|
try {
|
|
41291
|
-
|
|
41289
|
+
if (this.config.ai == null && this.config.fernToken != null) {
|
|
41290
|
+
analysis = await this.analyzeViaFaiService(cleanedDiff, language, previousVersion);
|
|
41291
|
+
} else {
|
|
41292
|
+
const chunks = service.chunkDiff(cleanedDiff, MAX_AI_DIFF_BYTES);
|
|
41293
|
+
const cappedChunks = chunks.slice(0, MAX_CHUNKS);
|
|
41294
|
+
const skippedChunks = chunks.length - cappedChunks.length;
|
|
41295
|
+
if (chunks.length > 1) {
|
|
41296
|
+
this.logger.info(
|
|
41297
|
+
`AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${MAX_CHUNKS}, skipping ${skippedChunks})` : "")
|
|
41298
|
+
);
|
|
41299
|
+
}
|
|
41300
|
+
analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
|
|
41301
|
+
}
|
|
41292
41302
|
} catch (error) {
|
|
41293
41303
|
this.logger.warn(`AutoVersionStep: FAI analysis failed (${String(error)}); falling back to PATCH bump.`);
|
|
41294
41304
|
analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
|
|
@@ -41311,6 +41321,106 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41311
41321
|
analysis
|
|
41312
41322
|
});
|
|
41313
41323
|
}
|
|
41324
|
+
/**
|
|
41325
|
+
* Non-replay autoversion: diffs HEAD (committed SDK) vs the working tree
|
|
41326
|
+
* (freshly generated files). Does NOT commit — GithubStep will commit all
|
|
41327
|
+
* changes (including the rewritten placeholder) in a single generation commit.
|
|
41328
|
+
*/
|
|
41329
|
+
async handleNonReplayFlow() {
|
|
41330
|
+
const language = this.config.language;
|
|
41331
|
+
const mappedMagicVersion = mapMagicVersionForLanguage(MAGIC_VERSION, language);
|
|
41332
|
+
const service = new AutoVersioningService({ logger: this.toTaskLogger() });
|
|
41333
|
+
const rawDiff = this.gitDiffHead();
|
|
41334
|
+
const previousVersion = await this.resolvePreviousVersionNonReplay({
|
|
41335
|
+
service,
|
|
41336
|
+
rawDiff,
|
|
41337
|
+
mappedMagicVersion,
|
|
41338
|
+
baseVersion: this.config.baseVersion
|
|
41339
|
+
});
|
|
41340
|
+
if (previousVersion == null) {
|
|
41341
|
+
return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: false });
|
|
41342
|
+
}
|
|
41343
|
+
if (rawDiff.trim().length === 0) {
|
|
41344
|
+
this.logger.info(`AutoVersionStep: empty diff (non-replay); rewriting placeholder to ${previousVersion}.`);
|
|
41345
|
+
return await this.finalizeNoChangeNonReplay({
|
|
41346
|
+
service,
|
|
41347
|
+
language,
|
|
41348
|
+
mappedMagicVersion,
|
|
41349
|
+
previousVersion,
|
|
41350
|
+
reason: "no diff between generations"
|
|
41351
|
+
});
|
|
41352
|
+
}
|
|
41353
|
+
const cleanedDiff = service.cleanDiffForAI(rawDiff, mappedMagicVersion);
|
|
41354
|
+
const rawBytes = Buffer.byteLength(rawDiff, "utf-8");
|
|
41355
|
+
const cleanedBytes = Buffer.byteLength(cleanedDiff, "utf-8");
|
|
41356
|
+
this.logger.debug(
|
|
41357
|
+
`AutoVersionStep (non-replay): raw=${formatSizeKB(rawBytes)}KB (${countFilesInDiff(rawDiff)} files), cleaned=${formatSizeKB(cleanedBytes)}KB (${countFilesInDiff(cleanedDiff)} files)`
|
|
41358
|
+
);
|
|
41359
|
+
if (cleanedDiff.trim().length === 0) {
|
|
41360
|
+
this.logger.info(
|
|
41361
|
+
`AutoVersionStep: cleaned diff empty (non-replay); rewriting placeholder to ${previousVersion}.`
|
|
41362
|
+
);
|
|
41363
|
+
return await this.finalizeNoChangeNonReplay({
|
|
41364
|
+
service,
|
|
41365
|
+
language,
|
|
41366
|
+
mappedMagicVersion,
|
|
41367
|
+
previousVersion,
|
|
41368
|
+
reason: "no semantic changes"
|
|
41369
|
+
});
|
|
41370
|
+
}
|
|
41371
|
+
if (cleanedBytes > MAX_RAW_DIFF_BYTES) {
|
|
41372
|
+
this.logger.warn(
|
|
41373
|
+
`AutoVersionStep (non-replay): diff too large (${formatSizeKB(cleanedBytes)}KB). Falling back to PATCH.`
|
|
41374
|
+
);
|
|
41375
|
+
return await this.finalizeWithBumpNonReplay({
|
|
41376
|
+
service,
|
|
41377
|
+
language,
|
|
41378
|
+
mappedMagicVersion,
|
|
41379
|
+
previousVersion,
|
|
41380
|
+
analysis: { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") }
|
|
41381
|
+
});
|
|
41382
|
+
}
|
|
41383
|
+
let analysis;
|
|
41384
|
+
try {
|
|
41385
|
+
if (this.config.ai == null && this.config.fernToken != null) {
|
|
41386
|
+
analysis = await this.analyzeViaFaiService(cleanedDiff, language, previousVersion);
|
|
41387
|
+
} else {
|
|
41388
|
+
const chunks = service.chunkDiff(cleanedDiff, MAX_AI_DIFF_BYTES);
|
|
41389
|
+
const cappedChunks = chunks.slice(0, MAX_CHUNKS);
|
|
41390
|
+
const skippedChunks = chunks.length - cappedChunks.length;
|
|
41391
|
+
if (chunks.length > 1) {
|
|
41392
|
+
this.logger.info(
|
|
41393
|
+
`AutoVersionStep (non-replay): split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${MAX_CHUNKS}, skipping ${skippedChunks})` : "")
|
|
41394
|
+
);
|
|
41395
|
+
}
|
|
41396
|
+
analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
|
|
41397
|
+
}
|
|
41398
|
+
} catch (error) {
|
|
41399
|
+
this.logger.warn(
|
|
41400
|
+
`AutoVersionStep (non-replay): FAI analysis failed (${String(error)}); falling back to PATCH bump.`
|
|
41401
|
+
);
|
|
41402
|
+
analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
|
|
41403
|
+
}
|
|
41404
|
+
if (analysis == null) {
|
|
41405
|
+
this.logger.info(
|
|
41406
|
+
`AutoVersionStep (non-replay): FAI returned NO_CHANGE; rewriting placeholder to ${previousVersion}.`
|
|
41407
|
+
);
|
|
41408
|
+
return await this.finalizeNoChangeNonReplay({
|
|
41409
|
+
service,
|
|
41410
|
+
language,
|
|
41411
|
+
mappedMagicVersion,
|
|
41412
|
+
previousVersion,
|
|
41413
|
+
reason: "FAI returned NO_CHANGE"
|
|
41414
|
+
});
|
|
41415
|
+
}
|
|
41416
|
+
return await this.finalizeWithBumpNonReplay({
|
|
41417
|
+
service,
|
|
41418
|
+
language,
|
|
41419
|
+
mappedMagicVersion,
|
|
41420
|
+
previousVersion,
|
|
41421
|
+
analysis
|
|
41422
|
+
});
|
|
41423
|
+
}
|
|
41314
41424
|
/**
|
|
41315
41425
|
* Terminal path for runs that determine no semver bump is needed. The
|
|
41316
41426
|
* current working tree still contains `0.0.0-fern-placeholder` from this
|
|
@@ -41368,6 +41478,98 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41368
41478
|
commitSha
|
|
41369
41479
|
};
|
|
41370
41480
|
}
|
|
41481
|
+
/**
|
|
41482
|
+
* Non-replay variant of finalizeNoChange. Rewrites the placeholder to
|
|
41483
|
+
* `previousVersion` but does NOT commit — GithubStep handles the commit.
|
|
41484
|
+
*/
|
|
41485
|
+
async finalizeNoChangeNonReplay(params) {
|
|
41486
|
+
const { service, language, mappedMagicVersion, previousVersion, reason } = params;
|
|
41487
|
+
if (!isValidSemver(previousVersion)) {
|
|
41488
|
+
const errorMessage = `AutoVersionStep: resolved previousVersion ${JSON.stringify(previousVersion)} is not a valid semver string. Refusing to rewrite placeholder to avoid shell injection.`;
|
|
41489
|
+
this.logger.error(errorMessage);
|
|
41490
|
+
return { executed: true, success: false, errorMessage };
|
|
41491
|
+
}
|
|
41492
|
+
await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, previousVersion);
|
|
41493
|
+
if (language === "go") {
|
|
41494
|
+
await service.addGoMajorVersionSuffix(this.outputDir, previousVersion);
|
|
41495
|
+
}
|
|
41496
|
+
const commitMessage = this.brandMessage(`SDK regeneration (no semver change: ${reason})`);
|
|
41497
|
+
return {
|
|
41498
|
+
executed: true,
|
|
41499
|
+
success: true,
|
|
41500
|
+
version: previousVersion,
|
|
41501
|
+
previousVersion,
|
|
41502
|
+
versionBump: "NO_CHANGE",
|
|
41503
|
+
commitMessage
|
|
41504
|
+
};
|
|
41505
|
+
}
|
|
41506
|
+
/**
|
|
41507
|
+
* Non-replay variant of finalizeWithBump. Rewrites the placeholder to
|
|
41508
|
+
* the bumped version but does NOT commit — GithubStep handles the commit.
|
|
41509
|
+
*/
|
|
41510
|
+
async finalizeWithBumpNonReplay(params) {
|
|
41511
|
+
const { service, language, mappedMagicVersion, previousVersion, analysis } = params;
|
|
41512
|
+
const newVersion = incrementVersion(previousVersion, analysis.versionBump);
|
|
41513
|
+
this.logger.info(
|
|
41514
|
+
`AutoVersionStep (non-replay): ${analysis.versionBump} bump: ${previousVersion} \u2192 ${newVersion}`
|
|
41515
|
+
);
|
|
41516
|
+
await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, newVersion);
|
|
41517
|
+
if (language === "go") {
|
|
41518
|
+
await service.addGoMajorVersionSuffix(this.outputDir, newVersion);
|
|
41519
|
+
}
|
|
41520
|
+
if (analysis.changelogEntry && analysis.changelogEntry.trim().length > 0) {
|
|
41521
|
+
await this.prependChangelogEntry({ version: newVersion, entry: analysis.changelogEntry });
|
|
41522
|
+
}
|
|
41523
|
+
return {
|
|
41524
|
+
executed: true,
|
|
41525
|
+
success: true,
|
|
41526
|
+
version: newVersion,
|
|
41527
|
+
commitMessage: analysis.message,
|
|
41528
|
+
changelogEntry: analysis.changelogEntry,
|
|
41529
|
+
previousVersion,
|
|
41530
|
+
versionBump: analysis.versionBump,
|
|
41531
|
+
prDescription: analysis.prDescription,
|
|
41532
|
+
versionBumpReason: analysis.versionBumpReason
|
|
41533
|
+
};
|
|
41534
|
+
}
|
|
41535
|
+
/**
|
|
41536
|
+
* Resolves the previous version for non-replay mode. Reads HEAD:.fern/metadata.json
|
|
41537
|
+
* (not HEAD~1, since no generation commit exists yet) as the primary fallback.
|
|
41538
|
+
*/
|
|
41539
|
+
async resolvePreviousVersionNonReplay(params) {
|
|
41540
|
+
const { service, rawDiff, mappedMagicVersion, baseVersion } = params;
|
|
41541
|
+
if (baseVersion != null && isValidSemver(baseVersion)) {
|
|
41542
|
+
this.logger.debug(
|
|
41543
|
+
`AutoVersionStep (non-replay): previous version from pipeline baseVersion: ${baseVersion}`
|
|
41544
|
+
);
|
|
41545
|
+
return this.normalizeVersionPrefix(baseVersion, mappedMagicVersion);
|
|
41546
|
+
}
|
|
41547
|
+
try {
|
|
41548
|
+
const extracted = service.extractPreviousVersion(rawDiff, mappedMagicVersion);
|
|
41549
|
+
if (extracted != null) {
|
|
41550
|
+
this.logger.debug(`AutoVersionStep (non-replay): previous version from diff: ${extracted}`);
|
|
41551
|
+
return extracted;
|
|
41552
|
+
}
|
|
41553
|
+
} catch (error) {
|
|
41554
|
+
if (!(error instanceof AutoVersioningException) || !error.magicVersionAbsent) {
|
|
41555
|
+
throw error;
|
|
41556
|
+
}
|
|
41557
|
+
this.logger.info("AutoVersionStep (non-replay): magic version not in diff; trying metadata + git tags.");
|
|
41558
|
+
}
|
|
41559
|
+
const metadataVersion = this.readVersionFromMetadataAtHead();
|
|
41560
|
+
if (metadataVersion != null) {
|
|
41561
|
+
return this.normalizeVersionPrefix(metadataVersion, mappedMagicVersion);
|
|
41562
|
+
}
|
|
41563
|
+
try {
|
|
41564
|
+
const tagVersion = await service.getLatestVersionFromGitTags(this.outputDir);
|
|
41565
|
+
if (tagVersion != null) {
|
|
41566
|
+
return this.normalizeVersionPrefix(tagVersion, mappedMagicVersion);
|
|
41567
|
+
}
|
|
41568
|
+
} catch (error) {
|
|
41569
|
+
this.logger.debug(`AutoVersionStep (non-replay): git-tags fallback failed (${String(error)}); ignoring.`);
|
|
41570
|
+
}
|
|
41571
|
+
return null;
|
|
41572
|
+
}
|
|
41371
41573
|
async resolvePreviousVersion(params) {
|
|
41372
41574
|
const { service, rawDiff, mappedMagicVersion, baseVersion } = params;
|
|
41373
41575
|
if (baseVersion != null && isValidSemver(baseVersion)) {
|
|
@@ -41417,6 +41619,42 @@ var AutoVersionStep = class extends BaseStep {
|
|
|
41417
41619
|
const stripped = version2.startsWith("v") ? version2.slice(1) : version2;
|
|
41418
41620
|
return mappedMagicVersion.startsWith("v") ? `v${stripped}` : stripped;
|
|
41419
41621
|
}
|
|
41622
|
+
/**
|
|
41623
|
+
* Reads the sdkVersion from the committed (HEAD) .fern/metadata.json.
|
|
41624
|
+
* Used by the non-replay path where no generation commit exists yet,
|
|
41625
|
+
* so HEAD is the unmodified SDK repository.
|
|
41626
|
+
*/
|
|
41627
|
+
readVersionFromMetadataAtHead() {
|
|
41628
|
+
try {
|
|
41629
|
+
const output = (0, import_child_process4.execFileSync)("git", ["show", "HEAD:.fern/metadata.json"], {
|
|
41630
|
+
cwd: this.outputDir,
|
|
41631
|
+
encoding: "utf-8",
|
|
41632
|
+
stdio: "pipe"
|
|
41633
|
+
});
|
|
41634
|
+
const parsed = JSON.parse(output);
|
|
41635
|
+
return parsed.sdkVersion ?? void 0;
|
|
41636
|
+
} catch (error) {
|
|
41637
|
+
this.logger.debug(`AutoVersionStep: failed to read HEAD:.fern/metadata.json (${String(error)})`);
|
|
41638
|
+
return void 0;
|
|
41639
|
+
}
|
|
41640
|
+
}
|
|
41641
|
+
/**
|
|
41642
|
+
* Computes a diff between HEAD (committed SDK) and the working tree (new
|
|
41643
|
+
* generation). Marks untracked files as intent-to-add so they appear in
|
|
41644
|
+
* the diff, matching LocalTaskHandler.generateDiffFile() behavior.
|
|
41645
|
+
*/
|
|
41646
|
+
gitDiffHead() {
|
|
41647
|
+
(0, import_child_process4.execFileSync)("git", ["add", "-N", "."], {
|
|
41648
|
+
cwd: this.outputDir,
|
|
41649
|
+
stdio: "pipe"
|
|
41650
|
+
});
|
|
41651
|
+
return (0, import_child_process4.execFileSync)("git", ["diff", "HEAD", "--", ".", ":(exclude).fern/metadata.json"], {
|
|
41652
|
+
cwd: this.outputDir,
|
|
41653
|
+
encoding: "utf-8",
|
|
41654
|
+
stdio: "pipe",
|
|
41655
|
+
maxBuffer: 256 * 1024 * 1024
|
|
41656
|
+
});
|
|
41657
|
+
}
|
|
41420
41658
|
gitDiff(from, to) {
|
|
41421
41659
|
return (0, import_child_process4.execFileSync)("git", ["diff", from, to, "--", ".", ":(exclude).fern/metadata.json"], {
|
|
41422
41660
|
cwd: this.outputDir,
|
|
@@ -41568,6 +41806,47 @@ ${newBlock}${remainder}`;
|
|
|
41568
41806
|
};
|
|
41569
41807
|
}
|
|
41570
41808
|
}
|
|
41809
|
+
/**
|
|
41810
|
+
* Calls the hosted FAI service (`/sdks/analyze-commit-diff`) with the fern token.
|
|
41811
|
+
* Used when no BAML `ai` config is supplied (remote generation via fiddle). FAI
|
|
41812
|
+
* handles chunking, parallelism, and retries server-side. Returns null on
|
|
41813
|
+
* NO_CHANGE; throws on transport/HTTP errors so the caller's PATCH fallback applies.
|
|
41814
|
+
*/
|
|
41815
|
+
async analyzeViaFaiService(cleanedDiff, language, previousVersion) {
|
|
41816
|
+
const baseUrl = this.config.faiBaseUrl ?? "https://fai.buildwithfern.com";
|
|
41817
|
+
const response = await fetch(`${baseUrl}/sdks/analyze-commit-diff`, {
|
|
41818
|
+
method: "POST",
|
|
41819
|
+
headers: {
|
|
41820
|
+
Authorization: `Bearer ${this.config.fernToken}`,
|
|
41821
|
+
"Content-Type": "application/json"
|
|
41822
|
+
},
|
|
41823
|
+
body: JSON.stringify({
|
|
41824
|
+
diff: cleanedDiff,
|
|
41825
|
+
language,
|
|
41826
|
+
previous_version: previousVersion,
|
|
41827
|
+
prior_changelog: this.config.priorChangelog ?? void 0,
|
|
41828
|
+
spec_commit_message: this.config.specCommitMessage ?? void 0
|
|
41829
|
+
})
|
|
41830
|
+
});
|
|
41831
|
+
if (!response.ok) {
|
|
41832
|
+
const body = await response.text().catch(() => "");
|
|
41833
|
+
throw new Error(`FAI analyze-commit-diff failed with status ${response.status}: ${body.slice(0, 500)}`);
|
|
41834
|
+
}
|
|
41835
|
+
const parsed = await response.json();
|
|
41836
|
+
if (!isFaiAnalyzeResponse(parsed)) {
|
|
41837
|
+
throw new Error("FAI analyze-commit-diff returned an unexpected response shape");
|
|
41838
|
+
}
|
|
41839
|
+
if (parsed.version_bump === "NO_CHANGE") {
|
|
41840
|
+
return null;
|
|
41841
|
+
}
|
|
41842
|
+
return {
|
|
41843
|
+
versionBump: parsed.version_bump,
|
|
41844
|
+
message: this.brandMessage(parsed.message),
|
|
41845
|
+
changelogEntry: nonEmpty(parsed.changelog_entry),
|
|
41846
|
+
prDescription: nonEmpty(parsed.pr_description),
|
|
41847
|
+
versionBumpReason: nonEmpty(parsed.version_bump_reason)
|
|
41848
|
+
};
|
|
41849
|
+
}
|
|
41571
41850
|
/**
|
|
41572
41851
|
* Dynamically imports @fern-api/cli-ai only when autoversion actually needs to
|
|
41573
41852
|
* call FAI. The package is ESM-only with extensionless internal imports, so
|
|
@@ -41609,6 +41888,20 @@ ${newBlock}${remainder}`;
|
|
|
41609
41888
|
return adapter;
|
|
41610
41889
|
}
|
|
41611
41890
|
};
|
|
41891
|
+
var FAI_VERSION_BUMPS = ["MAJOR", "MINOR", "PATCH", "NO_CHANGE"];
|
|
41892
|
+
function isFaiAnalyzeResponse(value) {
|
|
41893
|
+
if (typeof value !== "object" || value == null) {
|
|
41894
|
+
return false;
|
|
41895
|
+
}
|
|
41896
|
+
const candidate = value;
|
|
41897
|
+
return typeof candidate.message === "string" && typeof candidate.version_bump === "string" && FAI_VERSION_BUMPS.includes(candidate.version_bump) && isStringOrAbsent(candidate.changelog_entry) && isStringOrAbsent(candidate.pr_description) && isStringOrAbsent(candidate.version_bump_reason);
|
|
41898
|
+
}
|
|
41899
|
+
function isStringOrAbsent(value) {
|
|
41900
|
+
return value == null || typeof value === "string";
|
|
41901
|
+
}
|
|
41902
|
+
function nonEmpty(value) {
|
|
41903
|
+
return value != null && value.trim().length > 0 ? value : void 0;
|
|
41904
|
+
}
|
|
41612
41905
|
|
|
41613
41906
|
// src/pipeline/steps/GenerationCommitStep.ts
|
|
41614
41907
|
var import_child_process5 = require("child_process");
|
|
@@ -42836,10 +43129,6 @@ var PostGenerationPipeline = class {
|
|
|
42836
43129
|
);
|
|
42837
43130
|
replayEnabled = false;
|
|
42838
43131
|
}
|
|
42839
|
-
if (autoVersionEnabled && !replayEnabled) {
|
|
42840
|
-
this.logger.warn("AutoVersion requires Replay to be enabled. Disabling AutoVersion for this run.");
|
|
42841
|
-
autoVersionEnabled = false;
|
|
42842
|
-
}
|
|
42843
43132
|
if (replayEnabled && config.replay != null) {
|
|
42844
43133
|
this.steps.push(
|
|
42845
43134
|
new GenerationCommitStep(
|