Skip to content

Doing More with Goals

Here are some more things you can do with goals:

Prepare the checked out code

Any built-in goal, plus any goal created with the goal function, implements FulfillableGoal. These can have GoalProjectListeners.

Say you want to run tests as a separate goal. But you have to run a build before the tests can run. By default, the goal will execute in a fresh checkout of the repository (caveat: in local mode, it may reuse a cached checkout). Your test goal wants to run tests, it doesn’t really want to run a build; it wants that to have run already. Separately, you also want an integration-test goal and it also needs a build beforehand.

Encapsulate that “thing that needs to happen before the goal runs” in a GoalProjectListener.

Create a GoalProjectListener

A GoalProjectListener function accepts a Project, a GoalInvocation, and a GoalProjectListenerEvent (“before” or “after”). The important part is the Project; this is the checked-out repository that needs built (or whatever preparation you are encapsulating).

const BuildCheckedOutCode: GoalProjectListener = async (project, inv, event) => {
        if (!await project.hasDirectory("site")) {
            return { code: 0, message: "Looks OK, site directory already exists" };
        // do the build or whatever is needful

Create a GoalProjectListenerRegistration

A GoalProjectListenerRegistration object defines a name and when to run the GoalProjectListener.

The name field will appear in the description of the goal while it’s running, inside push events. The events array determines whether this runs before or after a goal executes (default is both).

const BuildCheckedOutCodeFirst: GoalProjectListenerRegistration = {
    name: "build the code",
    events: [GoalProjectListenerEvent.before],
    listener: BuildCheckedOutCode,

Register the GoalProjectListener on goals

Any FulfillableGoal can accept a GoalProjectListenerRegistration with its withProjectListener method. Call this when you create the goal. For instance, here is a possible custom test goal:

    const myTest = goal(
        { displayName: "Tests" },

Reset goals

Your SDM can have a command to reset goals on the last commit. Add the goalState extension pack to your SDM:

import { goalState } from "@atomist/sdm-core";


Then send @atomist reset goals <name of your SDM>, where “name of your sdm” is the name property in package.json, minus any @ characters.

You can add branch=my-branch and/or sha=<40-character SHA> to change which commit gets a new set of goals.