DevSensei | Code Owners for Bitbucket

Expression Language specification

Expressions are small computations on Pull Request attributes. Expressions are most useful in workflow conditions, string templates, 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'

  • 'PR ${title} opened by ${author}'

  • source ~= 'bugfix/*'

  • count(approved-by)

  • builds.successful

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

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

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.

String Template Literals

A literal string also supports templating. Arbitrary expressions can be spliced in with ${expr} if expr is convertible to string.

Template Examples
  • 'PR: ${title}' directly embeds the title attribute of type string.

  • 'Found ${count(commits)} commits' embeds the expression count(commits) of type int by automatic conversion to string.

String Conversions

Expressions of the following types may be directly inserted into a string template: int, boolean, string.

💡 We plan to support more data types in string templates soon. Let us know if you need this for your use case.

Lambda functions 🔬

Some functions require a lambda function as argument, for example exists or forall. A lambda function looks like a usual expression except $1 is used as a placeholder for the function argument.

For example, the following expressions are valid lambdas:

  • $1 ~= 'alice'

  • '{$1}' ~= 'alice' (the same via conversion.)

  • !$1

  • count($1) < 50

  • contains(reviewers.approved, $1)

As a complete example, the following expression checks that at least one reviewer is called "alice":

  • exists(reviewers, $1 = 'alice'),

in this expression reviewers is a collection of type set, and $1 is a placeholder that will be substituted with each reviewer in the collection until a true result is found.

Expression Syntax reference

Atomic expressions

Kind

Example

boolean literals

true or false

integer literals

7, -6, +2

string literals, enclosed in single quotes. Within quotes, single quotes ' , backslash \ , and Template splice openings ${ can be escaped by a single backslash.

'Hello world', 'from: ${source}'

an attribute reference

source, approved-by

lambda function argument reference

$1

wrapped expressions: '(' <expression> ')'

(true != false)

compound expressions

Kind

Syntax

Example

infix operator

<expression> <infix-op> <expression>

source ~= 'bugfix/*'

prefix operator

<prefix-op> <expression>

not draft

function call

<call-op> '(' <expression> [',' <expression> ]* ')'

count(open-tasks), contains(reviewers, 'user')

property access

<expression> '.' <property>

changed-files.modified

Whitespace

Outside of string literals, spaces are insignificant.

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)

array

a sequence of elements, may contain duplicates.

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 since 8.7

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 since 8.7

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 since 8.7

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')

join

Joins the string elements of a collection into a single string, separated by the specified delimiter.

(set, string) → string

call

join(commits.titles, ', ')

duration

Creates a duration from its expression. A duration expression is a number followed by a time unit (min, hour, day).

string → duration

call

duration('2 hours')

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.

Attributes labelled with the lightning emoji ⚡️ can be used in the retrigger-on section

Condition Attribute

Type

Meaning

title ⚡️

string

The pull request title

description ⚡️

string

The pull request description

source ⚡️

string

Source branch of pull request

destination ⚡️

string

Destination branch of pull request

repository-name ⚡️

string

Destination repository of pull request

source-head-sha ⚡️

string

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

draft ⚡️

boolean

Is it a draft pull request

conflicts⚡️

boolean

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

author

since 8.6

user

The user who created the PR.

age

since 8.11

duration

Time since the pull request was opened. Conditions involving the age can be configured via a schedule.

open-tasks

set

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

approved-by

set

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

reviewers

set

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

changed-files

since 8.6

set

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

builds

since 8.6

set

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

watchers

since 8.7

set

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

commits

since 8.7

list

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

jira-keys

since 8.7

set

Set of Jira keys mentioned in all commit messages.

Attribute Properties 🔬

Some attributes have properties, (for use in property access expressions) that provide a view over the contained data.

attribute

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