Pull request policy plugin NEW
The Bitbucket Cloud pull request policy plugin validates pull request attributes against different rules and creates merge checks for them.
It can be used for compliance and enforce several aspects of your pull request, e.g., title, description and branches conventions.
A policy is made of a list of rules.
Custom rule
A powerful rule that allows you to write your own validation logic.
import {const configure: (config: Config | (() => Config)) => voidconfigure} from "flowie.app"
import {const pullRequestPolicy: OptionsPluginNoDefault<PullRequestPolicyPluginOptions, false>pullRequestPolicy} from "flowie.app/plugins"
function function isSentenceCase(str: string): booleanisSentenceCase(str: stringstr: string) {
return /^[A-Z][^A-Z]*$/.RegExp.test(string: string): booleanReturns a Boolean value that indicates whether or not a pattern exists in a searched string.test(str: stringstr)
}
function configure(config: Config | (() => Config)): voidconfigure({
Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [
function pullRequestPolicy(options: PullRequestPolicyPluginOptions): PluginDef<PullRequestPolicyPluginOptions> (+1 overload)pullRequestPolicy([
{
name: stringThe name of the policy.name: "sentence-case-title",
check: (context: PullRequestContextData) => CustomPullRequestRuleResult | undefinedcheck: ({pullRequest: PullRequestpullRequest}) => ({
title: stringtitle: "Tile must be sentence case",
conclusion: boolean | "FAILED" | "SUCCESS" | "IGNORED""IGNORED" will be skipped, and the result won't be reported in the checks.
'true' is the same as "SUCCESS" and 'false' is the same as "FAILED"conclusion: function isSentenceCase(str: string): booleanisSentenceCase(pullRequest: PullRequestpullRequest.PullRequest.title: stringtitle),
}),
},
]),
],
})
The check is a function that takes the pullRequest as an argument and returns
a check result.
The conclusion can be either "FAILED", "SUCCESS" or "IGNORED". For convenience, true and false can also be use for "SUCCESS" and "FAILED" respectively.
When "IGNORED", the check will not be added to the pull request.
You can choose to report only on failure by returning "IGNORED" when success:
conclusion: isSentenceCase(pullRequest.title) ? "IGNORED" : "FAILED"
Displaying check results
The title is required, and you can also provide additional details using summary. Both properties accept Markdown syntax! 📄✨
Branch rules example:
The policy below contains three rules that ensure the following:
- The source branch name follows the naming convention.
- The correct branching model is used, not merging features directly into ‘main’ for instance.
- The source branch references a Jira ticket.
import {const configure: (config: Config | (() => Config)) => voidconfigure} from "flowie.app"
import {const pullRequestPolicy: OptionsPluginNoDefault<PullRequestPolicyPluginOptions, false>pullRequestPolicy} from "flowie.app/plugins"
function configure(config: Config | (() => Config)): voidconfigure({
Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [
function pullRequestPolicy(options: PullRequestPolicyPluginOptions): PluginDef<PullRequestPolicyPluginOptions> (+1 overload)pullRequestPolicy([
{
name: stringThe name of the policy.name: "branch-naming-conventions",
check: (context: PullRequestContextData) => CustomPullRequestRuleResult | undefinedcheck: ({pullRequest: PullRequestpullRequest}) => {
const const sourceBranch: stringsourceBranch = pullRequest: PullRequestpullRequest.PullRequest.source: PullRequestSourcesource.PullRequestEndpoint.branch: {
readonly name: string;
}
branch.name: stringname
const const isValidSource: booleanisValidSource = /^(hotfix|feature|bugfix|release)\//.RegExp.test(string: string): booleanReturns a Boolean value that indicates whether or not a pattern exists in a searched string.test(
const sourceBranch: stringsourceBranch
)
return {
title: stringtitle: `Invalid source branch naming: *${const sourceBranch: stringsourceBranch}*`,
summary?: string | undefinedsummary:
"See [naming conventions](https://docs.myorg.com/branching-policy#naming-conventions)",
conclusion: boolean | "IGNORED" | "FAILED" | "SUCCESS""IGNORED" will be skipped, and the result won't be reported in the checks.
'true' is the same as "SUCCESS" and 'false' is the same as "FAILED"conclusion: const isValidSource: booleanisValidSource ? "IGNORED" : "FAILED",
}
},
},
{
name: stringThe name of the policy.name: "branch-valid-source-for-target",
check: (context: PullRequestContextData) => CustomPullRequestRuleResult | undefinedcheck: ({pullRequest: PullRequestpullRequest}) => {
const const destinationBranch: stringdestinationBranch = pullRequest: PullRequestpullRequest.PullRequest.destination: PullRequestDestinationdestination.PullRequestEndpoint.branch: {
readonly name: string;
}
branch.name: stringname
const const sourceBranch: stringsourceBranch = pullRequest: PullRequestpullRequest.PullRequest.source: PullRequestSourcesource.PullRequestEndpoint.branch: {
readonly name: string;
}
branch.name: stringname
if (const destinationBranch: stringdestinationBranch === "main") {
const const isValidSource: booleanisValidSource =
const sourceBranch: stringsourceBranch.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.startsWith("hotfix/") || const sourceBranch: stringsourceBranch === "staging"
return {
title: stringtitle: "Source branch for *main* must be *staging* or *hotfix/**",
conclusion: boolean | "IGNORED" | "FAILED" | "SUCCESS""IGNORED" will be skipped, and the result won't be reported in the checks.
'true' is the same as "SUCCESS" and 'false' is the same as "FAILED"conclusion: const isValidSource: booleanisValidSource ? "IGNORED" : "FAILED",
}
} else if (const destinationBranch: stringdestinationBranch === "develop") {
const const isValidSource: booleanisValidSource =
const sourceBranch: stringsourceBranch.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.startsWith("feature/") ||
const sourceBranch: stringsourceBranch.String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.startsWith("bugfix/")
return {
title: stringtitle:
"Source branch for *develop* must be *feature/** or *bugfix/**",
conclusion: boolean | "IGNORED" | "FAILED" | "SUCCESS""IGNORED" will be skipped, and the result won't be reported in the checks.
'true' is the same as "SUCCESS" and 'false' is the same as "FAILED"conclusion: const isValidSource: booleanisValidSource ? "IGNORED" : "FAILED",
}
}
},
},
{
name: stringThe name of the policy.name: "branch-must-reference-jira",
check: (context: PullRequestContextData) => CustomPullRequestRuleResult | undefinedcheck: ({pullRequest: PullRequestpullRequest}) => {
const const sourceBranch: stringsourceBranch = pullRequest: PullRequestpullRequest.PullRequest.source: PullRequestSourcesource.PullRequestEndpoint.branch: {
readonly name: string;
}
branch.name: stringname
const const hasJira: booleanhasJira = /^.*\/PRJ-\d+$/.RegExp.test(string: string): booleanReturns a Boolean value that indicates whether or not a pattern exists in a searched string.test(const sourceBranch: stringsourceBranch)
// Only show an example if failing
const const summary: "" | "Example: feature/PRJ-123"summary = const hasJira: booleanhasJira ? "" : "Example: feature/PRJ-123"
return {
title: stringtitle: "Source branch must reference Jira ticket",
summary?: string | undefinedsummary,
conclusion: boolean | "IGNORED" | "FAILED" | "SUCCESS""IGNORED" will be skipped, and the result won't be reported in the checks.
'true' is the same as "SUCCESS" and 'false' is the same as "FAILED"conclusion: const hasJira: booleanhasJira,
}
},
},
]),
],
})
The policy above is displayed as per below when failing: