Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion fern/apis/generators-yml/definition/group.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,25 @@ types:
mode: optional<GithubSelfhostedMode>
branch: optional<string>
license: optional<license.GithubLicenseSchema>
pr-state:
type: optional<GithubPullRequestState>
docs: The state of the pull request when created (draft or ready). Only applies when mode is pull-request.

GithubSelfhostedMode:
enum:
- name: pullRequest
value: pull-request
- push

GithubPullRequestState:
docs: |
The state of the pull request when created.
- draft: Create the PR as a draft
- ready: Create the PR as ready for review (default)
enum:
- draft
- ready

GeneratorSnippetsSchema:
properties:
path:
Expand Down Expand Up @@ -127,7 +139,9 @@ types:
license: optional<license.GithubLicenseSchema>
mode: literal<"pull-request">
reviewers: optional<reviewers.ReviewersSchema>
# Add properties for pull request configuration
pr-state:
type: optional<GithubPullRequestState>
docs: The state of the pull request when created (draft or ready). Defaults to ready.

GithubPushSchema:
properties:
Expand Down
27 changes: 27 additions & 0 deletions generators-yml.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3270,6 +3270,13 @@
}
]
},
"group.GithubPullRequestState": {
"type": "string",
"enum": [
"draft",
"ready"
]
},
"group.GithubSelfhostedSchema": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -3308,6 +3315,16 @@
"type": "null"
}
]
},
"pr-state": {
"oneOf": [
{
"$ref": "#/definitions/group.GithubPullRequestState"
},
{
"type": "null"
}
]
}
},
"required": [
Expand Down Expand Up @@ -3437,6 +3454,16 @@
"type": "null"
}
]
},
"pr-state": {
"oneOf": [
{
"$ref": "#/definitions/group.GithubPullRequestState"
},
{
"type": "null"
}
]
}
},
"required": [
Expand Down
10 changes: 10 additions & 0 deletions packages/cli/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,11 @@ function addGenerateCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext)
choices: Object.values(GenerationMode),
description: "Defaults to the mode specified in generators.yml"
})
.option("pr-state", {
choices: ["draft", "ready"] as const,
description:
"The state of the pull request when created (draft or ready). Only applies when --mode pull-request is used."
})
.option("version", {
type: "string",
description: "The version for the generated packages"
Expand Down Expand Up @@ -645,6 +650,9 @@ function addGenerateCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext)
"The --fernignore flag is not supported with local generation (--local or --runner). It can only be used with remote generation."
);
}
if (argv.prState != null && argv.mode !== GenerationMode.PullRequest) {
return cliContext.failWithoutThrowing("The --pr-state flag can only be used with --mode pull-request.");
}
if (argv.api != null) {
return await generateAPIWorkspaces({
project: await loadProjectAndRegisterWorkspacesWithContext(cliContext, {
Expand All @@ -659,6 +667,7 @@ function addGenerateCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext)
useLocalDocker: argv.local || argv.runner != null,
preview: argv.preview,
mode: argv.mode,
prState: argv.prState,
force: argv.force,
runner: argv.runner as ContainerRunner,
inspect: false,
Expand Down Expand Up @@ -706,6 +715,7 @@ function addGenerateCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext)
useLocalDocker: argv.local,
preview: argv.preview,
mode: argv.mode,
prState: argv.prState,
force: argv.force,
runner: argv.runner as ContainerRunner,
inspect: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { FernFiddle } from "@fern-fern/fiddle-sdk";

import { GROUP_CLI_OPTION } from "../../constants";
import { validateAPIWorkspaceAndLogIssues } from "../validate/validateAPIWorkspaceAndLogIssues";
import { GenerationMode } from "./generateAPIWorkspaces";
import { GenerationMode, PullRequestState } from "./generateAPIWorkspaces";

export async function generateWorkspace({
organization,
Expand All @@ -30,6 +30,7 @@ export async function generateWorkspace({
keepDocker,
absolutePathToPreview,
mode,
prState,
runner,
inspect,
lfsOverride,
Expand All @@ -47,6 +48,7 @@ export async function generateWorkspace({
keepDocker: boolean;
absolutePathToPreview: AbsoluteFilePath | undefined;
mode: GenerationMode | undefined;
prState: PullRequestState | undefined;
runner: ContainerRunner | undefined;
inspect: boolean;
lfsOverride: string | undefined;
Expand Down Expand Up @@ -122,7 +124,8 @@ export async function generateWorkspace({
runner,
absolutePathToPreview,
inspect,
ai
ai,
prState
});
} else if (token != null) {
await runRemoteGenerationForAPIWorkspace({
Expand All @@ -137,6 +140,7 @@ export async function generateWorkspace({
whitelabel: workspace.generatorsConfiguration?.whitelabel,
absolutePathToPreview,
mode,
prState,
fernignorePath
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const GenerationMode = {

export type GenerationMode = Values<typeof GenerationMode>;

export type PullRequestState = "draft" | "ready";

export async function generateAPIWorkspaces({
project,
cliContext,
Expand All @@ -26,6 +28,7 @@ export async function generateAPIWorkspaces({
useLocalDocker,
preview,
mode,
prState,
force,
runner,
inspect,
Expand All @@ -41,6 +44,7 @@ export async function generateAPIWorkspaces({
keepDocker: boolean;
preview: boolean;
mode: GenerationMode | undefined;
prState: PullRequestState | undefined;
force: boolean;
runner: ContainerRunner | undefined;
inspect: boolean;
Expand Down Expand Up @@ -131,6 +135,7 @@ export async function generateAPIWorkspaces({
keepDocker,
absolutePathToPreview,
mode,
prState,
runner,
inspect,
lfsOverride,
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
- changelogEntry:
- summary: |
Add `--pr-state` CLI option for `fern generate --mode pull-request`. This allows users to specify whether the generated pull request should be created as a draft or ready for review. The option accepts `draft` or `ready` values. Additionally, `pr-state` can be configured in `generators.yml` under the `github` configuration for both self-hosted and pull-request modes.
type: feat
irVersion: 63
createdAt: "2025-12-10"
version: 3.6.0

- changelogEntry:
- summary: |
Add support for vendor-specific JSON content types in example validation. Content types like `application/vnd.bc.v1+json` are now accepted as valid JSON content types when validating examples against `literal<"application/json">` types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export interface GithubPullRequestSchema {
license?: FernDefinition.GithubLicenseSchema;
mode: "pull-request";
reviewers?: FernDefinition.ReviewersSchema;
/** The state of the pull request when created (draft or ready). Defaults to ready. */
"pr-state"?: FernDefinition.GithubPullRequestState;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

/**
* The state of the pull request when created.
* - draft: Create the PR as a draft
* - ready: Create the PR as ready for review (default)
*/
export type GithubPullRequestState = "draft" | "ready";
export const GithubPullRequestState = {
Draft: "draft",
Ready: "ready",
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export interface GithubSelfhostedSchema {
mode?: FernDefinition.GithubSelfhostedMode;
branch?: string;
license?: FernDefinition.GithubLicenseSchema;
/** The state of the pull request when created (draft or ready). Only applies when mode is pull-request. */
"pr-state"?: FernDefinition.GithubPullRequestState;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./GeneratorOutputSchema";
export * from "./GithubConfigurationSchema";
export * from "./GithubSelfhostedSchema";
export * from "./GithubSelfhostedMode";
export * from "./GithubPullRequestState";
export * from "./GeneratorSnippetsSchema";
export * from "./GeneratorPublishMetadataSchema";
export * from "./GithubCommitAndReleaseSchema";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as FernDefinition from "../../../../api/index";
import * as core from "../../../../core";
import { GithubLicenseSchema } from "../../license/types/GithubLicenseSchema";
import { ReviewersSchema } from "../../reviewers/types/ReviewersSchema";
import { GithubPullRequestState } from "./GithubPullRequestState";

export const GithubPullRequestSchema: core.serialization.ObjectSchema<
serializers.GithubPullRequestSchema.Raw,
Expand All @@ -17,6 +18,7 @@ export const GithubPullRequestSchema: core.serialization.ObjectSchema<
license: GithubLicenseSchema.optional(),
mode: core.serialization.stringLiteral("pull-request"),
reviewers: ReviewersSchema.optional(),
"pr-state": GithubPullRequestState.optional(),
});

export declare namespace GithubPullRequestSchema {
Expand All @@ -26,5 +28,6 @@ export declare namespace GithubPullRequestSchema {
license?: GithubLicenseSchema.Raw | null;
mode: "pull-request";
reviewers?: ReviewersSchema.Raw | null;
"pr-state"?: GithubPullRequestState.Raw | null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as serializers from "../../../index";
import * as FernDefinition from "../../../../api/index";
import * as core from "../../../../core";

export const GithubPullRequestState: core.serialization.Schema<
serializers.GithubPullRequestState.Raw,
FernDefinition.GithubPullRequestState
> = core.serialization.enum_(["draft", "ready"]);

export declare namespace GithubPullRequestState {
export type Raw = "draft" | "ready";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as FernDefinition from "../../../../api/index";
import * as core from "../../../../core";
import { GithubSelfhostedMode } from "./GithubSelfhostedMode";
import { GithubLicenseSchema } from "../../license/types/GithubLicenseSchema";
import { GithubPullRequestState } from "./GithubPullRequestState";

export const GithubSelfhostedSchema: core.serialization.ObjectSchema<
serializers.GithubSelfhostedSchema.Raw,
Expand All @@ -17,6 +18,7 @@ export const GithubSelfhostedSchema: core.serialization.ObjectSchema<
mode: GithubSelfhostedMode.optional(),
branch: core.serialization.string().optional(),
license: GithubLicenseSchema.optional(),
"pr-state": GithubPullRequestState.optional(),
});

export declare namespace GithubSelfhostedSchema {
Expand All @@ -26,5 +28,6 @@ export declare namespace GithubSelfhostedSchema {
mode?: GithubSelfhostedMode.Raw | null;
branch?: string | null;
license?: GithubLicenseSchema.Raw | null;
"pr-state"?: GithubPullRequestState.Raw | null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./GeneratorOutputSchema";
export * from "./GithubConfigurationSchema";
export * from "./GithubSelfhostedSchema";
export * from "./GithubSelfhostedMode";
export * from "./GithubPullRequestState";
export * from "./GeneratorSnippetsSchema";
export * from "./GeneratorPublishMetadataSchema";
export * from "./GithubCommitAndReleaseSchema";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import tmp from "tmp-promise";
import { writeFilesToDiskAndRunGenerator } from "./runGenerator";
import { isAutoVersion } from "./VersionUtils";

export type PullRequestState = "draft" | "ready";

export async function runLocalGenerationForWorkspace({
token,
projectConfig,
Expand All @@ -35,7 +37,8 @@ export async function runLocalGenerationForWorkspace({
context,
absolutePathToPreview,
runner,
ai
ai,
prState
}: {
token: FernToken | undefined;
projectConfig: fernConfigJson.ProjectConfig;
Expand All @@ -48,6 +51,7 @@ export async function runLocalGenerationForWorkspace({
runner: ContainerRunner | undefined;
inspect: boolean;
ai: generatorsYml.AiServicesSchema | undefined;
prState: PullRequestState | undefined;
}): Promise<void> {
const results = await Promise.all(
generatorGroup.generators.map(async (generatorInvocation) => {
Expand Down Expand Up @@ -251,7 +255,8 @@ export async function runLocalGenerationForWorkspace({
interactiveTaskContext,
selfhostedGithubConfig,
absolutePathToLocalOutput,
autoVersioningCommitMessage
autoVersioningCommitMessage,
prState
);
}
});
Expand Down Expand Up @@ -311,7 +316,8 @@ async function postProcessGithubSelfHosted(
context: TaskContext,
selfhostedGithubConfig: SelhostedGithubConfig,
absolutePathToLocalOutput: AbsoluteFilePath,
commitMessage?: string
commitMessage?: string,
prState?: PullRequestState
): Promise<void> {
try {
context.logger.debug("Starting GitHub self-hosted flow in directory: " + absolutePathToLocalOutput);
Expand Down Expand Up @@ -373,14 +379,20 @@ async function postProcessGithubSelfHosted(
const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage);

try {
// Determine if PR should be created as draft
// CLI --pr-state takes precedence, then generators.yml pr-state, then default to ready (not draft)
const configPrState = selfhostedGithubConfig["pr-state"];
const effectivePrState = prState ?? configPrState ?? "ready";
const isDraft = effectivePrState === "draft";

const { data: pullRequest } = await octokit.pulls.create({
owner,
repo,
title: prTitle,
body: prBody,
head,
base: baseBranch,
draft: false
draft: isDraft
});

context.logger.info(`Created pull request: ${pullRequest.html_url}`);
Expand Down
Loading