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 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>() {
    @Override
    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>
    .join("\n")

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, and you should review them:

$publicReposContent

The result should look similar to the following:


These repositories are public, and you should review them:

PROJECT_1/aPublicRepo
PROJECT_5/anotherPublicRepo

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()
    .state(PullRequestState.MERGED)
    .closedSince(yesterday)
    .toRepositoryId(repo.id)
    .build()

def mergedPullRequests = new PagedIterable<PullRequest>(new PageProvider<PullRequest>() {
    @Override
    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 must 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:

$content


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

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