@fern-api/generator-cli 0.9.31 → 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.
- package/dist/cli.js +135 -25
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2021,7 +2021,7 @@ var init_esm = __esm({
|
|
|
2021
2021
|
}
|
|
2022
2022
|
});
|
|
2023
2023
|
|
|
2024
|
-
// ../../node_modules/.pnpm/brace-expansion@5.0.
|
|
2024
|
+
// ../../node_modules/.pnpm/brace-expansion@5.0.6/node_modules/brace-expansion/dist/esm/index.js
|
|
2025
2025
|
function numeric(str) {
|
|
2026
2026
|
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
|
2027
2027
|
}
|
|
@@ -2124,7 +2124,7 @@ function expand_(str, max, isTop) {
|
|
|
2124
2124
|
}
|
|
2125
2125
|
const pad = n.some(isPadded);
|
|
2126
2126
|
N2 = [];
|
|
2127
|
-
for (let i2 = x2; test(i2, y2); i2 += incr) {
|
|
2127
|
+
for (let i2 = x2; test(i2, y2) && N2.length < max; i2 += incr) {
|
|
2128
2128
|
let c3;
|
|
2129
2129
|
if (isAlphaSequence) {
|
|
2130
2130
|
c3 = String.fromCharCode(i2);
|
|
@@ -2166,7 +2166,7 @@ function expand_(str, max, isTop) {
|
|
|
2166
2166
|
}
|
|
2167
2167
|
var escSlash, escOpen, escClose, escComma, escPeriod, escSlashPattern, escOpenPattern, escClosePattern, escCommaPattern, escPeriodPattern, slashPattern, openPattern, closePattern, commaPattern, periodPattern, EXPANSION_MAX;
|
|
2168
2168
|
var init_esm2 = __esm({
|
|
2169
|
-
"../../node_modules/.pnpm/brace-expansion@5.0.
|
|
2169
|
+
"../../node_modules/.pnpm/brace-expansion@5.0.6/node_modules/brace-expansion/dist/esm/index.js"() {
|
|
2170
2170
|
"use strict";
|
|
2171
2171
|
init_esm();
|
|
2172
2172
|
escSlash = "\0SLASH" + Math.random() + "\0";
|
|
@@ -22126,7 +22126,7 @@ var init_diff3 = __esm({
|
|
|
22126
22126
|
}
|
|
22127
22127
|
});
|
|
22128
22128
|
|
|
22129
|
-
// ../../node_modules/.pnpm/@fern-api+replay@0.16.
|
|
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 =
|
|
23127
|
-
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
24263
|
-
|
|
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 [];
|
|
@@ -41617,18 +41662,16 @@ async function pushSignedCommit({
|
|
|
41617
41662
|
await repository.pushObjectToRef(localHeadSha, tempRef);
|
|
41618
41663
|
tempRefPushed = true;
|
|
41619
41664
|
for (let attempt2 = 0; attempt2 < MAX_CONCURRENT_PUSH_RETRIES; attempt2++) {
|
|
41620
|
-
const
|
|
41621
|
-
|
|
41622
|
-
email: author?.email ?? FERN_BOT_EMAIL2
|
|
41623
|
-
};
|
|
41665
|
+
const resolvedAuthor = author ?? { name: FERN_BOT_NAME2, email: FERN_BOT_EMAIL2 };
|
|
41666
|
+
const committerField = author != null ? { committer: author } : {};
|
|
41624
41667
|
const { data: signedCommit } = await octokit.git.createCommit({
|
|
41625
41668
|
owner,
|
|
41626
41669
|
repo,
|
|
41627
41670
|
message,
|
|
41628
41671
|
tree: treeSha,
|
|
41629
41672
|
parents,
|
|
41630
|
-
author:
|
|
41631
|
-
|
|
41673
|
+
author: resolvedAuthor,
|
|
41674
|
+
...committerField
|
|
41632
41675
|
});
|
|
41633
41676
|
const signedSha = signedCommit.sha;
|
|
41634
41677
|
try {
|
|
@@ -41860,6 +41903,8 @@ var GithubStep = class extends BaseStep {
|
|
|
41860
41903
|
);
|
|
41861
41904
|
case "push":
|
|
41862
41905
|
return await this.executePushMode(repository, resolvedPrFields);
|
|
41906
|
+
case "commit-and-release":
|
|
41907
|
+
return await this.executeCommitAndReleaseMode(repository, resolvedPrFields);
|
|
41863
41908
|
default: {
|
|
41864
41909
|
const exhaustive = mode;
|
|
41865
41910
|
throw new Error(`Unexpected GitHub mode: ${String(exhaustive)}`);
|
|
@@ -42086,6 +42131,71 @@ var GithubStep = class extends BaseStep {
|
|
|
42086
42131
|
}
|
|
42087
42132
|
return result;
|
|
42088
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
|
+
}
|
|
42089
42199
|
createOctokit() {
|
|
42090
42200
|
const opts = { auth: this.config.token };
|
|
42091
42201
|
if (this.config.apiBaseUrl != null) {
|
|
@@ -42530,9 +42640,9 @@ var PostGenerationPipeline = class {
|
|
|
42530
42640
|
this.logger = logger;
|
|
42531
42641
|
let replayEnabled = config.replay?.enabled ?? false;
|
|
42532
42642
|
let autoVersionEnabled = config.autoVersion?.enabled ?? false;
|
|
42533
|
-
if (replayEnabled && config.github?.mode === "push") {
|
|
42643
|
+
if (replayEnabled && (config.github?.mode === "push" || config.github?.mode === "commit-and-release")) {
|
|
42534
42644
|
this.logger.warn(
|
|
42535
|
-
|
|
42645
|
+
`Replay is not supported with GitHub ${config.github.mode} mode. Disabling replay to prevent push to base branch.`
|
|
42536
42646
|
);
|
|
42537
42647
|
replayEnabled = false;
|
|
42538
42648
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fern-api/generator-cli",
|
|
3
|
-
"version": "0.9.
|
|
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.
|
|
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",
|