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)) => voidconfigure} from "flowie.app"

import {
  const merge: OptionsPlugin<MergePluginOptions, false>merge,
  const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requests
draftPullRequest
,
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 tasks
noUnresolvedTasks
,
const minimumBuilds: OptionsPluginNoDefault<MinimumBuildsPluginsOptions, false>minimumBuilds, const minimumApprovals: OptionsPluginNoDefault<MinimumApprovalsPluginsOptions, false>
Users get notified when pull requests don't have that number of approvals
minimumApprovals
,
} from "flowie.app/plugins" function configure(config: Config | (() => Config)): voidconfigure({ Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [ 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 requests
draftPullRequest
(),
// 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 tasks
noUnresolvedTasks
(),
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 approvals
minimumApprovals
(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)) => voidconfigure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requests
draftPullRequest
} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget} from "flowie.app/conditions" function configure(config: Config | (() => Config)): voidconfigure({ Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [ // run only when target is main, using plugin default options function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requests
draftPullRequest
([function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main")]),
], })
  • Run with different options for different branches.
import {const configure: (config: Config | (() => Config)) => voidconfigure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requests
draftPullRequest
} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget} from "flowie.app/conditions" function configure(config: Config | (() => Config)): voidconfigure({ Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [ function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requests
draftPullRequest
(
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: true}],
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: false}]
), ], })
  • With a catch-all clause.
import {const configure: (config: Config | (() => Config)) => voidconfigure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requests
draftPullRequest
} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget, function otherwise(): trueotherwise} from "flowie.app/conditions" function configure(config: Config | (() => Config)): voidconfigure({ Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [ function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requests
draftPullRequest
(
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: true}],
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: false}],
[function otherwise(): trueotherwise, {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: false, DraftPullRequestOptions.title?: string | undefined
Title for the merge check. It supports markdown for formatting. Default: **No** work in progress
title
: "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)) => voidconfigure} from "flowie.app"
import {const draftPullRequest: OptionsPlugin<DraftPullRequestOptions, false>
Support for draft pull requests
draftPullRequest
} from "flowie.app/plugins"
import {function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget} from "flowie.app/conditions" function configure(config: Config | (() => Config)): voidconfigure({ Config.plugins?: PluginDef<unknown>[] | undefinedplugins: [ function draftPullRequest(...rules: Rules<DraftPullRequestOptions | undefined>): PluginDef<DraftPullRequestOptions> (+2 overloads)
Support for draft pull requests
draftPullRequest
(
// Condition Configuration // | | // v v [function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main"), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: true}], // <-------- Rule 1
[function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget(/feature.*/), {DraftPullRequestOptions.draftOnCreate?: RulesOr<boolean | undefined>
Adds the label automatically to the pull request on creation, marking it as a draft. Default: true
draftOnCreate
: 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: Labelslabels, function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget, function source(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditionsource, const changeset: ChangesetConditionschangeset, function not(condition: ChainableCondition): ChainableConditionnot} from "flowie.app/conditions"

const labels: Labelslabels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)has("Ready to merge").ChainableCondition.and: (also: Condition<boolean>) => ChainableConditionand(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main"))
const labels: Labelslabels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)has("Ready to merge").ChainableCondition.or: (also: Condition<boolean>) => ChainableConditionor(function source(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditionsource("main"))
const labels: Labelslabels.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>) => ChainableConditionnot(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main"))
const changeset: ChangesetConditionschangeset.ChangesetConditions.matches: (glob: string) => ChainableConditionmatches("**/*.md" /* <== Glob */) // Reuse const const readyMain: ChainableConditionreadyMain = const labels: Labelslabels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)has("Ready to merge").ChainableCondition.and: (also: Condition<boolean>) => ChainableConditionand(function target(...branches: Array<string | RegExp> & NonEmptyArray): ChainableConditiontarget("main")) // Negation function not(condition: ChainableCondition): ChainableConditionnot(const labels: Labelslabels.Labels.has(label: string | LabelRef): ChainableCondition (+1 overload)has("Ready to merge")) function not(condition: ChainableCondition): ChainableConditionnot(const readyMain: ChainableConditionreadyMain)

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)) => voidconfigure} from "flowie.app"

function configure(config: Config | (() => Config)): voidconfigure({
  
Config.branches?: {
    restrictions?: boolean | "manual" | BranchRestrictionOptions;
} | undefined
branches
: {
restrictions?: boolean | "manual" | BranchRestrictionOptions | undefinedrestrictions: { /* 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" | undefinedpushGroups: ["CI"], }, "release": { // Alternatively, use "Everybody" special value to allow any user // with write access BranchOptions.pushGroups?: (string[] & [unknown, ...unknown[]]) | "Everybody" | undefinedpushGroups: "Everybody", }, "feature": { // Enable reset for changes BranchOptions.resetRequestedChanges?: boolean | undefinedresetRequestedChanges: 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" | undefinedpushGroups: ["Developers"], BranchOptions.allowDeleting?: boolean | undefinedallowDeleting: true, BranchOptions.allowRewriting?: boolean | undefinedallowRewriting: 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)) => voidconfigure} from "flowie.app"

function configure(config: Config | (() => Config)): voidconfigure({
  
Config.branches?: {
    restrictions?: boolean | "manual" | BranchRestrictionOptions;
} | undefined
branches
: {
restrictions?: boolean | "manual" | BranchRestrictionOptions | undefinedrestrictions: {
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" | undefinedpushGroups: ["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.

Editor example

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.