DevSensei | Code Owners for Bitbucket
Breadcrumbs

DevSensei: PR Workflow Automation

DevSensei-logo-colour.png
Workflow Automation for Dev Teams

What is DevSensei?

Table of Contents

Note

  • 🔰 fundamental topics for beginners getting started with DevSensei

  • 🔬 advanced topics for once you have your first workflows automated

DevSensei is a pull request workflow automation solution integrated into the Code Owners App.

It is for teams who want to:

  • reduce the overhead of working with pull requests.

    • e.g. auto merge minor changes after the automated checks passed

  • automate workflows in pull requests.

    • e.g. assign the right reviewers

  • follow and enforce team policies.

    • e.g. add tasks to the pull request based on what changes are made, and block merging until the tasks are completed.

Getting Started 🔰

  1. Enable DevSensei for your repository:

    1. Navigate to the repository → Repository Setting (⚙️ on the left) → DevSensei | Code Owners Settings. Alternatively, the setting is also available at the project level.

    2. In the settings panel, enable the DevSensei Workflows

  2. Add the sample devsensei.yaml file below to the default branch of your repository.

Workflow to add comment on pull requests ready to review (basic example)

workflows: - name: Add Comment conditions: - not(draft) actions: - add-comment: content: | This pull request is ready to review. Thanks.

  1. Done! When you create a pull request, then the comment will be added. On draft pull requests the comment is added as soon as the pull request is marked as ready to review.

Some more examples

workflows: - name: Bugfix Release Policy Reminder conditions: - destination ~= 'release/*' # When the pull request targets a release branch - source ~!= 'bugfix/*' # And isn't from a bugfix branch actions: # Add the reminder comment - add-comment: content: | Reminder: Only bugfixes should be merged into a release branch. If it is not an urgent bugfix, please change the pull request destination to the development branch. - name: Release Tasks conditions: - source=main - destination=latest-release actions: - add-comment: content: Is the public documentation up to date? task: true - add-comment: content: The release announcement is prepared? task: true - name: Add reviewers custom-attributes: codeowners: rules: | **/*.java java-expert@our-company.com **/*.js javascript-expert@our-company.com conditions: - destination ~!= 'sandbox/*' actions: - add-reviewers: members: codeowners

Workflows 🔰

Each workflow automates specific tasks for the pull requests of your team.

A workflow includes:

  1. a unique name to identify the workflow

  2. a set of conditions to be met. Once these conditions are met the actions are executed

  3. optionally: a set of actions to be executed when the conditions are met for a pull request

  4. optionally: a set of merge-checks (active when workflow conditions are met) that will prevent merging unless they succeed.

How does it work? 🔰

Configure the workflows for your team

  1. on the default branch

  2. in the devsensei.yaml file

  3. at the top-level directory of your repository.

The devsensei.yaml file consists of a set of workflows.

Conditions 🔰

Use conditions to decide for which pull requests to run your actions, or enforce extra merge checks. Conditions give you full control to tailor a workflow to run the actions exactly when you need.

A basic condition is a comparison on a pull request attributes like title, source and destination branch. Then compare the attributes with an operators like equality =, glob matching ~=, negations not to the desired value.

conditions: - draft # check that pull request is a draft - not(draft) # negate a comparison: Check that the pull request is not a draft - source = 'main' # Check that the source branch is the main branch - destination ~= 'releases/*' # Check that the destination is matching the glob releases/*

All conditions in the conditions list must be fulfilled to run an action. Use or and and blocks if you need logical combinations of conditions.

conditions: - or: - draft - title ~= 'DRAFT*' - and: - source = 'develop' - destination ~= 'releases/*'

Define complex conditions using expressions.

Actions 🔰

Actions do things for you, like adding comments and/or reviewers, merging the PR, etc.

A workflow has one or more actions.

When Do Actions Run 🔰🔬

Actions run when the conditions change from false to true.

Then, actions do not run again as long as the condition stays true.

When conditions go again back to false and then true, the actions run again.

https://bitbucket.org/mibexsoftware/codeowners-documentation/raw/main/bitbucket-dc/when-do-actions-run.png

This concept is called Edge Triggering, as actions are triggered on the 'edge' of the signal when the conditions do change.

Run Actions More Often 🔬

Sometimes you need to run the actions of a workflow more often, for example every time new commits are made to the pull request.

For that, there is the retrigger-on section. If the value of one of the attributes in the retrigger-on section changes, it will "reset" the condition signal and if the conditions are currently met, a new edge trigger happens, and the workflow’s actions will run again.

https://bitbucket.org/mibexsoftware/codeowners-documentation/raw/main/bitbucket-dc/retrigger-actions.png

Examples:

workflows: - name: Reminder that changes for releases need extra care conditions: - destination ~= 'release/*' retrigger-on: - source-head-sha # Retrigger if commits change actions: - add-comment: content: | Be careful. This changes are for a bugfix release. - name: Send a reminder to the customer of a planned fix conditions: - destination ~= 'customer/*' - source ~= 'bugfix' retrigger-on: - destination # Retrigger if the destination changes actions: - add-comment: content: | Inform the customer about the planned customer specific bugfix - name: Add CodeOwner reviewers, and update if the are new commits conditions: - destination ~= 'customer/*' - source ~= 'bugfix' retrigger-on: - diff-change # Retrigger if contents of diff change custom-attributes: codeowners: rules: | **/*.java java-expert@our-company.com **/*.js javascript-expert@our-company.com actions: - add-reviewers: members: codeowners

Merge checks 🔰

Merge checks let you define extra rules, enforced while workflows are active, to protect your team’s repository from merging invalid PRs. For example, only accepting PR’s that follow your specific branch naming conventions.

Compared to Bitbucket’s own builtin merge checks, DevSensei allows you to use the same expressions and attributes as workflow conditions to customize exactly when checks should prevent a merge.

Example

Bitbucket has a builtin merge check to enforce a minimum number of approvals. However, the only configuration is a number. With DevSensei you can scope this further by, for example, restricting the check to specific branch patterns:

workflows: - name: stricter main checks conditions: - destination = 'main' merge-checks: - title: 2 approvals for main check: - count(approved-by) >= 2

Syntax support in IDE for DevSensei configuration 🔰

The app provides a YAML Schema for the devsensei.yaml file.

Benefits:

  • Auto-completion of YAML keys

  • Basic validations, (e.g. ensure that a workflow has actions)

  • Documentation of YAML elements within the editor

  • Showing code examples from the Spec as help

Download the YAML Schema from Bitbucket:

https://YOUR_BITBUCKET/rest/codeowners/1.0/devsensei/schema

Depending on your IDE, map that YAML Schema to files named devsensei.yaml.

IntelliJ IDEA & JetBrains IDE’s
  1. Go to IntelliJ IDEA (or other Jetbrains IDE) settings

  2. Search for JSON Schema Mappings

  3. Add a new mapping:

    1. Name: DevSensei Schema file

    2. Schema URL: https://YOUR_BITBUCKET/rest/codeowners/1.0/devsensei/schema

    3. Schema version: JSON Schema version 7

    4. File: devsensei.yaml

VS Code

VS Code with the RedHat YAML plugin can either use an inline reference to the JSON Schema

# yaml-language-sever: $schema=https://YOUR_BITBUCKET/rest/codeowners/1.0/devsensei/schema workflows: # ...

or have a global mapping within the VS Code settings.json:

{ "yaml.schemas": { "https://YOUR_BITBUCKET/rest/codeowners/1.0/devsensei/schema": [ "devsensei.yaml" ] } }

Migration: From Code Owners to DevSensei 🔰

What advantages has DevSensei compared to Code Owners?

  • devsensei.yaml can share common rules across repositories with included devsensei.yaml files called "Includes" to reduce duplication and maintenance efforts.

  • Common configuration parts (e.g. reusing the list of reviewers) can be shared with YAML anchors.

  • DevSensei allows to build the automation you want with combining conditions and actions.

  • DevSensei currently supports add-reviewers, unapprove, codeowners-check (and other merge-checks), add-comment, and schedule-auto-merge. We will add more actions in the future to automate your pull request workflow. Let us know what actions you are looking for.

  • devsensei.yaml is read from the default branch of your repository. This will reduce the maintenance efforts significantly when the automation for the repository needs changes.

How can I migrate from Code Owners to DevSensei?

To start using DevSensei from your existing CODEOWNERS file, you have two options:

  1. a) Automated migration: migrate your CODEOWNERS file to devsensei.yaml with the built-in migration support (see button Download generated devsensei.yaml).

  2. b) Manual migration: migrate your CODEOWNERS settings to their equivalents in devsensei.yaml.

    1. Copy the assignment rules from the CODEOWNERS file to the rules section of codeowners custom attribute in devsensei.yaml.

    2. For the settings, use a combination of workflow conditions, and options for the add-reviewers, add-comment, and unapprove actions (see the Actions paragraph below).

    3. For the custom Code Owner groups (e.g. @@@my-group @peter @anna), add a custom-groups custom attribute to the workflow.

    4. For the merge checks, add a codeowners-check to the merge-checks section of a workflow.

  3. push the devsensei.yaml file to the root directory in the default branch of your repository

  4. enable DevSensei under repository settings -> DevSensei | Code Owners -> DevSensei Workflows -> Enabled

  5. when the app sees a devsensei.yaml file, it will use that instead of CODEOWNERS.

Note

DevSensei reads the devsensei.yaml configuration from the default branch of your repository for every pull request. This is in contrast to Code Owners configuration in CODEOWNERS file, that is taken from the destination branch of the pull request.

Below you can see both a CODEOWNERS file and the equivalent devsensei.yaml file. This should help you to migrate from your Code Owners rules to the new YAML format.

The format of the Code Owners rules is the same, so you can copy that to the rules section of the codeowners custom attribute.

CODEOWNERS

CODEOWNERS.destination_branch_pattern main CODEOWNERS.destination_branch_pattern release/* CODEOWNERS.toplevel.subdirectory_overrides enable CODEOWNERS.toplevel.assignment_routing random 2 CODEOWNERS.toplevel.assignment_limit 20 CODEOWNERS.toplevel.create_pull_request_comment enable CODEOWNERS.toplevel.auto_unapprove_on_change enable CODEOWNERS.source_branch_exclusion_pattern hotfix/* @@@MyDevs @PeterTheHacker @PeterTheJavaExpert ann@scala.lang @@JSDevs * @PeterTheHacker *.java @PeterTheJavaExpert *.js @PaulTheJSGuru @@JSExperts "a/path with spaces/*" docs@example.com !ci/playgrounds.yml src/components/**/*.js @@MyDevs Check(@@MyDevs >= 2)

devsensei.yaml

shared: - custom-groups: MyDevs: - PeterTheHacker - PeterTheJavaExpert - ann@scala.lang - @@JSDevs workflows: - name: Add Code Owners conditions: - or: - destination = 'main' - destination ~= 'release/*' - source ~!= 'hotfix/*' retrigger-on: - diff-change # If you want update Code Owners when the pull request code is updated custom-attributes: custom-groups: MyDevs: *MyDevs codeowners: rules: | * @PeterTheHacker *.java @PeterTheJavaExpert *.js @PaulTheJSGuru @@JSExperts "a/path with spaces/*" docs@example.com !ci/playgrounds.yml src/components/**/*.js @@MyDevs actions: - unapprove: members: codeowners - add-reviewers: members: codeowners assignment-routing: random: 2 assignment-limit: 20 - add-comment: report-from: add-reviewers merge-checks: codeowners-check: | Check(@@MyDevs >= 2)

Code Owners Settings NOT supported by DevSensei equivalents 🔬

Code Owners feature

Why not supported / Alternative?

CODEOWNERS.toplevel.subdirectory_override

Manually include rule files of sub directories

Note

To replicate the previous behavior of the sub-dir override feature with DevSensei workflows, you must:

  1. prefix the file patterns with the subdir in the corresponding codeowners custom attribute

  2. exclude the subdirs in the "root" codeowners custom attribute with a negation rule

Example: if you have CODEOWNERS with subdirectory_override=true and module-a/CODEOWNERS.

  1. prefix file patterns in migrated codeowners custom attributes of module-a like module-a/PATTERN

  2. add !module-a/ as last rule to migrated root CODEOWNERS action to ignore the sub directory of module-a in this action

Reference: devsensei.yaml

Workflows 🔰

Each workflow is meant to automate specific tasks for the pull requests of your team.

Properties

Attribute

Definition

name (required)

The name of the workflow. Must be unique in a repository.

conditions (optional, but probably wanted) 🔰

A set of conditions to be met for the actions of the workflow to be executed for a pull request. If empty then all pull requests will match.

retrigger-on (optional) 🔬

A set of expressions. Causes additional trigger for actions each time the value changes while the condition is currently met.

retrigger-on: # on change to an attribute's value - destination # on a change to the contents of the PR's diff. - diff-change

actions (required) 🔰

A set of actions to be executed each time the conditions are met for a pull request, or when a value in the retrigger-on list changes while the conditions are met.

merge-checks (optional) 🔰

A set of rules that are continuously checked. If any rule fails to pass then the PR is prevented from merging.

custom-attributes (optional) 🔰

A set of configurable attributes that can be used within actions and merge-checks. They can for example be used to define user groups or Code Owners in the workflow.

overrides (optional) 🔬

A workflow with the same name can be overridden in the main devsensei.yaml file. If so, the workflow object must have overrides=true. If not, then you will get a validation error for the duplicate names.

main.yaml

includes: - other.yaml workflows: # overriding included workflow - name: I am overridden overrides: true

other.yaml

workflows: # original definition of workflow - name: I am overridden conditions: #... actions: #...

Conditions 🔰

The conditions property of a workflow is configured with a list of conditions.

Condition evaluation

Each condition is a boolean expression, so composition with and and or obeys the usual rules for boolean algebra.

If there are no conditions (i.e. there is no mapping, or an empty list), then the condition is always met by default, so any action in the workflow run at least once. This means that each additional condition further constrains the cases where actions should be applied. If there are more than one condition in a workflow’s conditions property, then they must be all true simultaneously for the overall condition to be met (i.e. they are combined with and implicitly).

Conditions are dynamic expressions, and can retrieve and compare metadata (known as attributes) associated with the current pull request. Conditions are evaluated when a pull request is created, whenever the pull request or its various metadata changes.

Condition attributes

These are the various attributes available to use in conditions, retrigger-on, and merge-checks. Each attribute results in a typed value when evaluated.

Note

currently only attributes marked as watchable can be used in the retrigger-on section (subject to change). Let us know if you need this for your use case.

Condition Attribute

Watchable

Type

Meaning

title

✅ (yes)

string

The pull request title

source

✅ (yes)

string

Source branch of pull request

destination

✅ (yes)

string

Destination branch of pull request

repository-name

✅ (yes)

string

Destination repository of pull request

source-head-sha

✅ (yes)

string

The SHA-1 hash of commit of the HEAD ref on the source branch

draft

✅ (yes)

boolean

Is it a draft pull request

conflicts

✅ (yes)

boolean

Whether the Pull Request currently has conflicts with the target branch.

author

❌ (immutable)

user

The user who created the PR.

open-tasks

💡 (planned)

set

The set of tasks that are still open in the PR.

approved-by

💡 (planned)

set

The set of username slugs corresponding to users who have approved the PR.

reviewers

💡 (planned)

set

The set of users who are reviewers of the PR and their review status. Has properties .approved, .changed-request and .waiting.

changed-files

💡 (planned)

set

The set of all files that are affected by the pull request. Has properties .added, .deleted, .modified and .renamed.

builds

💡 (planned)

set

The set of builds for the most recent commit for the pull request. Has properties .successful, .failed and .in-progress.

watchers

💡 (planned)

set

The set of users who are watchers of the PR. Watchers are people that interacted with the pull request. See Bitbucket documentation.

commits

💡 (planned)

list

List of commits in this PR. Has properties .titles, .message and .authors. Each commit also has a jira-keys property

jira-keys

💡 (planned)

set

Set of Jira keys mentioned in all commit messages.

Aggregate condition operators 🔬

There are two logical operators that can compose a list of conditions

Condition Operator

Meaning

and

All conditions must be fulfilled

or

At least one of the conditions must be fulfilled

Custom Attributes 🔰

Custom attributes are configurable attributes to be used within a workflow, for example as part of the configuration of an action or a merge check.

Available custom attributes are:

  • custom-groups

  • codeowners

Custom Groups

Custom groups define a set of users grouped together as a single entity. There are similar to Bitbucket or Reviewer user groups, but they are only defined within a workflow.

A group is defined by a name and a list of identifiers. An identifier can be

  • a reference to a user (as individual via email or username) (e.g., @alice or alice@domain.com), or

  • a reference to another group (custom group, Bitbucket group or reviewers group) using @@<group-name>.

Syntax

Custom groups are defined as a yaml mapping from the name of the group to the list of identifiers within that group.

Example:

custom-groups: frontend-devs: - alice - bobby@domain.com - @@admins backend-devs: - bobby@domain.com - charly

Code Owners

The codeowners custom attributes is defined by a rules member, configuring what files are owned by which people.

Syntax

The rules are defined using a multi-line string where each line follows one of the two following patterns:

  • <file pattern> <code owners>: to assign files to some code owners

  • !<file pattern>: to exclude files from being assigned.

(Rule line order matters: the last matching line wins.)

See our complete reference for more context and information.

Example:

codeowners: rules: | * @alice bobby@domain.com frontend/ @@frontend-devs backend/ @@backend-devs devsensei.yaml @@admins !ci/playgrounds.yml

Expressions 🔬

Expressions are small computations on Pull Request attributes. Expressions are most useful in workflow conditions and custom merge checks. Expressions can be as simple as a constant (like a number or a string) or an attribute value, but expressions can also be more complex manipulation of attributes.

Some examples of expressions are

  • draft

  • true

  • 'Hello World'

  • source ~= 'bugfix/*'

  • count(approved-by)

  • builds.successful

  • forall(changed-files, $1 ~= '**/*.js')

Expressions can be composed in any form or fashion using operators described below, as long as type requirements are fulfilled. If an expression does not type-check (e.g., source > 3), it will make the devsensei.yaml file invalid.

Note

Expressions can return any type, but expressions involved in conditions have to return a boolean. For example, even though count(approved-by) is a valid expression, it is not valid as part of the conditions.

Expression types

Here are the following types that can be involved in expressions

Type name

Description

any

a wildcard meaning any type is accepted.

boolean

a value that is one of either true or false.

string

any UTF-8 text value.

int

an integer value (between -2147483648 and 2147483647)

set

an aggregate of unique elements, may have optional properties, (e.g. builds.successful or changed-files.added).

user

A registered application user; can be identified by both username and email.

lambda(from, to)

a lambda function expression taking an argument of type from and returning an argument of type to.

Expression Operators

Use Operators to transform expressions into more complex ones. For example, to compare two int values or to count the number of elements in a set. Operators can be infix (only for binary operators), prefix or call. Infix operators are placed within their arguments, prefix operators must be placed before their arguments, and call operators must enclose their argument(s) in parenthesis, separated by commas.

Operator

Meaning

Type

Position

Example

~=

Glob match with a branch pattern.

(string, string) → boolean

infix

source ~= 'bugfix/*'

~=

Regex match

(string, regex) → boolean

infix

title ~= regex('^(FIX|FEAT)')

~=

Glob or regex match which returns true when at least one file path in the set matches. (only changed-files supported)

(set, string) → boolean, (set, regex) → boolean

infix

changed-files.modified ~= '**/*.js', changed-files.added ~= regex('.*\.js')

~!=

Negation of glob or regex match (i.e. no matches)

(string, string) → boolean, (set, string) → boolean

infix

title ~!= 'DRAFT*'

=

Equals

(any, any) → boolean

infix

destination = 'master', author = foo@example.com

!=

Not equals

(any, any) → boolean

infix

count(open-tasks) != 0

! or not

Negate a boolean attribute (use of ! must be in a quoted string)

(boolean) → boolean

prefix

!draft

>, >=, < and <=

Number or string length comparison

(int, int) → boolean, (string, int) → boolean, (int, string) → boolean

infix

5 >= 3, title <= 50

count

Computes the number of elements in a set, or the number of characters in a string

(set) → int, (string) → int

call

count(approved-by), count('hello')

contains

Checks for membership of an element in a set, e.g. check if a string represents a user in the reviewers set.

(set, string) → boolean

call

contains(reviewers, 'user1')

not

Negates a boolean expression

(boolean) → boolean

call

not(count(approved-by) > 3)

forall

Checks whether the items in the first argument collection all satisfy the predicate specified by the lambda in the second argument. Always returns true for empty collections.

(set, lambda(set-element, boolean)) → boolean

call

forall(changed-files, $1 ~= regex('src/.*'))

exists

Checks whether there is at least one item in the first argument collection that satisfy the predicate specified by the lambda in the second argument. Always returns false for empty collections.

(set, lambda(set-element, boolean)) → boolean

call

exists(reviewers, $1 = 'alice')

Properties 🔬

Some attributes have properties with a sub-set of the data.

Syntax

<attribute>.<property>

Applying a property will access the property of the attribute.

attribute/type

Property

Meaning

builds

successful

The successful builds

failed

The failed builds

in-progress

The builds that are in progress

changed-files

added

Only the new files that were not seen before

deleted

Only the files that will be deleted after merging

modified

Only the files that changed (and possibly also moved).

renamed

Only the files that were not changed, but were moved

reviewers

approved

Only the reviewers who have approved the PR.

changes-requested

Only the reviewers who have requested changes on the PR.

waiting

Only the reviewers who have not completed their review.

commits

titles

list of all tiles of the commits.

messages

list of all commit messages.

authors

set of all authors of all the commits

commit

title

The title of the commit message (the first line)

jira-keys (per commit only)

set of Jira keys in commit. Eg exists(commits, count($1.jira-keys) = 0) to ensure all commits have a Jira key

Lambda functions 🔬

Some functions require to have a lambda function as argument, for example exists or forall. A lambda function can be composed as any usual expression, where the argument of the lambda is represented by $1.

For example, the following expressions are valid lambdas:

  • $1 ~= 'alice'

  • !$1

  • count($1) < 50

  • contains(reviewers.approved, $1)

Syntax reference

Expressions can be

  • a simple boolean, true or false

  • an integer value, e.g., 7, -6, +2

  • a string literal, enclosed in single quotes, e.g., 'Hello world'. Within quotes, single quotes ' and backslash \ must be escaped by preceding them with a backslash.

  • an attribute value, e.g., source, approved-by

  • of the form <expression> <infix operator> <expression>, e.g., source ~= 'bugfix/*'

  • of the form <prefix operator> <expression>, e.g., !draft

  • of the form <call operator>(<expression> [, <expression>]*), e.g., count(open-tasks), contains(reviewers, 'user')

  • of the form <expression>.<property>, e.g., changed-files.modified

  • of the form ( <expression> ), e.g. (changed-files ~= '**/*.js') = true

Outside of string literals, spaces are insignificant.

Retrigger On 🔬

The retrigger-on section is optional. If configured, it defines a set of expressions that are continuously evaluated, similar to conditions.

Example

retrigger-on: - destination - diff-change

It provides more fine-grained control over when edge-triggering occurs. Normally, actions only run on the "edge" when the condition changes from "not-met" to "met". For example, if attribute values change, but the condition is still met, then normally actions will not run again.

However, if retrigger-on is configured, then whenever one of its expression values changes while the condition is met, the signal for the condition will temporarily flip, enabling a new edge-trigger even though the condition is already met.

You can consider the behavior similar to two electrical signals: condition (A), and retrigger-on (B), combined with a logic gate (A & !B). Signal B is only activated in the instant where a change occurs, but otherwise deactivates, which creates more "edges" in the combined signal.

Concept of retrigger-on

condition (A): ___^^^^^^^^^^^___^^^__ retrigger-on (B): ______^___^___________ A & !B: ___^^^_^^^_^^^___^^^__ EdgeTrigger: ___^___^___^_____^____

Without the retrigger-on section the workflow actions would only run twice. The effect of retrigger-on makes it so that the workflow actions actually run 4 times instead.

Retrigger Expressions

Concretely, in a single expression you can either specify a watchable attribute, or observe changes to the combined diff.

Kind

Syntax

Result

read-attribute

<attribute>

the value of the attribute. Note: the attribute must be watchable

diff-change

diff-change

Compute the overall change in the diff, ignoring other commit metadata.

Look at the attributes table to see which are watchable. Let us know if you would like more kinds of checks to be supported in this section.

Actions 🔰

List of available actions that fuel your automation.

Let us know if you miss an action.

add-reviewers

Suggests and/or adds reviewers to a pull request. (See also add-watchers for watchers instead)

Action option

Definition

members

Decides which users are selected to be added as reviewers.

Choice 1: codeowners

requires Code Owners custom attribute to be defined. Members will be resolved according to the rules applied to the overall diff of the PR.

- add-reviewers: members: codeowners

Choice 2: identifier list

Identifiers are either a user slug, an email address or a group reference (either to a custom group, or to a Bitbucket group).

Group references are resolved transitively to all members of the group.

- add-reviewers: members: - user1 # user slug - user2@domain.com # email address - @@my-group # group reference

assignment-limit (optional)

number

limit the number of reviewers that can be assigned to a pull request. This feature is useful to prevent unwanted notifications in the case of manipulation mistakes (e.g., creating a Pull Request from the wrong branch, or an unhappy rebase)

- add-reviewers: assignment-limit: 20

assignment-routing (optional)

random: n (where n is a positive integer)

reduce the number of reviewers that are automatically assigned to a pull request. Currently, the only available method of assignment is at random.

- add-reviewers: assignment-routing: random: 2

strategy (optional)

choice from assign-only, suggest-only, or suggest-and-assign (default).

  • Suggested reviewers are automatically applied to the Reviewers input field on the Create Pull Request page.

  • Assigned reviewers are added as reviewers whenever the action is triggered.

- add-reviewers: strategy: suggest-only

add-watchers

Adds watchers to a pull request. (See also add-reviewers for reviewers instead)

Action option

Definition

members

Decides which users are selected to be added as watchers.

Choice 1: codeowners

requires Code Owners custom attribute to be defined. Members will be resolved according to the rules applied to the overall diff of the PR.

- add-watchers: members: codeowners

Choice 2: identifier list

Identifiers are either a user slug, an email address or a group reference (either to a custom group, or to a Bitbucket group).

Group references are resolved transitively to all members of the group.

- add-watchers: members: - user1 # user slug - user2@domain.com # email address - @@my-group # group reference

unapprove

Unapprove all or some of the approved reviews in the Pull Request.

Action option

Definition

members

Decides which users are selected to be unapproved.

Choice 1: all

unapprove all users who are currently reviewers.

- unapprove: members: all

Choice 2: codeowners

requires Code Owners custom attribute to be defined. Members will be resolved according to the rules applied to the diff of the most recent change to the PR.

- unapprove: members: codeowners

Choice 3: identifier list

Identifiers are either a user slug, an email address or a group reference (either to a custom group, or to a Bitbucket group).

Group references are resolved transitively to all members of the group.

- unapprove: members: - user1 # user slug - user2@domain.com # email address - @@my-group # group reference

add-comment

Adds a single comment or task to a pull request. Can either be custom content, or automated reporting of action results.

Action option

Definition

content (exclusive)

(string) content of the comment

add-comment: content: Cool fix!

report-from (exclusive)

(string) key referring to a previous action result in the workflow.

add-comment: report-from: add-reviewers

DevSensei will use a builtin formatter to report the results of the action in a readable format.

For now: 💡only add-reviewers is supported.

task

(boolean) whether create as a task (defaults to false)

add-comment: content: | This PR is missing a Jira issue key in the title. Please add it. task: true

schedule-auto-merge

Schedules this pull request to be automatically merged once all merge checks are clear.

Note

schedule-auto-merge shares some functionality with the Auto Merge DevSensei feature, however the features operate independently.

Important

If your repository uses branch permissions to prevent all changes on a branch, then the schedule-auto-merge action will only work with the merge-as-author: true flag.

Action option

Definition

strategy

Optional, possible values: ff, ff-only, no-ff, rebase-ff-only, rebase-no-ff, squash, squash-ff-only

Merge strategy used for the pull request. When not specified, the default merge strategy of the repository will be used. Strategy must be allowed by the repository configuration.

schedule-auto-merge: strategy: no-ff

message

Optional (string) message for the merge commit

schedule-auto-merge: strategy: no-ff message: Merged by DevSensei policy

merge-as-author

Optional (boolean). By default, DevSensei will automatically merge Pull Requests using its own service account. To merge on behalf of the Pull Request author, set merge-as-author to true.

schedule-auto-merge: strategy: no-ff merge-as-author: true

auto-subject

Optional (boolean). When true, Bitbucket generates a message summarizing the merge. If no message is specified, this is always true.

Prerequisites

This action requires:

  • Bitbucket 8.15 or newer

  • The Bitbucket Auto-Merge feature must be enabled for the repository

Auto-Merge Behavior

When the action runs, it submits an Auto-Merge for this pull request. Bitbucket then will merge the pull request, depending on its state:

Pull Request State

What Bitbucket Does

Pull request is mergeable

Immediately merges it

Merge checks are failing

Merges as soon as merge checks pass

Merge conflict is present

No Auto Merge Request is submitted

A commit changes/new commits added while pull request is open

Auto Merge status is cancelled, and DevSensei will not re-submit a Auto Merge Request until the workflow is retriggered.

Check the Bitbucket documentation for more details on how auto merge behaves.

Because the Auto-Merge request may be cancelled by new commits, you may decide to ask again for auto-merging. See the Retrigger On section for more details

Auto-Merge Use Cases

The schedule-auto-merge action makes it simpler to automatically accept lower risk pull requests by merging quickly. As it is based on Bitbucket’s Auto Merge feature, after scheduling, Bitbucket will still prevent merge if there is a veto from one of the merge checks enabled for your repository (for example anything in the merge-checks section).

Use Case

Example Workflow

Fast track code based on a source branch prefix or suffix, e.g. hotfix, code formatting, or documentation fixes, bypassing other checks.

conditions: - source ~= 'hotfix/*' actions: - schedule-auto-merge: strategy: no-ff message: hotfix branch

Fast track code based if build succeed and is from a trusted bot-author, e.g. one that only submits library dependency updates.

conditions: - author = 'deps-updater-bot' - count(builds.successful) > 1 - count(builds.in-progress) = 0 - count(builds.failed) = 0 actions: - schedule-auto-merge: strategy: no-ff message: dependency updates

Merge when the developer requests to skip reviewers, for example by adding a title with a specific prefix.

You can also achieve this using the DevSensei Auto Merge feature, which has the benefit of an interactive graphical user interface, and when skipping is requested it automatically removes the "Codeowners minimum approvals" merge check.

Alternatively, instead of checking the title, you could also look for a pattern in the source branch.

conditions: - title ~= '[SKIP]*' actions: - schedule-auto-merge: strategy: no-ff message: skipped reviewers

Merge when a PR is without conflicts and not in a draft state.

conditions: - "!draft" - "!conflicts" actions: - schedule-auto-merge: strategy: no-ff message: no draft or conflict

Merge when a required number of approvals is met (Equivalent to SHIP/SHOW/ASK).

conditions: - title ~= '[ASK>1]*' - count(approved-by) > 1 actions: - schedule-auto-merge: strategy: no-ff message: no draft or conflict

Other Auto-Merge Use Cases (Planned)

The following ideas rely upon planned or otherwise unimplemented features. Contact support to let us know your use case.

  • Schedule merge when a no reviewer requested changes. (requires new attribute)

  • Wait until another PR has been merged (e.g. merge queue)

  • Check that JIRA ticket is attached before merge (requires a new attribute)

  • Check total size of change is within a small range (requires a new attribute).

add-codeowners

Note

This action is now deprecated and divided into several smaller, more regular, features. See actions add-reviewers and unapprove, and merge check codeowners-check.

Adds Code Owners as reviewers to a pull request.

Action option

Definition

assignment-limit

number

limit the number of Code Owners that can be assigned to a pull request. (opt-in) This feature is useful to prevent unwanted notifications in the case of manipulation mistakes (e.g., creating a Pull Request from the wrong branch, or an unhappy rebase)

- add-codeowners: assignment-limit: 20

assignment-routing

random: n

reduce the number of Code Owners that are automatically assigned to a pull request. (opt-in) Currently, the only available method of assignment is at random.

- add-codeowners: assignment-routing: random: 2

rules

The Code Owners rules and merge checks. For existing CODEOWNERS users, copy your owner rules and the merge checks to the rules section. Note: for sub-directory overrides, check "Not supported settings" section.

- add-codeowners: rules: | * @jordan jordan@example.com /backend/ @@backendies /frontend/ @@frontendies

custom-groups

Define custom Code Owners groups.

- add-codeowners: rules: | pipelines.yml @@admins backend/ @@backendies src/components/**/*.js @@frontendies custom-groups: admins: - @bobby backendies: - @jordan - john.doe@localhost.ch frontendies: - @charly - @@admins

auto-unapprove-on-change

Remove approval if owned code changes.

- add-codeowners: auto-unapprove-on-change: true

cancel-auto-merge (Planned)

Cancels any pending auto merge. This action is planned. Contact support to let us know your use case.

Merge Checks 🔰

List of enforced checks that protect against merging unless they all succeed.

Let us know if your use case is not covered.

Merge checks reuse the same expressions as conditions, and can be grouped with a custom title. DevSensei also offers alias merge checks for common use cases.

Custom Check
Example

merge-checks: - title: block when PR title requests check: - title ~!= '[block]*'

Properties

Property

Definition

check (required)

a list of conditions that must all be met for the check to pass. The list of conditions are implicitly combined with and, so an empty list will always pass the check.

title (required)

a (string) label to describe the custom check. It will also display in the UI, and helps to identify in error messages which check is blocking merging of a PR.

Alias: Minimum Approvals

Equivalent to a custom merge check with the single condition count(approved-by) >= n for positive number n.

Example

merge-checks: - minimum-approvals: 1

Properties

Property

Definition

minimum-approvals (required)

a positive (int) integer value, representing the minimum number of approvers on the PR for the check to succeed.

Alias: Minimum Successful Builds

Equivalent to a custom merge check with the conditions:

  • count(builds.successful) >= n for a positive number n.

  • count(builds.failed) = 0 disallow failed builds

  • count(builds.in-progress) = 0 wait for all builds to be complete

Example

merge-checks: - minimum-successful-builds: 1

Properties

Property

Definition

minimum-successful-builds (required)

a positive (int) integer value, representing the minimum number successful builds on the PRs latest commit to succeed.

Alias: No Open Tasks

Equivalent to a custom merge check with the single condition count(open-tasks) = 0.

Add this merge check by adding the string no-open-tasks to the workflow merge-checks list.

Example

merge-checks: - no-open-tasks

Properties

This merge check alias has no property.

Code Owners Merge Check

This merge check enforces rules as described in the Code Owners documentation. For existing CODEOWNERS users, copy your merge checks to this section.

Properties

Property

Definition

codeowners-check (required)

a multiline string, where each line is an independent rule, and each line must be simultaneously true. Combine multiple rules on the same line with | (OR) or & (AND). Check the exact rules.

Example

merge-checks: - codeowners-check: | (Check(@@MyDevs >= 2) | Check(@@JavaExperts >= 1))

Merge Check Use Cases

The merge-checks section gives more control over which pull requests should be merged, for example applying unique checks to specific branch patterns.

Use Case

Example Workflow

Enforce that there are no incomplete tasks

merge-checks: - title: require no incomplete tasks check: - count(open-tasks) = 0

Enforce that the build passed, and no other build is in progress or failed

merge-checks: - title: require a successful build check: - count(builds.successful) > 1 - count(builds.in-progress) = 0 - count(builds.failed) = 0

Enforce that on PRs to the main branch there are at least 3 approvers

workflows: - name: strict main checks conditions: - destination = 'main' merge-checks: - title: 3 approvers on main check: - count(approved-by) >= 3

Enforce branch naming conventions

merge-checks: - title: Follow branch pattern check: - or: - source ~= 'feature/*' - source ~= 'release/*' - source ~= 'hotfix/*' - source = 'main'

Prevent merge to release branch from a feature branch.

workflows: - name: strict release checks conditions: - destination ~= 'release/*' merge-checks: - title: never release from feature check: - source ~!= 'feature/*'

Restrict PRs on certain branches to be specific author.

workflows: - name: strict release author checks conditions: - destination ~= 'release/*' merge-checks: - title: enforce PR author check: - author = 'release-admin'

Prevent merge PR with a long title.

workflows: - name: Formatting checks conditions: merge-checks: - title: enforce max title length check: - title <= 50

Ensure there are Jira issues references and commit titles follow a guide line

workflows: - name: Commit message checks conditions: merge-checks: - title: Ensure that Jira key issues is referenced check: - count(jira-keys) > 0 - title: Ensure your commit title starts with FIX,FEATURE or MISC check: - forall(commits.titles, $1~=regex('(FIX|FEATURE|MISC).*'))

Prevent merge PR with too many file changes.

workflows: - name: Size checks conditions: merge-checks: - title: enforce max changed files check: - count(changed-files.added) <= 50

Prevent merge if draft or conflicting.

This matches Bitbucket’s own behavior so is unnecessary, but demonstrates how you could create equivalent checks with conditions.

merge-checks: - title: Not draft or conflicting check: - not(draft) - not(conflicts)

Planned Use Cases

The following ideas rely upon planned or otherwise unimplemented features. Contact support to let us know your use case.

Fine-grained equivalents to built-in Bitbucket merge checks

Supporting the following would allow to create finer grained checks (e.g. by filtering on destination branch pattern):

  • Require no "requested changes" status

Support non-functional requirements

  • Require that a pull request has been open long enough to be seen (time-based condition)

  • Prevent merge to a release branch on a specific day.

More fine-grained attributes

  • check text contents of open-tasks, e.g. if your team uses a marker to indicate some tasks can be ignored.

  • check specific user in approved-by.

Code Owners merge checks

Over time we hope to migrate the merge checks from the add-codeowners action, starting with the codeowners-check option. We could possibly make them more flexible, for example check the intersection of approved-by with the added code owners.

Share Configuration with Includes 🔬

With includes, common Devsensei workflows can be shared both across repositories as well within projects in a monorepo.

To include a devsensei.yaml file from another repository, use this syntax:

includes: - repository: shared-configs file: project-devsensei.yaml

Only repositories of the same Bitbucket project are supported.

The Bitbucket users require READ access to the repository with the shared configuration. Otherwise users will get permission issues, because they cannot access the required configuration.

When a plain YAML file path is specified, the included file is read from the same repository:

includes: - common/devsensei.yaml

  • The files are read from the latest commit of the default branch

  • Only one level of includes are support (no recursion supported)

Sharing Parts of the Configuration with YAML Anchors 🔬

Use YAML Anchors to define reusable content in the same YAML file.

Example showing how to share conditions across workflows:

shared: conditions: - &non-hotfix-to-release-branch - source~!=hotfix/* - destination~=release/* workflows: - name: comment conditions: *non-hotfix-to-release-branch actions: - add-comment: content: Hotfix branch expected as source branch when merging into release branches. - name: codeowners custom-attributes: codeowners: rules: | *.js @peter conditions: *non-hotfix-to-release-branch actions: - add-reviewers: members: codeowners

  • Anchors can be used for any content. For example, to define reusable custom user groups, or conditions.

  • Reference must reference anchors in the same file. It is not possible to define an anchor in one file, and reference it from another file.

  • Anchor names cannot contain the [, ], {, }, and , characters.

  • Anchors can be defined anywhere in the file, although we encourage to use the shared section at the top of the file for clarity and easier maintenance.

Configuration-Changes and Existing Pull Requests 🔬

DevSensei reads the configuration always from the default branch. That means that all open pull requests will use the same configuration and changes in the configuration affect all pull requests.

When you change the configuration, then when DevSensei runs the next time, the new configuration is applied.

Table 1. The next time DevSensei runs:

Configuration change

Effect on Existing Pull Request

New Workflow is Added

The workflow’s actions are applied if the conditions are met

Workflow is Renamed

Workflows are identified by name, therefore this acts as if a new workflow is introduced. See above

actions are changed

The next time actions are running, the new configuration for the action is used

condition changes

Then new conditions are checked the next time DevSensei runs

retrigger-on changes

The condition signal is reset when the retrigger-on function changes. The actions run again if the conditions are met