Heads up! ScriptRunner for Bitbucket documentation has moved to https://docs.adaptavist.com/sr4bib. Adaptavist will keep this site up for a bit, but no future updates to documentation will be published here. ScriptRunner 6.20.0 will be the last release to link to scriptrunner.adaptavist.com for in-app help.

ScriptRunner Jobs allow you to run your own code at regular intervals. Some examples are:

The Jobs page displays a list of all configured jobs on the current Bitbucket instance. Click Create Job to configure a custom scheduled job and/or built-in scheduled jobs.

Most jobs take a cron expression to define the interval a job is run at.

If you’re unfamiliar with cron expressions we recommend you use the Cron Maker resource to help you build your own.

Run or Schedule User Deactivation

This scheduled job deactivates particular users at a specified date and time, preventing further access to Bitbucket Server.

Over time the people in your organization who can access Bitbucket Sever can change. For example someone leaves your company or someone in a different department is given temporary access to Bitbucket Server.

After this happens, an administrator usually has to deactivate the user manually. Scheduling the deactivation in advance can be helpful for Bitbucket admins with already jam-packed todo lists; as soon as they know the date when a user should lose access, they can schedule it and know it will happen automatically. This can free up unused seats on your license and keep you compliant with your organization’s policies and relevant regulations.

To deactivate the users all their global permissions are removed, including any groups they are a part of which will completely remove their access to Bitbucket. Unfortunately at this time Bitbucket does not provide a way to deactivate users using their API. Please see BSERV-8817 for further details on this.

In the example below we will deactivate User and Contractor on 17th August 2018 at 9am. In the notes section we have even referenced a JIRA issue key ACCESS-1234 so we can refer back to who reported this change.

deactivate users

Clicking Run now will run the job right now, so you can test (on a development instance ideally). Otherwise clicking Add will schedule the job to be run at the specified date.

After we have added the job and it has run it will be marked as expired, hovering over the expiry time will show the exact time the job was run. This means we can now go ahead and remove this job from the list as in the example below:

deactivate users expired

Run or Schedule Custom Email

This scheduled job will automatically send email notifications periodically based on a condition and email template. This is particularly useful if you want a daily summary of what has happened in Bitbucket.

You will just need to configure the interval and then provide a subject and an email template, as well as the email format (plain text or HTML) and a list of recipients.

List all public repositories

This example sends an email notification listing all the public repositories present in your Bitbucket Server instance.

The email format selected should be Plain text.

You can use the following code below in the Condition and Configuration section:

import com.atlassian.bitbucket.repository.Repository
import com.atlassian.bitbucket.repository.RepositorySearchRequest
import com.atlassian.bitbucket.repository.RepositoryService
import com.atlassian.bitbucket.util.Page
import com.atlassian.bitbucket.util.PageProvider
import com.atlassian.bitbucket.util.PageRequest
import com.atlassian.bitbucket.util.PageRequestImpl
import com.atlassian.bitbucket.util.PagedIterable
import com.atlassian.sal.api.component.ComponentLocator

import javax.annotation.Nonnull

import static com.atlassian.bitbucket.repository.RepositoryVisibility.PUBLIC

def repositoryService = ComponentLocator.getComponent(RepositoryService)

def publicRepositories = new PagedIterable<Repository>(new PageProvider<Repository>() {
    Page<Repository> get(@Nonnull PageRequest pageRequest) {
        repositoryService.search(new RepositorySearchRequest.Builder().visibility(PUBLIC).build(), pageRequest) (1)
}, new PageRequestImpl(0, 100))
    .collect { Repository repository -> "$repository.project.key/$repository.slug" } (2)

config.content = publicRepositories (3)

!publicRepositories.isEmpty() (4)
As this is a condition the email will only be sent if there are public repositories in your Bitbucket instance.
1 We find all public repositories
2 For each public repository we build a string containing the project key and repository slug
3 We use the config variable in the binding to store the content for use in the template below (see additional configuration section for details)
4 The condition will evaluate to true if we have any private repositories and then the email will be sent

The subject template:

List of public repositories:

The body template:

These repositories are public, you should review them:


The result should look similar to the following:

These repositories are public, you should review them:


Additional Configuration in Emails

You may notice the syntax for getting content in the template is a bit clunky, as the template engine does not allow you to use the import keyword. Rather than doing this, you can pass in a config map to the bindings for both the subject and body templates. This is done in the Mail configuration section.

A useful example of a Mail configuration section that defines config variables for all pull requests that have been merged in the last 24 hours is:

import com.atlassian.bitbucket.nav.NavBuilder
import com.atlassian.bitbucket.pull.PullRequest
import com.atlassian.bitbucket.pull.PullRequestSearchRequest
import com.atlassian.bitbucket.pull.PullRequestService
import com.atlassian.bitbucket.pull.PullRequestState
import com.atlassian.bitbucket.repository.RepositoryService
import com.atlassian.bitbucket.util.Page
import com.atlassian.bitbucket.util.PageProvider
import com.atlassian.bitbucket.util.PageRequest
import com.atlassian.bitbucket.util.PageRequestImpl
import com.atlassian.bitbucket.util.PagedIterable
import com.atlassian.sal.api.component.ComponentLocator
import groovy.xml.MarkupBuilder

import javax.annotation.Nonnull

def pullRequestService = ComponentLocator.getComponent(PullRequestService)
def repositoryService = ComponentLocator.getComponent(RepositoryService)

def projectKey = "PROJECT_1"
def repoSlug = "rep_1"

def repo = repositoryService.getBySlug(projectKey, repoSlug)

def yesterday = new Date().minus(1)

def pullRequestSearchRequest = new PullRequestSearchRequest.Builder()

def mergedPullRequests = new PagedIterable<PullRequest>(new PageProvider<PullRequest>() {
    Page<PullRequest> get(@Nonnull PageRequest pageRequest) {
        pullRequestService.search(pullRequestSearchRequest, pageRequest)
}, new PageRequestImpl(0, 100)) as Iterable<PullRequest>

def navBuilder = ComponentLocator.getComponent(NavBuilder)

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)

builder.ul {
    mergedPullRequests.each { PullRequest pullRequest ->
        def url = navBuilder.repo(repo).pullRequest(pullRequest.id).buildAbsolute()

        builder.li {
            a(href: url, pullRequest.title)

config.content = writer.toString()
config.numberMerged = mergedPullRequests.size()
You will have to change the projectKey and repoSlug variable above to match your own. The email format selected should be HTML.

The subject template:

$numberMerged PRs have been merged today

The body template:

The following pull requests were merged today:


As a result of that configuration, an email will be sent to the selected email addresses with a content similar to the following one:

send mail job result

Clicking on the links will take you to that pull request in Bitbucket.

Run or Schedule Repository Size Limit Email

This scheduled job helps you to identify all of the repositories that exceed a particular size by receiving an email listing all of the repositories that exceed a particular size.

You can find more details here about how a large repository negatively impacts the performance of Git on Bitbucket Server.

This is particularly useful as repositories grow larger and you want to identify the ones that require maintenance before they have a negative impact on your instance as detailed above.

Once you’ve identified the repositories you can use some of the approaches here to reduce the size of the repository.

Some of the identified repositories could be good candidates for having their files moved to Git LFS.

The size of the repository checked does NOT include files under Git LFS.

Have questions? Visit the Atlassian Community to connect, share, and learn with other Atlassian users and experts, including Adaptavist staff.

Ask a question about ScriptRunner for JIRA, Bitbucket Server, or Confluence.

Want to learn more? Check out courses on Adaptavist Learn, an online platform to onboard and train new users for Atlassian solutions.