Configuration
This page explains the concepts and options available to configure Flowie. If you are just getting started, you can skip this page and come back later.
Flowie utilizes configuration as code using JavaScript/TypeScript to define it.
Configuration is defined via Flowie settings pages, which can be accessed from the repository or workspace settings.
A typical configuration looks like this:
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
import {
const merge: OptionsPlugin<MergePluginOptions, false>
merge,
const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requestsdraftPullRequest,
function noChangesRequested(): PluginDef<boolean> (+2 overloads)
Users get notified if pull requests have ‘Changes requested’ associated with
any of the reviewers.noChangesRequested,
function noUnresolvedTasks(): PluginDef<boolean> (+2 overloads)
Users get notified when they have open pull request tasksnoUnresolvedTasks,
const minimumBuilds: OptionsPluginNoDefault<MinimumBuildsPluginsOptions, false>
minimumBuilds,
const minimumApprovals: OptionsPluginNoDefault<MinimumApprovalsPluginsOptions, false>
Users get notified when pull requests don't have that number of approvalsminimumApprovals,
} from "flowie.app/plugins"
function configure(config: Config | (() => Config)): void
configure({
Config.plugins?: PluginDef<unknown>[] | undefined
plugins: [
function merge(options: MergePluginOptions): PluginDef<MergePluginOptions> (+2 overloads)
merge({
MergePluginOptions.allow?: RulesOr<string[] | undefined>
allow: ["Team leaders"],
}),
function draftPullRequest(): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requestsdraftPullRequest(),
// Basic checks
function noChangesRequested(): PluginDef<boolean> (+2 overloads)
Users get notified if pull requests have ‘Changes requested’ associated with
any of the reviewers.noChangesRequested(),
function noUnresolvedTasks(): PluginDef<boolean> (+2 overloads)
Users get notified when they have open pull request tasksnoUnresolvedTasks(),
function minimumBuilds(options: MinimumBuildsPluginsOptions): PluginDef<MinimumBuildsPluginsOptions> (+1 overload)
minimumBuilds(1),
function minimumApprovals(options: MinimumApprovalsPluginsOptions): PluginDef<MinimumApprovalsPluginsOptions> (+1 overload)
Users get notified when pull requests don't have that number of approvalsminimumApprovals(2),
],
})
Scopes
The configurations can be defined at repository, project or workspace level. Configurations are inherited by all repositories in the project or workspace, and can be overridden by the repository configuration.
Plugins
Plugins are defined using the plugins
property. They provide high level and common
workflows by leveraging Flowie functionalities such as labels and checks combined.
Check the Plugins section of the documentation to find more about the plugins available.
Rules and conditions
Rules and conditions allow you to define different configurations based on the context of the pull request.
Examples:
- Define a condition to use a different plugin configuration based on the target branch.
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requestsdraftPullRequest} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target} from "flowie.app/conditions"
function configure(config: Config | (() => Config)): void
configure({
Config.plugins?: PluginDef<unknown>[] | undefined
plugins: [
// run only when target is main, using plugin default options
function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requestsdraftPullRequest([function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main")]),
],
})
- Run with different options for different branches.
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requestsdraftPullRequest} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target} from "flowie.app/conditions"
function configure(config: Config | (() => Config)): void
configure({
Config.plugins?: PluginDef<unknown>[] | undefined
plugins: [
function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requestsdraftPullRequest(
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: true}],
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: false}]
),
],
})
- With a catch-all clause.
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requestsdraftPullRequest} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target, function otherwise(): true
otherwise} from "flowie.app/conditions"
function configure(config: Config | (() => Config)): void
configure({
Config.plugins?: PluginDef<unknown>[] | undefined
plugins: [
function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requestsdraftPullRequest(
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: true}],
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: false}],
[function otherwise(): true
otherwise, {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: false, DraftPullRequestOptions.title?: string | undefined
Title for the merge check. It supports markdown for formatting. Default:
**No** work in progresstitle: "A different title"}]
),
],
})
Rules
A Rule
is a tuple that defines a Condition
and a configuration value for the plugin, which is
used when the condition is matched. You can define multiple rules for the same
plugin; if no rule is matched, the plugin will not be applied.
A rule is defined as follows:
[condition, configuration]
multiple rules use the following syntax:
[condition, configuration], [condition, configuration], ...
if the plugin has a default configuration, you can omit it:
[condition], [condition, configuration], ...
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requestsdraftPullRequest} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target} from "flowie.app/conditions"
function configure(config: Config | (() => Config)): void
configure({
Config.plugins?: PluginDef<unknown>[] | undefined
plugins: [
function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requestsdraftPullRequest(
// Condition Configuration
// | |
// v v
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: true}], // <-------- Rule 1
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as
a draft. Default: truedraftOnCreate: false}] // <--- Rule 2
/*... more rules...
^--- If none of the conditions are matched,
the plugin will not be applied.
*/
),
],
})
Conditions
Condition is a function that takes a context and returns a value. The
most common conditions are built-in
and can be imported from flowie.app/conditions
.
Combining conditions
You can combine conditions logically:
import {const labels: Labels
labels, function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target, function source(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
source, const changeset: ChangesetConditions
changeset, function not(condition: ChainableCondition): ChainableCondition
not} from "flowie.app/conditions"
const labels: Labels
labels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)
has("Ready to merge").ChainableCondition.and: (also: Condition<boolean>) => ChainableCondition
and(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"))
const labels: Labels
labels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)
has("Ready to merge").ChainableCondition.or: (also: Condition<boolean>) => ChainableCondition
or(function source(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
source("main"))
const labels: Labels
labels.Labels.hasAny(...labels: Array<string | LabelRef> & NonEmptyArray): ChainableCondition (+1 overload)
hasAny("Ready to merge", "WIP").ChainableCondition.and: ((also: Condition<boolean>) => ChainableCondition) & {
not: (also: Condition<boolean>) => ChainableCondition;
}
and.not: (also: Condition<boolean>) => ChainableCondition
not(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"))
const changeset: ChangesetConditions
changeset.ChangesetConditions.matches: (glob: string) => ChainableCondition
matches("**/*.md" /* <== Glob */)
// Reuse
const const readyMain: ChainableCondition
readyMain = const labels: Labels
labels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)
has("Ready to merge").ChainableCondition.and: (also: Condition<boolean>) => ChainableCondition
and(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableCondition
target("main"))
// Negation
function not(condition: ChainableCondition): ChainableCondition
not(const labels: Labels
labels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)
has("Ready to merge"))
function not(condition: ChainableCondition): ChainableCondition
not(const readyMain: ChainableCondition
readyMain)
Branch restrictions
The branch.restrictions
property allows customizing or disabling the branch
permissions managed by Flowie in order to implement some of its features.
By default, when restrictions are enabled, Flowie will protect branches based on the branching model defined on Bitbucket. It will protect production
,
main
and release
branches, by only allowing pull request merge through Flowie,
and no write, push, create, rewrite or delete. It also removes any extraneous configuration.
If you wish to keep other branch restrictions, or modify the default behavior
your can use branch.restrictions
:
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
function configure(config: Config | (() => Config)): void
configure({
Config.branches?: {
restrictions?: boolean | "manual" | BranchRestrictionOptions;
} | undefined
branches: {
restrictions?: boolean | "manual" | BranchRestrictionOptions | undefined
restrictions: {
/*
It accepts a branch pattern or the branch types from the
branching model that are prefixed: release, feature,
hotfix and bugfix
*/
"main": {
// Let CI group write to the branch
BranchOptions.pushGroups?: (string[] & [unknown, ...unknown[]]) | "Everybody" | undefined
pushGroups: ["CI"],
},
"release": {
// Alternatively, use "Everybody" special value to allow any user
// with write access
BranchOptions.pushGroups?: (string[] & [unknown, ...unknown[]]) | "Everybody" | undefined
pushGroups: "Everybody",
},
"feature": {
// Enable reset for changes
BranchOptions.resetRequestedChanges?: boolean | undefined
resetRequestedChanges: true,
// Premium Bitbucket only features
// resetApprovals: true
// resetApprovalsKeepIfNoChanges: true
},
// A custom rule, outside the branching model
"my-branch-prefix/*": {
BranchOptions.pushGroups?: (string[] & [unknown, ...unknown[]]) | "Everybody" | undefined
pushGroups: ["Developers"],
BranchOptions.allowDeleting?: boolean | undefined
allowDeleting: true,
BranchOptions.allowRewriting?: boolean | undefined
allowRewriting: true,
},
},
},
})
Or can completely disable it by setting restrictions
to false
.
Create branches
Typically, you will want to allow admins or scripts to create new branches under protected prefixes. For instance, creating new releases.
import {const configure: (config: Config | (() => Config)) => void
configure} from "flowie.app"
function configure(config: Config | (() => Config)): void
configure({
Config.branches?: {
restrictions?: boolean | "manual" | BranchRestrictionOptions;
} | undefined
branches: {
restrictions?: boolean | "manual" | BranchRestrictionOptions | undefined
restrictions: {
release: {
pushGroups: [string, string];
}
release: {
// Give permission to the Administrators group
// to create new branches, e.g.,
// release/1.0, release/2.0, and so on.
BranchOptions.pushGroups?: (string[] & [unknown, ...unknown[]]) | "Everybody" | undefined
pushGroups: ["Administrators", "CI"],
},
},
},
})
Editor support
The script setting pages have support for auto-completion and validation using the Monaco editor. This is the same editor used in VS Code. Learn more about the editor features here.
Why JavaScript?
Instead of inventing a new configuration language, Flowie leverages JavaScript/TypeScript to configure the workflow. This allows you to use the full power of a programming language to configure your workflow, which is especially useful for complex workflows and when using conditionals. Also, it can re-use all the tooling and knowledge already available.
This is a similar concept of tools like AWS CDK, Pulumi and CDK for Terraform.
You are not required to know JavaScript, as most of the configurations should be really simple, however, if you require mode advanced or custom configurations, you will probably benefit from it.