@fern-api/generator-cli 0.9.10 → 0.9.11
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/api.d.ts +80 -2
- package/dist/api.js +287 -20
- package/dist/cli.js +223 -18
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -499,6 +499,7 @@ declare const consolePipelineLogger: PipelineLogger;
|
|
|
499
499
|
interface PipelineConfig {
|
|
500
500
|
outputDir: string;
|
|
501
501
|
replay?: ReplayStepConfig;
|
|
502
|
+
autoVersion?: AutoVersionStepConfig;
|
|
502
503
|
fernignore?: FernignoreStepConfig;
|
|
503
504
|
github?: GithubStepConfig;
|
|
504
505
|
cliVersion?: string;
|
|
@@ -508,6 +509,7 @@ interface PipelineConfig {
|
|
|
508
509
|
interface PipelineContext {
|
|
509
510
|
previousStepResults: {
|
|
510
511
|
replay?: ReplayStepResult;
|
|
512
|
+
autoVersion?: AutoVersionStepResult;
|
|
511
513
|
fernignore?: FernignoreStepResult;
|
|
512
514
|
};
|
|
513
515
|
}
|
|
@@ -516,6 +518,17 @@ interface ReplayStepConfig {
|
|
|
516
518
|
stageOnly?: boolean;
|
|
517
519
|
skipApplication?: boolean;
|
|
518
520
|
}
|
|
521
|
+
interface AutoVersionStepConfig {
|
|
522
|
+
enabled: boolean;
|
|
523
|
+
/** Generator language: typescript | python | java | go | ruby | csharp | php | swift | rust | kotlin */
|
|
524
|
+
language: string;
|
|
525
|
+
/** Existing changelog content passed to the AI for context. */
|
|
526
|
+
priorChangelog?: string;
|
|
527
|
+
/** Fallback version when no prior generation exists (first run). */
|
|
528
|
+
baseVersion?: string;
|
|
529
|
+
/** Fern token used to authorize the FAI (AnalyzeSdkDiff) call. */
|
|
530
|
+
fernToken: string;
|
|
531
|
+
}
|
|
519
532
|
interface FernignoreStepConfig {
|
|
520
533
|
enabled: boolean;
|
|
521
534
|
customContents?: string;
|
|
@@ -555,8 +568,10 @@ interface GithubStepConfig {
|
|
|
555
568
|
previousGenerationSha: string;
|
|
556
569
|
currentGenerationSha: string;
|
|
557
570
|
};
|
|
558
|
-
/** When true: separate PRs per generation,
|
|
571
|
+
/** When true: separate PRs per generation, automerge support, run_id in body */
|
|
559
572
|
automationMode?: boolean;
|
|
573
|
+
/** When true: skip opening a PR / pushing when the generated output has no diff from the base branch. */
|
|
574
|
+
skipIfNoDiff?: boolean;
|
|
560
575
|
/** Enable GitHub automerge on the PR (only effective when automationMode && !hasBreakingChanges) */
|
|
561
576
|
autoMerge?: boolean;
|
|
562
577
|
/** Pre-computed: version bump is MAJOR (from --version AUTO AI analysis) */
|
|
@@ -570,6 +585,7 @@ interface PipelineResult {
|
|
|
570
585
|
success: boolean;
|
|
571
586
|
steps: {
|
|
572
587
|
replay?: ReplayStepResult;
|
|
588
|
+
autoVersion?: AutoVersionStepResult;
|
|
573
589
|
fernignore?: FernignoreStepResult;
|
|
574
590
|
github?: GithubStepResult;
|
|
575
591
|
};
|
|
@@ -602,6 +618,24 @@ interface ReplayStepResult extends StepResult {
|
|
|
602
618
|
interface FernignoreStepResult extends StepResult {
|
|
603
619
|
pathsPreserved?: string[];
|
|
604
620
|
}
|
|
621
|
+
interface AutoVersionStepResult extends StepResult {
|
|
622
|
+
/** The new version to use (e.g. "1.2.3"). Field name matches fiddle's Java AutoVersionResult DTO. */
|
|
623
|
+
version?: string;
|
|
624
|
+
/** Commit subject for the `[fern-autoversion]` commit. */
|
|
625
|
+
commitMessage?: string;
|
|
626
|
+
/** User-facing changelog entry appended to changelog.md. */
|
|
627
|
+
changelogEntry?: string;
|
|
628
|
+
/** Prior SDK version before this change (e.g. "1.2.2"). */
|
|
629
|
+
previousVersion?: string;
|
|
630
|
+
/** The version bump level determined by FAI. */
|
|
631
|
+
versionBump?: "MAJOR" | "MINOR" | "PATCH" | "NO_CHANGE";
|
|
632
|
+
/** Structured PR description with Before/After code fences for breaking changes. */
|
|
633
|
+
prDescription?: string;
|
|
634
|
+
/** One-sentence justification for WHY the version bump was chosen. */
|
|
635
|
+
versionBumpReason?: string;
|
|
636
|
+
/** SHA of the `[fern-autoversion]` commit once it's been made. TS-only; no fiddle counterpart. */
|
|
637
|
+
commitSha?: string;
|
|
638
|
+
}
|
|
605
639
|
interface GithubStepResult extends StepResult {
|
|
606
640
|
commitSha?: string;
|
|
607
641
|
branchUrl?: string;
|
|
@@ -733,4 +767,48 @@ interface ReplayStatusParams {
|
|
|
733
767
|
}
|
|
734
768
|
declare function replayStatus(params: ReplayStatusParams): StatusResult;
|
|
735
769
|
|
|
736
|
-
|
|
770
|
+
interface SubmitReplayInitParams {
|
|
771
|
+
/** Fiddle API origin (e.g. "https://fiddle.buildwithfern.com") */
|
|
772
|
+
fiddleOrigin: string;
|
|
773
|
+
/** Fern auth token */
|
|
774
|
+
fernToken: string;
|
|
775
|
+
/** GitHub repository (e.g. "owner/repo" or full URL) */
|
|
776
|
+
githubRepo: string;
|
|
777
|
+
/** Result from replayInit() */
|
|
778
|
+
initResult: ReplayInitResult;
|
|
779
|
+
}
|
|
780
|
+
interface SubmitReplayInitResult {
|
|
781
|
+
/** URL of the PR created by Fiddle */
|
|
782
|
+
prUrl: string;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Submits a Replay init lockfile to the Fiddle API for server-side PR creation.
|
|
786
|
+
*
|
|
787
|
+
* Extracts owner/repo from the GitHub repository string, posts the lockfile
|
|
788
|
+
* and related metadata to the Fiddle remote-gen endpoint, and returns the
|
|
789
|
+
* resulting PR URL.
|
|
790
|
+
*
|
|
791
|
+
* This function is shared between CLI v1 and CLI v2 to avoid duplicating
|
|
792
|
+
* the Fiddle submission logic.
|
|
793
|
+
*/
|
|
794
|
+
declare function submitReplayInit(params: SubmitReplayInitParams): Promise<SubmitReplayInitResult>;
|
|
795
|
+
/**
|
|
796
|
+
* Parses a GitHub repository string into owner and repo components.
|
|
797
|
+
* Accepts formats: "owner/repo", "github.com/owner/repo",
|
|
798
|
+
* "https://github.com/owner/repo", "https://github.com/owner/repo.git"
|
|
799
|
+
*/
|
|
800
|
+
declare function parseOwnerRepo(githubRepo: string): {
|
|
801
|
+
owner: string;
|
|
802
|
+
repo: string;
|
|
803
|
+
};
|
|
804
|
+
/**
|
|
805
|
+
* Error thrown when submitting a Replay init to Fiddle fails.
|
|
806
|
+
* Consumers can inspect `statusCode` to distinguish between
|
|
807
|
+
* different failure modes (e.g. 404 = GitHub App not installed).
|
|
808
|
+
*/
|
|
809
|
+
declare class ReplaySubmitError extends Error {
|
|
810
|
+
readonly statusCode: number | undefined;
|
|
811
|
+
constructor(message: string, statusCode?: number);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export { type BootstrapLogEntry, type GenerateReadmeParams, type GenerateReadmeToStreamParams, type GenerateReferenceParams, type GenerateReferenceToStreamParams, type GithubPrParams, type GithubPushParams, type GithubReleaseParams, type GithubStepConfig, type GithubStepResult, type PipelineConfig, type PipelineContext, type PipelineLogger, type PipelineResult, PostGenerationPipeline, type ReplayForgetParams, type ReplayInitParams, type ReplayInitResult, type ReplayResolveParams, type ReplayRunParams, type ReplayRunResult, type ReplayStatusParams, type ReplayStepResult, ReplaySubmitError, type SubmitReplayInitParams, type SubmitReplayInitResult, consolePipelineLogger, formatBootstrapSummary, formatReplayPrBody, generateReadme, generateReadmeToStream, generateReference, generateReferenceToStream, githubPr, githubPush, githubRelease, logReplaySummary, parseOwnerRepo, replayForget, replayInit, replayResolve, replayRun, replayStatus, submitReplayInit };
|
package/dist/api.js
CHANGED
|
@@ -881,6 +881,7 @@ var require_dist2 = __commonJS({
|
|
|
881
881
|
var api_exports2 = {};
|
|
882
882
|
__export(api_exports2, {
|
|
883
883
|
PostGenerationPipeline: () => PostGenerationPipeline,
|
|
884
|
+
ReplaySubmitError: () => ReplaySubmitError,
|
|
884
885
|
consolePipelineLogger: () => consolePipelineLogger,
|
|
885
886
|
formatBootstrapSummary: () => formatBootstrapSummary,
|
|
886
887
|
formatReplayPrBody: () => formatReplayPrBody,
|
|
@@ -892,11 +893,13 @@ __export(api_exports2, {
|
|
|
892
893
|
githubPush: () => githubPush,
|
|
893
894
|
githubRelease: () => githubRelease,
|
|
894
895
|
logReplaySummary: () => logReplaySummary,
|
|
896
|
+
parseOwnerRepo: () => parseOwnerRepo,
|
|
895
897
|
replayForget: () => replayForget,
|
|
896
898
|
replayInit: () => replayInit,
|
|
897
899
|
replayResolve: () => replayResolve,
|
|
898
900
|
replayRun: () => replayRun,
|
|
899
|
-
replayStatus: () => replayStatus
|
|
901
|
+
replayStatus: () => replayStatus,
|
|
902
|
+
submitReplayInit: () => submitReplayInit
|
|
900
903
|
});
|
|
901
904
|
module.exports = __toCommonJS(api_exports2);
|
|
902
905
|
|
|
@@ -6017,6 +6020,46 @@ var ClonedRepository = class _ClonedRepository {
|
|
|
6017
6020
|
const result = await this.git.raw(["rev-parse", "HEAD^{tree}"]);
|
|
6018
6021
|
return result.trim();
|
|
6019
6022
|
}
|
|
6023
|
+
async getHeadCommitMessage() {
|
|
6024
|
+
await this.git.cwd(this.clonePath);
|
|
6025
|
+
const result = await this.git.raw(["log", "-1", "--format=%B", "HEAD"]);
|
|
6026
|
+
return result.replace(/\n$/, "");
|
|
6027
|
+
}
|
|
6028
|
+
async getHeadParents() {
|
|
6029
|
+
await this.git.cwd(this.clonePath);
|
|
6030
|
+
const result = await this.git.raw(["rev-list", "--parents", "-n", "1", "HEAD"]);
|
|
6031
|
+
const parts = result.trim().split(/\s+/);
|
|
6032
|
+
return parts.slice(1);
|
|
6033
|
+
}
|
|
6034
|
+
/**
|
|
6035
|
+
* Pushes a specific object (commit SHA) to a remote ref.
|
|
6036
|
+
* Used to upload tree and blob objects to the remote without moving a branch.
|
|
6037
|
+
*
|
|
6038
|
+
* Pass `force: true` when overwriting a ref that may already point to an
|
|
6039
|
+
* unrelated commit (e.g. retrying a temp ref after a local rebase rewrites
|
|
6040
|
+
* history so the new SHA is not a descendant of the previous one).
|
|
6041
|
+
*/
|
|
6042
|
+
async pushObjectToRef(localSha, remoteRef, { force = false } = {}) {
|
|
6043
|
+
await this.git.cwd(this.clonePath);
|
|
6044
|
+
const spec = force ? `+${localSha}:${remoteRef}` : `${localSha}:${remoteRef}`;
|
|
6045
|
+
await this.git.raw(["push", "origin", spec]);
|
|
6046
|
+
}
|
|
6047
|
+
/**
|
|
6048
|
+
* Resets the current branch HEAD to the given SHA. After fetching a newly-created remote
|
|
6049
|
+
* signed commit, use this to align the local branch so subsequent operations (tag push,
|
|
6050
|
+
* getHeadSha(), etc.) see the signed SHA.
|
|
6051
|
+
*/
|
|
6052
|
+
async resetHardToSha(sha) {
|
|
6053
|
+
await this.git.cwd(this.clonePath);
|
|
6054
|
+
await this.git.raw(["reset", "--hard", sha]);
|
|
6055
|
+
}
|
|
6056
|
+
/**
|
|
6057
|
+
* Rebases the current branch onto the given remote branch (`git pull --rebase origin <branch>`).
|
|
6058
|
+
*/
|
|
6059
|
+
async pullWithRebase(branch) {
|
|
6060
|
+
await this.git.cwd(this.clonePath);
|
|
6061
|
+
await this.git.raw(["pull", "origin", branch, "--rebase"]);
|
|
6062
|
+
}
|
|
6020
6063
|
async getCommitTreeHash(commitSha) {
|
|
6021
6064
|
await this.git.cwd(this.clonePath);
|
|
6022
6065
|
const result = await this.git.raw(["rev-parse", `${commitSha}^{tree}`]);
|
|
@@ -10024,6 +10067,35 @@ var FERN_BOT_NAME = "fern-api";
|
|
|
10024
10067
|
var FERN_BOT_EMAIL = "115122769+fern-api[bot]@users.noreply.github.com";
|
|
10025
10068
|
var FERN_BOT_LOGIN = "fern-api[bot]";
|
|
10026
10069
|
|
|
10070
|
+
// src/pipeline/steps/BaseStep.ts
|
|
10071
|
+
var BaseStep = class {
|
|
10072
|
+
constructor(outputDir, logger) {
|
|
10073
|
+
this.outputDir = outputDir;
|
|
10074
|
+
this.logger = logger;
|
|
10075
|
+
}
|
|
10076
|
+
outputDir;
|
|
10077
|
+
logger;
|
|
10078
|
+
};
|
|
10079
|
+
|
|
10080
|
+
// src/pipeline/steps/AutoVersionStep.ts
|
|
10081
|
+
var AutoVersionStep = class extends BaseStep {
|
|
10082
|
+
constructor(outputDir, logger, config) {
|
|
10083
|
+
super(outputDir, logger);
|
|
10084
|
+
this.config = config;
|
|
10085
|
+
}
|
|
10086
|
+
config;
|
|
10087
|
+
name = "autoVersion";
|
|
10088
|
+
async execute(_context) {
|
|
10089
|
+
this.logger.info(
|
|
10090
|
+
`AutoVersionStep: no-op scaffold for language=${this.config.language} (logic lands in FER-9980)`
|
|
10091
|
+
);
|
|
10092
|
+
return {
|
|
10093
|
+
executed: true,
|
|
10094
|
+
success: true
|
|
10095
|
+
};
|
|
10096
|
+
}
|
|
10097
|
+
};
|
|
10098
|
+
|
|
10027
10099
|
// src/pipeline/steps/GithubStep.ts
|
|
10028
10100
|
var import_rest2 = require("@octokit/rest");
|
|
10029
10101
|
var import_promises2 = require("fs/promises");
|
|
@@ -10157,6 +10229,128 @@ ${prBody}`;
|
|
|
10157
10229
|
return { prTitle, prBody };
|
|
10158
10230
|
}
|
|
10159
10231
|
|
|
10232
|
+
// src/pipeline/github/pushSignedCommit.ts
|
|
10233
|
+
var MAX_CONCURRENT_PUSH_RETRIES = 3;
|
|
10234
|
+
async function pushSignedCommit({
|
|
10235
|
+
repository,
|
|
10236
|
+
octokit,
|
|
10237
|
+
owner,
|
|
10238
|
+
repo,
|
|
10239
|
+
branch,
|
|
10240
|
+
force,
|
|
10241
|
+
rebaseOnConflict = false,
|
|
10242
|
+
logger
|
|
10243
|
+
}) {
|
|
10244
|
+
const tempRef = `refs/temp/fern-${Date.now()}`;
|
|
10245
|
+
let tempRefPushed = false;
|
|
10246
|
+
try {
|
|
10247
|
+
let [localHeadSha, treeSha, message, parents] = await Promise.all([
|
|
10248
|
+
repository.getHeadSha(),
|
|
10249
|
+
repository.getHeadTreeHash(),
|
|
10250
|
+
repository.getHeadCommitMessage(),
|
|
10251
|
+
repository.getHeadParents()
|
|
10252
|
+
]);
|
|
10253
|
+
await repository.pushObjectToRef(localHeadSha, tempRef);
|
|
10254
|
+
tempRefPushed = true;
|
|
10255
|
+
for (let attempt = 0; attempt < MAX_CONCURRENT_PUSH_RETRIES; attempt++) {
|
|
10256
|
+
const { data: signedCommit } = await octokit.git.createCommit({
|
|
10257
|
+
owner,
|
|
10258
|
+
repo,
|
|
10259
|
+
message,
|
|
10260
|
+
tree: treeSha,
|
|
10261
|
+
parents
|
|
10262
|
+
});
|
|
10263
|
+
const signedSha = signedCommit.sha;
|
|
10264
|
+
try {
|
|
10265
|
+
await upsertBranchRef({ octokit, owner, repo, branch, sha: signedSha, force });
|
|
10266
|
+
logger.debug(`Updated refs/heads/${branch} to signed commit ${signedSha}`);
|
|
10267
|
+
await syncLocalToSignedCommit({ repository, branch, signedSha });
|
|
10268
|
+
return signedSha;
|
|
10269
|
+
} catch (err) {
|
|
10270
|
+
if (!isNonFastForwardError(err) || force || attempt >= MAX_CONCURRENT_PUSH_RETRIES - 1) {
|
|
10271
|
+
throw err;
|
|
10272
|
+
}
|
|
10273
|
+
if (rebaseOnConflict) {
|
|
10274
|
+
logger.warn(
|
|
10275
|
+
`Non-fast-forward on refs/heads/${branch} (attempt ${attempt + 1}/${MAX_CONCURRENT_PUSH_RETRIES}); rebasing locally and retrying.`
|
|
10276
|
+
);
|
|
10277
|
+
await repository.pullWithRebase(branch);
|
|
10278
|
+
[localHeadSha, treeSha, message, parents] = await Promise.all([
|
|
10279
|
+
repository.getHeadSha(),
|
|
10280
|
+
repository.getHeadTreeHash(),
|
|
10281
|
+
repository.getHeadCommitMessage(),
|
|
10282
|
+
repository.getHeadParents()
|
|
10283
|
+
]);
|
|
10284
|
+
await repository.pushObjectToRef(localHeadSha, tempRef, { force: true });
|
|
10285
|
+
} else {
|
|
10286
|
+
throw err;
|
|
10287
|
+
}
|
|
10288
|
+
}
|
|
10289
|
+
}
|
|
10290
|
+
throw new Error(
|
|
10291
|
+
`Failed to push signed commit to refs/heads/${branch} after ${MAX_CONCURRENT_PUSH_RETRIES} attempts`
|
|
10292
|
+
);
|
|
10293
|
+
} finally {
|
|
10294
|
+
if (tempRefPushed) {
|
|
10295
|
+
try {
|
|
10296
|
+
await octokit.git.deleteRef({ owner, repo, ref: tempRef.replace(/^refs\//, "") });
|
|
10297
|
+
} catch (err) {
|
|
10298
|
+
logger.debug(`Failed to delete temp ref ${tempRef}: ${extractErrorMessage(err)}`);
|
|
10299
|
+
}
|
|
10300
|
+
}
|
|
10301
|
+
}
|
|
10302
|
+
}
|
|
10303
|
+
async function upsertBranchRef({
|
|
10304
|
+
octokit,
|
|
10305
|
+
owner,
|
|
10306
|
+
repo,
|
|
10307
|
+
branch,
|
|
10308
|
+
sha,
|
|
10309
|
+
force
|
|
10310
|
+
}) {
|
|
10311
|
+
try {
|
|
10312
|
+
await octokit.git.updateRef({
|
|
10313
|
+
owner,
|
|
10314
|
+
repo,
|
|
10315
|
+
ref: `heads/${branch}`,
|
|
10316
|
+
sha,
|
|
10317
|
+
force
|
|
10318
|
+
});
|
|
10319
|
+
} catch (err) {
|
|
10320
|
+
if (isNotFoundError(err)) {
|
|
10321
|
+
await octokit.git.createRef({
|
|
10322
|
+
owner,
|
|
10323
|
+
repo,
|
|
10324
|
+
ref: `refs/heads/${branch}`,
|
|
10325
|
+
sha
|
|
10326
|
+
});
|
|
10327
|
+
return;
|
|
10328
|
+
}
|
|
10329
|
+
throw err;
|
|
10330
|
+
}
|
|
10331
|
+
}
|
|
10332
|
+
async function syncLocalToSignedCommit({
|
|
10333
|
+
repository,
|
|
10334
|
+
branch,
|
|
10335
|
+
signedSha
|
|
10336
|
+
}) {
|
|
10337
|
+
await repository.fetch(["origin", branch]);
|
|
10338
|
+
await repository.resetHardToSha(signedSha);
|
|
10339
|
+
}
|
|
10340
|
+
function hasNumericStatus(err) {
|
|
10341
|
+
return typeof err === "object" && err != null && "status" in err && typeof err.status === "number";
|
|
10342
|
+
}
|
|
10343
|
+
function isNotFoundError(err) {
|
|
10344
|
+
return hasNumericStatus(err) && err.status === 404;
|
|
10345
|
+
}
|
|
10346
|
+
function isNonFastForwardError(err) {
|
|
10347
|
+
if (!hasNumericStatus(err) || err.status !== 422) {
|
|
10348
|
+
return false;
|
|
10349
|
+
}
|
|
10350
|
+
const message = extractErrorMessage(err).toLowerCase();
|
|
10351
|
+
return message.includes("not a fast forward") || message.includes("not a fast-forward") || message.includes("update is not a fast forward");
|
|
10352
|
+
}
|
|
10353
|
+
|
|
10160
10354
|
// src/pipeline/replay-summary.ts
|
|
10161
10355
|
function plural(n, word) {
|
|
10162
10356
|
return `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
@@ -10288,16 +10482,6 @@ function formatReplayPrBody(result, options) {
|
|
|
10288
10482
|
return parts.join("\n");
|
|
10289
10483
|
}
|
|
10290
10484
|
|
|
10291
|
-
// src/pipeline/steps/BaseStep.ts
|
|
10292
|
-
var BaseStep = class {
|
|
10293
|
-
constructor(outputDir, logger) {
|
|
10294
|
-
this.outputDir = outputDir;
|
|
10295
|
-
this.logger = logger;
|
|
10296
|
-
}
|
|
10297
|
-
outputDir;
|
|
10298
|
-
logger;
|
|
10299
|
-
};
|
|
10300
|
-
|
|
10301
10485
|
// src/pipeline/steps/GithubStep.ts
|
|
10302
10486
|
var GithubStep = class extends BaseStep {
|
|
10303
10487
|
constructor(outputDir, logger, config) {
|
|
@@ -10407,7 +10591,7 @@ var GithubStep = class extends BaseStep {
|
|
|
10407
10591
|
await repository.commitAllChanges(finalCommitMessage2);
|
|
10408
10592
|
this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
|
|
10409
10593
|
}
|
|
10410
|
-
if (this.config
|
|
10594
|
+
if (shouldCheckNoDiff(this.config)) {
|
|
10411
10595
|
const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
|
|
10412
10596
|
if (noDiff) {
|
|
10413
10597
|
this.logger.info("No changes detected after generation \u2014 skipping PR creation");
|
|
@@ -10420,11 +10604,15 @@ var GithubStep = class extends BaseStep {
|
|
|
10420
10604
|
updatedExistingPr: isUpdatingExistingPR
|
|
10421
10605
|
};
|
|
10422
10606
|
if (!this.config.previewMode) {
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10607
|
+
await pushSignedCommit({
|
|
10608
|
+
repository,
|
|
10609
|
+
octokit,
|
|
10610
|
+
owner,
|
|
10611
|
+
repo,
|
|
10612
|
+
branch: prBranch,
|
|
10613
|
+
force: isUpdatingExistingPR,
|
|
10614
|
+
logger: this.logger
|
|
10615
|
+
});
|
|
10428
10616
|
const pushedBranch = await repository.getCurrentBranch();
|
|
10429
10617
|
result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`;
|
|
10430
10618
|
this.logger.info(`Pushed branch: ${result.branchUrl}`);
|
|
@@ -10525,7 +10713,7 @@ var GithubStep = class extends BaseStep {
|
|
|
10525
10713
|
const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
|
|
10526
10714
|
await repository.commitAllChanges(finalCommitMessage);
|
|
10527
10715
|
this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
|
|
10528
|
-
if (this.config
|
|
10716
|
+
if (shouldCheckNoDiff(this.config)) {
|
|
10529
10717
|
const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
|
|
10530
10718
|
if (noDiff) {
|
|
10531
10719
|
this.logger.info("No changes detected after generation \u2014 skipping push");
|
|
@@ -10537,7 +10725,18 @@ var GithubStep = class extends BaseStep {
|
|
|
10537
10725
|
success: true
|
|
10538
10726
|
};
|
|
10539
10727
|
if (!this.config.previewMode) {
|
|
10540
|
-
|
|
10728
|
+
const octokit = new import_rest2.Octokit({ auth: this.config.token });
|
|
10729
|
+
const { owner, repo } = parseRepository(this.config.uri);
|
|
10730
|
+
await pushSignedCommit({
|
|
10731
|
+
repository,
|
|
10732
|
+
octokit,
|
|
10733
|
+
owner,
|
|
10734
|
+
repo,
|
|
10735
|
+
branch: baseBranch,
|
|
10736
|
+
force: false,
|
|
10737
|
+
rebaseOnConflict: true,
|
|
10738
|
+
logger: this.logger
|
|
10739
|
+
});
|
|
10541
10740
|
const pushedBranch = await repository.getCurrentBranch();
|
|
10542
10741
|
result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`;
|
|
10543
10742
|
this.logger.info(`Pushed branch: ${result.branchUrl}`);
|
|
@@ -10596,6 +10795,9 @@ _Fern Run ID: \`${config.runId}\`_`;
|
|
|
10596
10795
|
function shouldEnableAutomerge(config) {
|
|
10597
10796
|
return config.automationMode === true && config.autoMerge === true && config.hasBreakingChanges !== true;
|
|
10598
10797
|
}
|
|
10798
|
+
function shouldCheckNoDiff(config) {
|
|
10799
|
+
return config.skipIfNoDiff === true;
|
|
10800
|
+
}
|
|
10599
10801
|
function shouldPushGenerationBaseTag(replayResult) {
|
|
10600
10802
|
return (replayResult?.patchesWithConflicts ?? 0) > 0;
|
|
10601
10803
|
}
|
|
@@ -10929,6 +11131,9 @@ var PostGenerationPipeline = class {
|
|
|
10929
11131
|
);
|
|
10930
11132
|
config.replay.enabled = false;
|
|
10931
11133
|
}
|
|
11134
|
+
if (config.autoVersion?.enabled) {
|
|
11135
|
+
this.steps.push(new AutoVersionStep(config.outputDir, this.logger, config.autoVersion));
|
|
11136
|
+
}
|
|
10932
11137
|
if (config.replay?.enabled) {
|
|
10933
11138
|
this.steps.push(
|
|
10934
11139
|
new ReplayStep(
|
|
@@ -10969,6 +11174,9 @@ var PostGenerationPipeline = class {
|
|
|
10969
11174
|
if (step.name === "replay") {
|
|
10970
11175
|
result.steps.replay = stepResult;
|
|
10971
11176
|
pipelineContext.previousStepResults.replay = stepResult;
|
|
11177
|
+
} else if (step.name === "autoVersion") {
|
|
11178
|
+
result.steps.autoVersion = stepResult;
|
|
11179
|
+
pipelineContext.previousStepResults.autoVersion = stepResult;
|
|
10972
11180
|
} else if (step.name === "fernignore") {
|
|
10973
11181
|
result.steps.fernignore = stepResult;
|
|
10974
11182
|
} else if (step.name === "github") {
|
|
@@ -11156,9 +11364,66 @@ var import_replay5 = require("@fern-api/replay");
|
|
|
11156
11364
|
function replayStatus(params) {
|
|
11157
11365
|
return (0, import_replay5.status)(params.outputDir);
|
|
11158
11366
|
}
|
|
11367
|
+
|
|
11368
|
+
// src/replay/replay-submit-init.ts
|
|
11369
|
+
async function submitReplayInit(params) {
|
|
11370
|
+
const { fiddleOrigin, fernToken, githubRepo, initResult } = params;
|
|
11371
|
+
const { owner, repo } = parseOwnerRepo(githubRepo);
|
|
11372
|
+
const response = await fetch(`${fiddleOrigin}/api/remote-gen/replay/init`, {
|
|
11373
|
+
method: "POST",
|
|
11374
|
+
headers: {
|
|
11375
|
+
"Content-Type": "application/json",
|
|
11376
|
+
Authorization: `Bearer ${fernToken}`
|
|
11377
|
+
},
|
|
11378
|
+
body: JSON.stringify({
|
|
11379
|
+
owner,
|
|
11380
|
+
repo,
|
|
11381
|
+
lockfileContents: initResult.lockfileContent,
|
|
11382
|
+
fernignoreEntries: initResult.fernignoreEntries,
|
|
11383
|
+
prBody: initResult.prBody
|
|
11384
|
+
})
|
|
11385
|
+
});
|
|
11386
|
+
if (!response.ok) {
|
|
11387
|
+
if (response.status === 404) {
|
|
11388
|
+
throw new ReplaySubmitError(
|
|
11389
|
+
"The Fern GitHub App is not installed on this repository. Install it at https://github.com/apps/fern-api to enable server-side PR creation.",
|
|
11390
|
+
response.status
|
|
11391
|
+
);
|
|
11392
|
+
}
|
|
11393
|
+
const body = await response.text();
|
|
11394
|
+
throw new ReplaySubmitError(`Failed to create PR via Fern: ${body}`, response.status);
|
|
11395
|
+
}
|
|
11396
|
+
const data = await response.json();
|
|
11397
|
+
if (!isSubmitResponse(data)) {
|
|
11398
|
+
throw new ReplaySubmitError("Unexpected response from Fern: missing prUrl field.");
|
|
11399
|
+
}
|
|
11400
|
+
return { prUrl: data.prUrl };
|
|
11401
|
+
}
|
|
11402
|
+
function parseOwnerRepo(githubRepo) {
|
|
11403
|
+
const cleaned = githubRepo.replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/^github\.com\//, "");
|
|
11404
|
+
const parts = cleaned.split("/");
|
|
11405
|
+
const owner = parts[parts.length - 2];
|
|
11406
|
+
const repo = parts[parts.length - 1];
|
|
11407
|
+
if (!owner || !repo) {
|
|
11408
|
+
throw new Error(`Could not parse owner/repo from: ${githubRepo}`);
|
|
11409
|
+
}
|
|
11410
|
+
return { owner, repo };
|
|
11411
|
+
}
|
|
11412
|
+
var ReplaySubmitError = class extends Error {
|
|
11413
|
+
statusCode;
|
|
11414
|
+
constructor(message, statusCode) {
|
|
11415
|
+
super(message);
|
|
11416
|
+
this.name = "ReplaySubmitError";
|
|
11417
|
+
this.statusCode = statusCode;
|
|
11418
|
+
}
|
|
11419
|
+
};
|
|
11420
|
+
function isSubmitResponse(data) {
|
|
11421
|
+
return typeof data === "object" && data != null && "prUrl" in data && typeof data.prUrl === "string";
|
|
11422
|
+
}
|
|
11159
11423
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11160
11424
|
0 && (module.exports = {
|
|
11161
11425
|
PostGenerationPipeline,
|
|
11426
|
+
ReplaySubmitError,
|
|
11162
11427
|
consolePipelineLogger,
|
|
11163
11428
|
formatBootstrapSummary,
|
|
11164
11429
|
formatReplayPrBody,
|
|
@@ -11170,9 +11435,11 @@ function replayStatus(params) {
|
|
|
11170
11435
|
githubPush,
|
|
11171
11436
|
githubRelease,
|
|
11172
11437
|
logReplaySummary,
|
|
11438
|
+
parseOwnerRepo,
|
|
11173
11439
|
replayForget,
|
|
11174
11440
|
replayInit,
|
|
11175
11441
|
replayResolve,
|
|
11176
11442
|
replayRun,
|
|
11177
|
-
replayStatus
|
|
11443
|
+
replayStatus,
|
|
11444
|
+
submitReplayInit
|
|
11178
11445
|
});
|
package/dist/cli.js
CHANGED
|
@@ -25629,6 +25629,46 @@ var ClonedRepository = class _ClonedRepository {
|
|
|
25629
25629
|
const result = await this.git.raw(["rev-parse", "HEAD^{tree}"]);
|
|
25630
25630
|
return result.trim();
|
|
25631
25631
|
}
|
|
25632
|
+
async getHeadCommitMessage() {
|
|
25633
|
+
await this.git.cwd(this.clonePath);
|
|
25634
|
+
const result = await this.git.raw(["log", "-1", "--format=%B", "HEAD"]);
|
|
25635
|
+
return result.replace(/\n$/, "");
|
|
25636
|
+
}
|
|
25637
|
+
async getHeadParents() {
|
|
25638
|
+
await this.git.cwd(this.clonePath);
|
|
25639
|
+
const result = await this.git.raw(["rev-list", "--parents", "-n", "1", "HEAD"]);
|
|
25640
|
+
const parts = result.trim().split(/\s+/);
|
|
25641
|
+
return parts.slice(1);
|
|
25642
|
+
}
|
|
25643
|
+
/**
|
|
25644
|
+
* Pushes a specific object (commit SHA) to a remote ref.
|
|
25645
|
+
* Used to upload tree and blob objects to the remote without moving a branch.
|
|
25646
|
+
*
|
|
25647
|
+
* Pass `force: true` when overwriting a ref that may already point to an
|
|
25648
|
+
* unrelated commit (e.g. retrying a temp ref after a local rebase rewrites
|
|
25649
|
+
* history so the new SHA is not a descendant of the previous one).
|
|
25650
|
+
*/
|
|
25651
|
+
async pushObjectToRef(localSha, remoteRef, { force = false } = {}) {
|
|
25652
|
+
await this.git.cwd(this.clonePath);
|
|
25653
|
+
const spec = force ? `+${localSha}:${remoteRef}` : `${localSha}:${remoteRef}`;
|
|
25654
|
+
await this.git.raw(["push", "origin", spec]);
|
|
25655
|
+
}
|
|
25656
|
+
/**
|
|
25657
|
+
* Resets the current branch HEAD to the given SHA. After fetching a newly-created remote
|
|
25658
|
+
* signed commit, use this to align the local branch so subsequent operations (tag push,
|
|
25659
|
+
* getHeadSha(), etc.) see the signed SHA.
|
|
25660
|
+
*/
|
|
25661
|
+
async resetHardToSha(sha) {
|
|
25662
|
+
await this.git.cwd(this.clonePath);
|
|
25663
|
+
await this.git.raw(["reset", "--hard", sha]);
|
|
25664
|
+
}
|
|
25665
|
+
/**
|
|
25666
|
+
* Rebases the current branch onto the given remote branch (`git pull --rebase origin <branch>`).
|
|
25667
|
+
*/
|
|
25668
|
+
async pullWithRebase(branch) {
|
|
25669
|
+
await this.git.cwd(this.clonePath);
|
|
25670
|
+
await this.git.raw(["pull", "origin", branch, "--rebase"]);
|
|
25671
|
+
}
|
|
25632
25672
|
async getCommitTreeHash(commitSha) {
|
|
25633
25673
|
await this.git.cwd(this.clonePath);
|
|
25634
25674
|
const result = await this.git.raw(["rev-parse", `${commitSha}^{tree}`]);
|
|
@@ -33456,6 +33496,35 @@ var FERN_BOT_NAME = "fern-api";
|
|
|
33456
33496
|
var FERN_BOT_EMAIL = "115122769+fern-api[bot]@users.noreply.github.com";
|
|
33457
33497
|
var FERN_BOT_LOGIN = "fern-api[bot]";
|
|
33458
33498
|
|
|
33499
|
+
// src/pipeline/steps/BaseStep.ts
|
|
33500
|
+
var BaseStep = class {
|
|
33501
|
+
constructor(outputDir, logger) {
|
|
33502
|
+
this.outputDir = outputDir;
|
|
33503
|
+
this.logger = logger;
|
|
33504
|
+
}
|
|
33505
|
+
outputDir;
|
|
33506
|
+
logger;
|
|
33507
|
+
};
|
|
33508
|
+
|
|
33509
|
+
// src/pipeline/steps/AutoVersionStep.ts
|
|
33510
|
+
var AutoVersionStep = class extends BaseStep {
|
|
33511
|
+
constructor(outputDir, logger, config) {
|
|
33512
|
+
super(outputDir, logger);
|
|
33513
|
+
this.config = config;
|
|
33514
|
+
}
|
|
33515
|
+
config;
|
|
33516
|
+
name = "autoVersion";
|
|
33517
|
+
async execute(_context) {
|
|
33518
|
+
this.logger.info(
|
|
33519
|
+
`AutoVersionStep: no-op scaffold for language=${this.config.language} (logic lands in FER-9980)`
|
|
33520
|
+
);
|
|
33521
|
+
return {
|
|
33522
|
+
executed: true,
|
|
33523
|
+
success: true
|
|
33524
|
+
};
|
|
33525
|
+
}
|
|
33526
|
+
};
|
|
33527
|
+
|
|
33459
33528
|
// src/pipeline/steps/GithubStep.ts
|
|
33460
33529
|
var import_promises3 = require("fs/promises");
|
|
33461
33530
|
var import_path7 = require("path");
|
|
@@ -33588,6 +33657,128 @@ ${prBody}`;
|
|
|
33588
33657
|
return { prTitle, prBody };
|
|
33589
33658
|
}
|
|
33590
33659
|
|
|
33660
|
+
// src/pipeline/github/pushSignedCommit.ts
|
|
33661
|
+
var MAX_CONCURRENT_PUSH_RETRIES = 3;
|
|
33662
|
+
async function pushSignedCommit({
|
|
33663
|
+
repository,
|
|
33664
|
+
octokit,
|
|
33665
|
+
owner,
|
|
33666
|
+
repo,
|
|
33667
|
+
branch,
|
|
33668
|
+
force,
|
|
33669
|
+
rebaseOnConflict = false,
|
|
33670
|
+
logger
|
|
33671
|
+
}) {
|
|
33672
|
+
const tempRef = `refs/temp/fern-${Date.now()}`;
|
|
33673
|
+
let tempRefPushed = false;
|
|
33674
|
+
try {
|
|
33675
|
+
let [localHeadSha, treeSha, message, parents] = await Promise.all([
|
|
33676
|
+
repository.getHeadSha(),
|
|
33677
|
+
repository.getHeadTreeHash(),
|
|
33678
|
+
repository.getHeadCommitMessage(),
|
|
33679
|
+
repository.getHeadParents()
|
|
33680
|
+
]);
|
|
33681
|
+
await repository.pushObjectToRef(localHeadSha, tempRef);
|
|
33682
|
+
tempRefPushed = true;
|
|
33683
|
+
for (let attempt2 = 0; attempt2 < MAX_CONCURRENT_PUSH_RETRIES; attempt2++) {
|
|
33684
|
+
const { data: signedCommit } = await octokit.git.createCommit({
|
|
33685
|
+
owner,
|
|
33686
|
+
repo,
|
|
33687
|
+
message,
|
|
33688
|
+
tree: treeSha,
|
|
33689
|
+
parents
|
|
33690
|
+
});
|
|
33691
|
+
const signedSha = signedCommit.sha;
|
|
33692
|
+
try {
|
|
33693
|
+
await upsertBranchRef({ octokit, owner, repo, branch, sha: signedSha, force });
|
|
33694
|
+
logger.debug(`Updated refs/heads/${branch} to signed commit ${signedSha}`);
|
|
33695
|
+
await syncLocalToSignedCommit({ repository, branch, signedSha });
|
|
33696
|
+
return signedSha;
|
|
33697
|
+
} catch (err) {
|
|
33698
|
+
if (!isNonFastForwardError(err) || force || attempt2 >= MAX_CONCURRENT_PUSH_RETRIES - 1) {
|
|
33699
|
+
throw err;
|
|
33700
|
+
}
|
|
33701
|
+
if (rebaseOnConflict) {
|
|
33702
|
+
logger.warn(
|
|
33703
|
+
`Non-fast-forward on refs/heads/${branch} (attempt ${attempt2 + 1}/${MAX_CONCURRENT_PUSH_RETRIES}); rebasing locally and retrying.`
|
|
33704
|
+
);
|
|
33705
|
+
await repository.pullWithRebase(branch);
|
|
33706
|
+
[localHeadSha, treeSha, message, parents] = await Promise.all([
|
|
33707
|
+
repository.getHeadSha(),
|
|
33708
|
+
repository.getHeadTreeHash(),
|
|
33709
|
+
repository.getHeadCommitMessage(),
|
|
33710
|
+
repository.getHeadParents()
|
|
33711
|
+
]);
|
|
33712
|
+
await repository.pushObjectToRef(localHeadSha, tempRef, { force: true });
|
|
33713
|
+
} else {
|
|
33714
|
+
throw err;
|
|
33715
|
+
}
|
|
33716
|
+
}
|
|
33717
|
+
}
|
|
33718
|
+
throw new Error(
|
|
33719
|
+
`Failed to push signed commit to refs/heads/${branch} after ${MAX_CONCURRENT_PUSH_RETRIES} attempts`
|
|
33720
|
+
);
|
|
33721
|
+
} finally {
|
|
33722
|
+
if (tempRefPushed) {
|
|
33723
|
+
try {
|
|
33724
|
+
await octokit.git.deleteRef({ owner, repo, ref: tempRef.replace(/^refs\//, "") });
|
|
33725
|
+
} catch (err) {
|
|
33726
|
+
logger.debug(`Failed to delete temp ref ${tempRef}: ${extractErrorMessage(err)}`);
|
|
33727
|
+
}
|
|
33728
|
+
}
|
|
33729
|
+
}
|
|
33730
|
+
}
|
|
33731
|
+
async function upsertBranchRef({
|
|
33732
|
+
octokit,
|
|
33733
|
+
owner,
|
|
33734
|
+
repo,
|
|
33735
|
+
branch,
|
|
33736
|
+
sha,
|
|
33737
|
+
force
|
|
33738
|
+
}) {
|
|
33739
|
+
try {
|
|
33740
|
+
await octokit.git.updateRef({
|
|
33741
|
+
owner,
|
|
33742
|
+
repo,
|
|
33743
|
+
ref: `heads/${branch}`,
|
|
33744
|
+
sha,
|
|
33745
|
+
force
|
|
33746
|
+
});
|
|
33747
|
+
} catch (err) {
|
|
33748
|
+
if (isNotFoundError(err)) {
|
|
33749
|
+
await octokit.git.createRef({
|
|
33750
|
+
owner,
|
|
33751
|
+
repo,
|
|
33752
|
+
ref: `refs/heads/${branch}`,
|
|
33753
|
+
sha
|
|
33754
|
+
});
|
|
33755
|
+
return;
|
|
33756
|
+
}
|
|
33757
|
+
throw err;
|
|
33758
|
+
}
|
|
33759
|
+
}
|
|
33760
|
+
async function syncLocalToSignedCommit({
|
|
33761
|
+
repository,
|
|
33762
|
+
branch,
|
|
33763
|
+
signedSha
|
|
33764
|
+
}) {
|
|
33765
|
+
await repository.fetch(["origin", branch]);
|
|
33766
|
+
await repository.resetHardToSha(signedSha);
|
|
33767
|
+
}
|
|
33768
|
+
function hasNumericStatus(err) {
|
|
33769
|
+
return typeof err === "object" && err != null && "status" in err && typeof err.status === "number";
|
|
33770
|
+
}
|
|
33771
|
+
function isNotFoundError(err) {
|
|
33772
|
+
return hasNumericStatus(err) && err.status === 404;
|
|
33773
|
+
}
|
|
33774
|
+
function isNonFastForwardError(err) {
|
|
33775
|
+
if (!hasNumericStatus(err) || err.status !== 422) {
|
|
33776
|
+
return false;
|
|
33777
|
+
}
|
|
33778
|
+
const message = extractErrorMessage(err).toLowerCase();
|
|
33779
|
+
return message.includes("not a fast forward") || message.includes("not a fast-forward") || message.includes("update is not a fast forward");
|
|
33780
|
+
}
|
|
33781
|
+
|
|
33591
33782
|
// src/pipeline/replay-summary.ts
|
|
33592
33783
|
function plural(n, word) {
|
|
33593
33784
|
return `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
@@ -33683,16 +33874,6 @@ function formatReplayPrBody(result, options) {
|
|
|
33683
33874
|
return parts.join("\n");
|
|
33684
33875
|
}
|
|
33685
33876
|
|
|
33686
|
-
// src/pipeline/steps/BaseStep.ts
|
|
33687
|
-
var BaseStep = class {
|
|
33688
|
-
constructor(outputDir, logger) {
|
|
33689
|
-
this.outputDir = outputDir;
|
|
33690
|
-
this.logger = logger;
|
|
33691
|
-
}
|
|
33692
|
-
outputDir;
|
|
33693
|
-
logger;
|
|
33694
|
-
};
|
|
33695
|
-
|
|
33696
33877
|
// src/pipeline/steps/GithubStep.ts
|
|
33697
33878
|
var GithubStep = class extends BaseStep {
|
|
33698
33879
|
constructor(outputDir, logger, config) {
|
|
@@ -33802,7 +33983,7 @@ var GithubStep = class extends BaseStep {
|
|
|
33802
33983
|
await repository.commitAllChanges(finalCommitMessage2);
|
|
33803
33984
|
this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
|
|
33804
33985
|
}
|
|
33805
|
-
if (this.config
|
|
33986
|
+
if (shouldCheckNoDiff(this.config)) {
|
|
33806
33987
|
const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
|
|
33807
33988
|
if (noDiff) {
|
|
33808
33989
|
this.logger.info("No changes detected after generation \u2014 skipping PR creation");
|
|
@@ -33815,11 +33996,15 @@ var GithubStep = class extends BaseStep {
|
|
|
33815
33996
|
updatedExistingPr: isUpdatingExistingPR
|
|
33816
33997
|
};
|
|
33817
33998
|
if (!this.config.previewMode) {
|
|
33818
|
-
|
|
33819
|
-
|
|
33820
|
-
|
|
33821
|
-
|
|
33822
|
-
|
|
33999
|
+
await pushSignedCommit({
|
|
34000
|
+
repository,
|
|
34001
|
+
octokit,
|
|
34002
|
+
owner,
|
|
34003
|
+
repo,
|
|
34004
|
+
branch: prBranch,
|
|
34005
|
+
force: isUpdatingExistingPR,
|
|
34006
|
+
logger: this.logger
|
|
34007
|
+
});
|
|
33823
34008
|
const pushedBranch = await repository.getCurrentBranch();
|
|
33824
34009
|
result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`;
|
|
33825
34010
|
this.logger.info(`Pushed branch: ${result.branchUrl}`);
|
|
@@ -33920,7 +34105,7 @@ var GithubStep = class extends BaseStep {
|
|
|
33920
34105
|
const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
|
|
33921
34106
|
await repository.commitAllChanges(finalCommitMessage);
|
|
33922
34107
|
this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`);
|
|
33923
|
-
if (this.config
|
|
34108
|
+
if (shouldCheckNoDiff(this.config)) {
|
|
33924
34109
|
const noDiff = await repository.treeHashEquals(`origin/${baseBranch}`);
|
|
33925
34110
|
if (noDiff) {
|
|
33926
34111
|
this.logger.info("No changes detected after generation \u2014 skipping push");
|
|
@@ -33932,7 +34117,18 @@ var GithubStep = class extends BaseStep {
|
|
|
33932
34117
|
success: true
|
|
33933
34118
|
};
|
|
33934
34119
|
if (!this.config.previewMode) {
|
|
33935
|
-
|
|
34120
|
+
const octokit = new Octokit2({ auth: this.config.token });
|
|
34121
|
+
const { owner, repo } = parseRepository(this.config.uri);
|
|
34122
|
+
await pushSignedCommit({
|
|
34123
|
+
repository,
|
|
34124
|
+
octokit,
|
|
34125
|
+
owner,
|
|
34126
|
+
repo,
|
|
34127
|
+
branch: baseBranch,
|
|
34128
|
+
force: false,
|
|
34129
|
+
rebaseOnConflict: true,
|
|
34130
|
+
logger: this.logger
|
|
34131
|
+
});
|
|
33936
34132
|
const pushedBranch = await repository.getCurrentBranch();
|
|
33937
34133
|
result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`;
|
|
33938
34134
|
this.logger.info(`Pushed branch: ${result.branchUrl}`);
|
|
@@ -33991,6 +34187,9 @@ _Fern Run ID: \`${config.runId}\`_`;
|
|
|
33991
34187
|
function shouldEnableAutomerge(config) {
|
|
33992
34188
|
return config.automationMode === true && config.autoMerge === true && config.hasBreakingChanges !== true;
|
|
33993
34189
|
}
|
|
34190
|
+
function shouldCheckNoDiff(config) {
|
|
34191
|
+
return config.skipIfNoDiff === true;
|
|
34192
|
+
}
|
|
33994
34193
|
function shouldPushGenerationBaseTag(replayResult) {
|
|
33995
34194
|
return (replayResult?.patchesWithConflicts ?? 0) > 0;
|
|
33996
34195
|
}
|
|
@@ -34324,6 +34523,9 @@ var PostGenerationPipeline = class {
|
|
|
34324
34523
|
);
|
|
34325
34524
|
config.replay.enabled = false;
|
|
34326
34525
|
}
|
|
34526
|
+
if (config.autoVersion?.enabled) {
|
|
34527
|
+
this.steps.push(new AutoVersionStep(config.outputDir, this.logger, config.autoVersion));
|
|
34528
|
+
}
|
|
34327
34529
|
if (config.replay?.enabled) {
|
|
34328
34530
|
this.steps.push(
|
|
34329
34531
|
new ReplayStep(
|
|
@@ -34364,6 +34566,9 @@ var PostGenerationPipeline = class {
|
|
|
34364
34566
|
if (step.name === "replay") {
|
|
34365
34567
|
result.steps.replay = stepResult;
|
|
34366
34568
|
pipelineContext.previousStepResults.replay = stepResult;
|
|
34569
|
+
} else if (step.name === "autoVersion") {
|
|
34570
|
+
result.steps.autoVersion = stepResult;
|
|
34571
|
+
pipelineContext.previousStepResults.autoVersion = stepResult;
|
|
34367
34572
|
} else if (step.name === "fernignore") {
|
|
34368
34573
|
result.steps.fernignore = stepResult;
|
|
34369
34574
|
} else if (step.name === "github") {
|