Who we are

We are the developers of Plastic SCM, a full version control stack (not a Git variant). We work on the strongest branching and merging you can find, and a core that doesn't cringe with huge binaries and repos. We also develop the GUIs, mergetools and everything needed to give you the full version control stack.

If you want to give it a try, download it from here.

We also code SemanticMerge, and the gmaster Git client.

Plastic SCM DevOps: Custom mergebots

Wednesday, October 10, 2018 Miguel , 0 Comments

We designed our DevOps ecosystem for Plastic SCM with versatility in mind. All functionality is provided by separate pieces of software, each one with a specific purpose: plugs to perform actions and mergebots to orchestrate the process. In this guide, we'll be looking at the mergebots and their general structure so you can implement your own!

What is a mergebot?

We define a mergebot as an independent program that connects to a Plastic SCM server through WebSockets, where it registers itself and stands by for the event notifications that Plastic SCM emits.

The main goal of a mergebot is to effectively lead the integration process in an event-driven way. It can (and should) make use of the available plugs to perform the necessary checks and actions to implement the integration that suits your teams development methodology.

A great example of a mergebot is our TrunkBot, a mergebot that enforces Trunk-Based Development in a Plastic SCM repository. You can take a look at its source code in our GitHub repository.

Mergebot types

Note: From this point on, we'll use ${SERVER_DIR} to reference %PROGRAMFILES%\PlasticSCM5\server in Windows or /var/lib/plasticscm in macOS or Linux, and ${DEVOPS_DIR} to reference ${SERVER_DIR}/devops.

The mergebot type definition is a simple JSON document like this one:

{
  "name" : "custombot",
  "type" : "mergebot",
  "displayName": "Custom Bot",
  "description": "This bot is just what we need to keep things running",
  "command": "/path/to/my/custombot.exe --my-non-generic-param",
  "template": "/path/to/my/custombot.config.template"
}

It contains the plug description in a human-readable way, the plug executable file path and the plug template path.

This is what it would look like in WebAdmin:

mergebot type configuration

Let's see what each of these parameters mean.

  • name is the internal identifier of the mergebot inside the Plastic SCM Devops system. Ensure it's unique!
  • type tells Plastic SCM what kind of element this is. It should always be set to mergebot.
  • displayName is the human readable name for your mergebot that displays in the Plug Types page in the WebAdmin.
  • description provides additional information about your plugin to administrators browsing the available plug types list.
  • command defines the program to start and any additional command line arguments that it requires. All mergebot programs must support a particular set of parameters (more on that here), but any additional parameters required by your custom mergebot can be saved here.
  • template sets the path to find the mergebot configuration template.

If you don't want to manually create the mergebot type file yourself, you can also add new custom mergebot types using the WebAdmin, at http://your.plasticscm.server.com:7178/devops/mergebots/types/custom/new (replace the host with your actual instance hostname). Otherwise, ensure you store your brand-new mergebot type file inside ${DEVOPS_DIR}/config/mergebots/available. We name these files ${mergebotName}.definition.conf to keep it organized, but any other name will do. For example, we could have named the type definition file of custombot as custombot.definition.conf.

When it's ready, it will display in the available mergebot types list:

mergebot types

Mergebot configuration templates

You're not forced to define any particular configuration parameters for your custom mergebot, but chances are that you'll need to have some parameters particular to your workflow that allow you to change the mergebot behavior without recompiling its sources. For instance, you might like to have the ability to change the target Plastic SCM repository (or repositories) on the fly, or have a list of build stages to enable/disable.

If that's the case, then it's very useful to let the Admin enter those parameters using a UI instead of having to type a config file.

That's exactly what the configuration templates are for: a way to define the parameters your mergebot needs so that WebAdmin knows how to render a UI to let the Admin enter the right config.

The WebAdmin will use the configuration template to render a UI, but also to generate a JSON configuration file to send to your custom mergebot on start up so it can process it accordingly.

The template itself is a JSON document whose root is an array of parameter objects. Look at this example:

[
  {
    "name": "server",
    "displayName": "Server",
    "type": "plastic_server",
    "description": "Plastic SCM server."
  },
  {
    "name": "repository",
    "displayName": "Repository",
    "type": "string",
    "description": "The repository that the bot will monitor."
  },
  {
    "name": "trunk_branch_name",
    "displayName": "Trunk branch",
    "type": "string",
    "description": "The stable development branch name."
  },
  {
    "name": "dev_branch_name",
    "displayName": "Dev branch",
    "type": "string",
    "description": "The latest development branch name."
  },
  {
    "name": "bot_user",
    "displayName": "Bot User",
    "type": "user_api_key",
    "description": "The API Key of the Plastic SCM user that the mergebot will use to checkin and perform API calls."
  },
  {
    "name":"plastic_config",
    "displayName":"Plastic SCM configuration",
    "type":"group",
    "description":"",
    "items":[
      {
        "name": "attr_name",
        "displayName": "Branch status attribute name",
        "type": "string",
        "description": "Branch attribute name to store the lifecycle stage."
      },
      {
        "name": "resolved_value",
        "displayName": "Resolved status value",
        "type": "string",
        "description": "Start processing branches that have their attribute set to this value."
      },
      {
        "name": "failed_value",
        "displayName": "Failed status value",
        "type": "string",
        "description": "Set the branch attribute to this value if build fails."
      },
    ]
  },
  {
    "name": "issues_config",
    "displayName": "Issue Tracker Integration",
    "type": "group",
    "description":"All fields are case-sensitive!",
    "items":[
      {
        "name": "plug",
        "displayName": "Plug",
        "type": "issuetracker_plug",
        "description": "Select a plug to interact with your Issue Tracker."
      },
      {
        "name": "project_key",
        "displayName": "Project key",
        "type": "string",
        "description": "Key of the issue tracker project."
      }
    ]
  },
  {
    "name": "ci_config",
    "displayName": "CI Integration",
    "type": "group",
    "description": "",
    "items": [
      {
        "name": "plug",
        "displayName": "Plug",
        "type": "ci_plug",
        "description": "Select a plug to launch builds."
      },
      {
        "name": "plan",
        "displayName": "Plan",
        "type": "string",
        "description": "The name of the plan to run to test a task."
      }
    ]
  },
  {
    "name": "notifier_group",
    "displayName": "Notifications",
    "type": "group",
    "description": "",
    "items": [
      {
        "name": "plug",
        "displayName": "Plug",
        "type": "notifier_plug",
        "description": "Select a plug to notify the results."
      },
      {
        "name": "recipient",
        "displayName": "Notify to:",
        "type": "string",
        "description": "Email address to notify the integration results."
      }
    ]
  }
]

Each parameter has these attributes available:

  • name is the key of the current parameter. It should be unique to prevent collisions.
  • type can be one of these:
    • bool for yes/no values.
    • int for numeric values.
    • email for text in email format.
    • string for text.
    • password for sensitive information.
    • group to organize child parameters hierarchically.
    • user_api_key to reference an existing User API Key (more info here).
    • plastic_server is a regular text field that gets automatically filled with the Plastic SCM Server hostname and port in the WebAdmin form.
    • issuetracker_plug to reference an existing Issue Tracker plug configuration by name.
    • ci_plug
    • to reference an existing Continuous Integration plug configuration by name.
    • notifier_plug to reference an existing Notifier plug configuration by name.
  • displayName contains the human readable name of the parameter.
  • description adds detailed information about the parameter that displays in the configuration form.
  • items is only allowed for group types and stores an array of nested configuration parameters.

The WebAdmin will create an actual configuration file (JSON) based on the template you defined plus the actual values entered by the administrator. See the following section to see an example of a config file.

We recommend that you include these config template files in the directory where the mergebot executable is, using the format ${mergebotName}.config.template. In our example, that would be /path/to/my/custombot.config.template.

Creating mergebot configurations

The Plastic SCM Server allows administrators to configure individual instances of a given mergebot, based on the mergebot type definitions we discussed above. They are JSON files with fields that set concrete values of the generic type properties. They can be created through the WebAdmin (http://your.plasticscm.server.com:7178/devops/mergebots/new) or manually, placing them in ${DEVOPS_DIR}/config/mergebots/start.

Here's an example of a mergebot configuration based on the template introduced above:

{
  "name": "custombot-top-secret",
  "description": "Don't tell anybody about this!",
  "type": "custombot",
  "apikey": "82B3622691DF82D52EC310E441086C73023561755A499E07C606FF41204AF822",
  "start": "yes",
  "type": "IssueTracker",
  "configuration": {
    "server": "my.plasticscm.server.com:8087",
    "repository": "topSecretRepo",
    "trunk_branch_name": "/main",
    "dev_branch_name": "/dev",
    "bot_user": "CC75A440E4FEDCF6A71D1FC5F84E0A36AEE2C80DEB7476D7422F6508D416EC6B",
    "plastic_config": {
      "attr_name": "task-status",
      "resolved_value": "done",
      "failed_value": "failed"
    },
    "issues_config": {
      "plug": "myjiraplug",
      "project_key": "VIP"
    },
    "ci_config": {
      "plug": "myjenkinsplug",
      "plan": "top-secret"
    },
    "notifier_group": {
      "plug": "myemailplug",
      "recipient": "build.results@mycompany.com"
    }
  }
}

Where:

  • name is the key of the configuration. Avoid duplicates in your mergebot configurations!
  • description gives some extra details about the bot configuration.
  • type is the mergebot type of this mergebot configuration.
  • apikey sets the authentication key for the Plastic SCM Server API used. See more information here.
  • start is a toggle value ("yes", "no", true or false) indicating whether this mergebot configuration should run on server startup.
  • configuration contains the configuration values in an object as specified by the mergebot type configuration template.

Before running a mergebot configuration, the Plastic SCM Server extracts the value of the configuration property and writes it to a temporary file. Its path is then passed as a command line argument.

This is the temporary file generated by the Plastic SCM Server from the mergebot configuration above:

{
  "server": "my.plasticscm.server.com:8087",
  "repository": "topSecretRepo",
  "trunk_branch_name": "/main",
  "dev_branch_name": "/dev",
  "bot_user": "CC75A440E4FEDCF6A71D1FC5F84E0A36AEE2C80DEB7476D7422F6508D416EC6B",
  "plastic_config": {
    "attr_name": "task-status",
    "resolved_value": "done",
    "failed_value": "failed"
  },
  "issues_config": {
    "plug": "myjiraplug",
    "project_key": "VIP"
  },
  "ci_config": {
    "plug": "myjenkinsplug",
    "plan": "top-secret"
  },
  "notifier_group": {
    "plug": "myemailplug",
    "recipient": "build.results@mycompany.com"
  }
}

And this is what it would look like in WebAdmin:

mergebot configuration

API keys

There are two types of API keys to have in mind for mergebots. The first one is the connection API key, mandatory for each mergebot configuration, that allows a mergebot configuration to authenticate its messages in the websocket connection. This is the same concept we saw in the Custom Plug Guide.

These keys are just random strings of hexadecimal numbers that are used in the server to authenticate valid incoming mergebot registration requests. They're stored at ${DEVOPS_DIR}/config/apikeys/conn.

They are generated in the background when you create a new mergebot configuration through the WebAdmin, so they aren't displayed in the UI. However, you can generate your own, bypassing the server. Their file names should be ${key}.key and their JSON contents are like this:

{
  "key": "82B3622691DF82D52EC310E441086C73023561755A499E07C606FF41204AF822"
}

Mergebots store the connection API keys as a required property of the JSON mergebot configuration object.

User API Keys

The other API key for mergebots that we introduced in the configuration above (configuration type user_api_key, name bot_user) is slightly different. They are still random strings of hexadecimal characters that your mergebot will use to authenticate its requests to the server. However, because mergebots are intended to perform actions in the Plastic SCM repositories, the API keys need to be linked to an existing Plastic SCM user.

As a result, when a mergebot authenticates its actions with a User API Key, the server will take the specified user name and run the operation with their credentials.

You'll notice a New button next to the API key field when you create a new mergebot configuration in the WebAdmin. It takes you to http://your.plasticscm.server.com:7178/devops/mergebots/new, where you are prompted to enter a user name for the new API key.

Alternatively, you can create the API key by hand. They're stored at ${DEVOPS_DIR}/config/apikeys/user. Their file names should also be ${key}.key and their JSON contents are like this:

{
  "key": "CC75A440E4FEDCF6A71D1FC5F84E0A36AEE2C80DEB7476D7422F6508D416EC6E",
  "seid": "bot-user"
}

Please note that the key includes a seid property. This means that servers using LDAP as authentication backend will store the user ID instead of their readable name! Keep that in mind if you decide to create the User API Keys yourself.

Mergebot implementation

Now, it's time to tackle the actual mergebot implementation! The most important thing to keep in mind is that mergebots can be implemented in any programming language, so choose your favorite one! Just ensure that you have an appropriate library to provide you with websocket connectivity.

Command line arguments

Every mergebot is expected to admit five arguments. These contain information stored in the server when a mergebot configuration needs to start. This is a sample execution of a mergebot configuration, started from the Plastic SCM Server:

/path/to/my/custombot.exe \
    --my-non-generic-param
    --websocket wss://plasticscm.server.net:7111/plug
    --restapi http://plasticscm.server.net:7178
    --name custombot-top-secret
    --apikey 82B3622691DF82D52EC310E441086C73023561755A499E07C606FF41204AF822
    --config /tmp/13b1e08d-ac9f-4788-bdaa-650ae4f9cf25.conf
  • --websocket is the websockets endpoint in the Plastic SCM Server. The mergebot must connect to it and register itself later on.
  • --restapi is the base URL of the Plastic SCM Server REST API.
  • --name is the name of the configuration as set by the administrator that created it.
  • --config sets the path to find the configuration file. It contains the mergebot configuration in the format defined by the mergebot template.
  • --apikey is the string used to authenticate the requests sent by the mergebot in the registration stage.

As we mentioned before, if your mergebot needs some other parameters beyond these ones, you can set those in the command property of the mergebot type JSON file. In this case, we used --my-non-generic-param to illustrate that.

It's also advisable to allow a -h or --help parameter in case you'd like to manually launch it and you don't have the time to read this guide.

Mergebot logging

If your custom mergebot is generating log information (and it should!), place the log files in ${DEVOPS_DIR}/logs/{mergebot-type-name}.{mergebot-configuration-name}.log.

The Plastic SCM Server will also store the PID of the running plug at ${DEVOPS_DIR}/run/${mergebot-configuration-name}.pid.

Registering the mergebot

The first thing your custom mergebot should do is check that the received command line arguments and the configuration found at the specified file (passed with --config) are all valid.

Then, move on to the websocket connection. Use the websocket URI passed as a command line argument and connect to it. Plastic SCM Server expects a Login message followed by a RegisterTrigger message when establishing a new mergebot websocket connection. In both cases, the message contents are JSON objects.

The login message includes the API key to authenticate:

{
  "action": "login",
  "key": "CC75A440E4FEDCF6A71D1FC5F84E0A36AEE2C80DEB7476D7422F6508D416EC6B"
}

The RegisterTrigger message lets your mergebot subscribe to a defined list of Plastic SCM events, receiving a message whenever they are triggered:

{
  "action": "register",
  "type": "trigger",
  "eventlist": [
    "newChangesets",
    "branchAttributeChanged"
  ]
}
The type property value is fixed to trigger in this case. The eventlist parameter should contain a list of the events that you want the Plastic SCM server to notify your mergebot. In this example, it's limited to the two values you see in the example above: newChangesets and branchAttributeChanged.

At this point, your mergebot is successfully registered in the Plastic SCM Server and it should be standing by for event notifications!

Reconnection

You might consider adding some internal logic to your mergebot so it can automatically reconnect to the server if the connection is broken at some point!

Handling requests

All incoming messages from the server to a mergebot will arrive through the websocket connection and they'll contain a JSON payload like this one:

{
  "type": "trigger",
  "event": "newChangesets",
  // event parameters, different for each event type
  "properties": {
    "repository": "topSecretRepo",
    "branch": "scm3235"
  }
}

Plastic SCM event notifications are fire-and-forget, so your custom mergebot doesn't need to do anything besides processing the incoming message.

Now, let's dive into the event types and properties!

NewChangesets

This event is triggered whenever a new changeset is checkedin to a server. It contains the repository and branch name where the event was triggered.

Example:

{
  "type": "trigger",
  "event": "newChangesets",
  "properties": {
    "repository": "topSecretRepo",
    "branch": "/main/scm3235"
  }
}

BranchAttributeChanged

This event is triggered whenever a branch has a new attribute applied to it or one of the previously applied attributes has its value changed. It contains the necessary information to describe the action.

Example:

{
  "type": "trigger",
  "event": "newChangesets",
  "properties": {
    "repository": "topSecretRepo",
    "branchId": 354899,
    "branchFullName": "/main/scm3235",
    "branchOwner": "kerrigan",
    "branchComment": "Apply materials to models",
    "attributeName": "task-status",
    "attributeValue": "done"
  }
}

Plastic SCM REST API

When you need to perform queries or actions in Plastic SCM as part of the event handling, that's when the Plastic SCM REST API comes into play. We're preparing thorough documentation for it when it's ready to release, but in the meantime, you can rely on these endpoints that we've consolidated for the DevOps environment.

Authentication

All Plastic SCM REST API requests must be authenticated using a User API Key (see here for more details). To accomplish that, simply set the Authorization HTTP header to ApiKey ${your-user-api-key}.

Using the example of custombot-top-secret, your HTTP Header should look like this:

Authorization: ApiKey 82B3622691DF82D52EC310E441086C73023561755A499E07C606FF41204AF822

GetBranch

Retrieves the information of a given branch.

URL: /api/v1/repos/:repoName/branches/:branchName

Method: GET

Request

  • repoName is the name of the target repository.
  • branchName is the name of the branch you're requesting.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/branches/main/scm3235

Response

{
  "id": 354899,
  "name": "/main/scm3235",
  "repositoryId": "5",
  "headChangeset": 15673,
  "date": "2018-09-11T17:43:25Z",
  "owner": "kerrigan",
  "comment": "Apply materials to models",
  "type": "branch"
}

GetChangeset

Retrieves the information of a given changeset.

URL: /api/v1/repos/:repoName/changesets/:changesetId

Method: GET

Request

  • repoName is the name of the target repository.
  • changesetId is the ID number of the changeset you're requesting.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/changesets/15673

Response

{
  "id": 354899,
  "changesetId": 15673,
  "repositoryId": "5",
  "parentChangesetId": 15668,
  "branch": "/main/scm3235",
  "date": "2018-09-27T11:18:44Z",
  "guid": "b4d072a1-986d-456f-87e5-b9ce9e2eb1bb",
  "owner": "kerrigan",
  "comment": "Refactor long method into many detailed ones",
  "type": "changeset"
}

GetAttributeValue

Retrieves the value of an attribute applied to an object.

URL: /api/v1/repos/:repoName/attributes/:attributeName/:targetType/:targetName

Method: GET

Request

  • repoName is the name of the target repository.
  • attributeName is the name of the target attribute.
  • targetType is the type of the target object (branch, changeset or label).
  • targetName is the identifier (branch name, changeset ID or label name) of the target object.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/attributes/task-status/branch/main/scm3235

Response

{
  "value": "done"
}

Find

Queries a repository in search of objects.

URL: /api/v1/repos/:repoName/find?query=query&queryDateFormat=dateFormat&fields=fields

Method: GET

Request

  • repoName is the name of the target repository.
  • query contains the query string just like the cm find command does.
  • queryDateFormat sets the format of the dates returned in the result objects (see here for more info).
  • fields is a comma-separated list of the fields that you want to be returned. Object fields that aren't in this list are omitted.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/find?query=branches where name = 'scm3235'&queryDateFormat=yyyy-MM-dd hh:mm:ss&fields=id,name,date

Response

[
  {
    "id": 354899,
    "name": "/main/scm3235",
    "date": "2018-09-11 17:43:25"
  },
  {
    "id": 355367,
    "name": "/hotfix/scm3235",
    "date": "2018-09-13 10:05:33"
  }
]

GetUserProfile

Retrieves the profile information for a given user.

URL: /api/v1/users/:userName/profile

Method: GET

Request

  • repoName is the name of the target repository.
  • changesetId is the ID number of the changeset you're requesting.

Example: http://my.plastic.server.com:7178/api/v1/users/kerrigan/profile

Response

The response contents will depend on the profile template definition in your server. That template is found in ${SERVER_DIR}/profiles/profiletemplate.conf.

{
  "email": "kerrigan@char.ufo",
  "slack": {
    "username": "Queen of Blades",
    "notifications": {
      "on_build_failed": "true",
      "on_tests_failed": "false"
    }
  }
}

MergeTo

Merges a branch into another branch.

URL: /api/v1/repos/:repoName/mergeto

Method: POST

Request

  • repoName is the name of the target repository.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/mergeto

Body example:

{
  "sourceType": "15673",
  "source": "changeset",
  "destination": "/main",
  "comment": "Integrate task 3235",
  "createShelve": true,
  "ensureNoDstChanges": false,
}
  • sourceType can be branch, shelve, changeset or label.
  • source is a string containing the source object ID or name.
  • destination is the merge destination branch name.
  • comment holds the text to insert as a comment in the resulting changeset.
  • createShelve means whether or not the server will checkin the merge into a shelve or a regular changeset.
  • ensureNoDstChanges will force the merge-to operation to take place only if the merge destination changeset is also the base contributor.

Response

{
  "status": "ok",
  "message": "",
  "changesetNumber": -5931 // shelves have negative changeset numbers
}
  • status can be ok, ancestorNotFound, mergeNotNeeded, conflicts, destinationChanges, error or multipleHeads.
  • message has a value only when status is error, destinationChanges or conflicts. It contains a description of why the operation was unsuccessful.
  • changesetNumber contains the ID of the changeset created as a result of the merge-to operation. It'll be negative if the createShelve property was set to true in the request.

ChangeAttributeValue

Applies an attribute to an object with the specified value.

URL: /api/v1/repos/:repoName/attributes/:attributeName

Method: PUT

Request

  • repoName is the name of the target repository.
  • attributeName is the name of the target attribute.

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/attributes/task-status/branch/main/scm3235

Body example:

{
  "targetName": "/main/scm3235",
  "targetType": "branch",
  "value": "failed"
}
  • targetType can be branch, changeset or label.

Response

200 OK

UploadMergeReport

Uploads the results of a merge to the Plastic SCM Server. This updates the statistics displayed on the WebAdmin. The properties included in the upload can be customized.

URL: /api/v1/mergereports/:mergebotName

Method: PUT

Request

  • mergebotName is the name of the mergebot that uploads the data.

Example: http://my.plastic.server.com:7178/api/v1/mergereports/custombot-top-secret

Failed merge example:

{
  "timestamp": "2018-09-11T17:43:25Z",
  "repositoryId": "5",
  "branchId": 354899,
  "properties": [
    {
      "text": "Fix main scene materials",
      "link": "https://myboard.atlassian.net/",
      "type": "issuetracker"
    },
    {
      "text": "conflicts",
      "type": "merge_failed",
      "value": "The \"Merge-to\" operation cannot be executed because the merge from changeset cs:4@wololo to branch br:/trunk has conflicts.\r\nIt is neccesary to run a \"Merge from\" operation from a workspace to resolve those conflicts."
    }
  ]
}

Failed build example:

{
  "timestamp": "2018-09-11T17:43:25Z",
  "repositoryId": "5",
  "branchId": 354899,
  "properties": [
    {
      "text": "Fix main scene materials",
      "link": "https://myboard.atlassian.net/",
      "type": "issuetracker"
    },
    {
      "text": "ok",
      "type": "merge_ok"
    },
    {
      "text": "build time (min)",
      "type": "number",
      "value": "0.96"
    },
    {
      "text": "build ko (plan: test)",
      "type": "build_failed",
      "value": ""
    }
  ]
}

Successful build example:

{
  "timestamp": "2018-09-11T17:43:25Z",
  "repositoryId": "5",
  "branchId": 354899,
  "properties": [
    {
      "text": "Fix main scene materials",
      "link": "https://myboard.atlassian.net/",
      "type": "issuetracker"
    },
    {
      "text": "ok",
      "type": "merge_ok"
    },
    {
      "text": "build time (min)",
      "type": "number",
      "value": "17.33"
    },
    {
      "text": "build ok (plan: test)",
      "type": "build_ok"
    }
  ]
}

The available fields for the properties array are:

  • type: defines the type of the property. It can be one of these:
    • issuetracker for issue tracking information.
    • merge_ok for successful merges.
    • merge_failed for unsuccessful merges.
    • build_ok for successful builds.
    • build_failed for unsuccessful builds.
    • number for numeric values, useful for stats.
  • text: contains a readable value depending on the property type.
    • If type is issuetracker, it contains the issue summary.
    • If type is merge_ok or merge_failed, it contains the merge result.
    • If type is build_ok or build_failed, it contains the build result message.
    • If type is number, it contains the numeric value name.
  • value: contains a detailed value for the property, depending on its type.
    • If type is merge_failed or build_failed, it contains a description of the error.
    • If type is number, it contains the actual value.
    • Otherwise, it's empty.
  • link: only used in issuetracker properties; it contains a URL to the issue tracker page for the issue.

Response

200 OK

DeleteShelve

Removes a shelve from the repository.

URL: /api/v1/repos/:repoName/shelves/:shelveId

Method: DELETE

Request

  • repoName is the name of the target repository.
  • shelveId is the ID of the target shelve (don't use negative numbers here).

Example: http://my.plastic.server.com:7178/api/v1/repos/topSecretRepo/shelves/5931

Response

200 OK

Errors

Whenever a request is incorrect or something goes wrong processing it in the Plastic SCM Server, you'll get a response with the appropriate result code and a JSON object in the body like this:

{
  "error": {
    "message": "The repository 'fakeRepo' was not found."
  }
}

Using plugs

It would be entirely possible for you to manually handle the Plastic SCM Server events and code the resulting actions yourself. But why do all the job when you can rely on the Plastic SCM DevOps plugs? You can query and edit issues in your Issue Tracker, launch builds in the Continuous Integration system of your choice and also use a Notifier plug to let your fellow devs know about the build results.

Issue Tracker REST API

These REST endpoints provide information about issues and allow the mergebot to modify them.

IsConnected

Tells whether the issue tracker system of the specified plug is available.

URL: /api/v1/issues/:plugName/checkconnection

Method: GET

Request

  • plugName is the name of an issue tracker plug configuration.

Example: http://my.plastic.server.com:7178/api/v1/issues/myjiraplug/checkconnection

Response

{
  "value": "false",
}

GetIssueUrl

Returns the URL of the specified issue to review it in a browser.

URL: /api/v1/issues/:plugName/:projectKey/:issueId

Method: GET

Request

  • plugName is the name of an issue tracker plug configuration.
  • projectKey is the key of the project where the issue is stored.
  • issueId is the identifier of the issue.

Example: http://my.plastic.server.com:7178/api/v1/issues/myjiraplug/VIP/3235

Response

{
  "value": "https://myboard.atlassian.net/browse/VIP-3235",
}

GetIssueField

Returns the value of a given field for the specified issue.

URL: /api/v1/issues/:plugName/:projectKey/:issueId/:fieldName

Method: GET

Request

  • plugName is the name of an issue tracker plug configuration.
  • projectKey is the key of the project where the issue is stored.
  • issueId is the identifier of the issue.
  • fieldName is the name of the issue field.

Example: http://my.plastic.server.com:7178/api/v1/issues/myjiraplug/VIP/3235/status

Body example:

{
  "newValue": "To Do",
}

Response

200 OK

SetIssueFieldValue

Sets the value of a given field for the specified issue.

URL: /api/v1/issues/:plugName/:projectKey/:issueId/:fieldName

Method: PUT

Request

  • plugName is the name of an issue tracker plug configuration.
  • projectKey is the key of the project where the issue is stored.
  • issueId is the identifier of the issue.
  • fieldName is the name of the issue field.

Example: http://my.plastic.server.com:7178/api/v1/issues/myjiraplug/VIP/3235/status

Response

{
  "value": "In Progress",
}

Continuous Integration REST API

These REST endpoints allow the mergebot to interact with continuous integration systems to start new builds and retrieve their status.

GetPlanStatus

Returns the status information of one of the builds of a given plan in the targeted continuous integration plug.

URL: /api/v1/ci/:plugName/:planName/:buildId

Method: GET

Request

  • plugName is the name of a continuous integration plug configuration.
  • planName is the name of the plan to query.
  • buildId is the identifier of the build to retrieve its status.

Example: http://my.plastic.server.com:7178/api/v1/ci/myjenkinsplug/top-secret

Response

{
  "isFinished": true,
  "succeeded": false,
  "explanation": "Build stage 'engine' failed."
}
  • isFinished is true if the build finished already, otherwise false.
  • succeeded is true if the build completed successfully, otherwise false.
  • explanation contains details about why the build failed.

LaunchPlan

Starts a build of a given plan in the targeted continuous integration plug.

URL: /api/v1/ci/:plugName/:planName/:buildId

Method: POST

Request

  • plugName is the name of a continuous integration plug configuration.
  • planName is the name of the plan where a build should be started.

Example: http://my.plastic.server.com:7178/api/v1/ci/myjenkinsplug/top-secret

Body example:

{
  "objectSpec": "cs:15673@topSecretRepo@my.plasticscm.server.com:8087",
  "comment": "CustomBot - Releasing branch /main/scm3235",
  "properties": {
    "branchHead": "15673",
    "branchName": "/main/scm3235",
    "branchOwner": "kerrigan",
    "taskNumber": "3235",
  }
}
  • objectSpec contains the spec to be set in the continuous integration agent workspace.
  • comment is used to describe the launched build.
  • properties is a dictionary of string key-value pairs to pass additional parameters to the build.

Response

{
  "value": "216",
}

Notifier REST API

This REST endpoint implements the notification features of the Plastic SCM DevOps environment.

NotifyMessage

Sends a message to a list of recipients.

URL: /api/v1/notify/:plugName

Method: POST

Request

  • plugName is the name of a notifier plug configuration.

Example: http://my.plastic.server.com:7178/api/v1/notify/myemailplug

Body example:

{
  "message": "Branch /main/scm3235 was merged correctly to /main.",
  "recipients": [
    "kerrigan@char.ufo",
    "qa.department@dut.gov",
    "build.results@mycompany.com"
  ]
}
  • message is the actual message to send to the recipients.
  • recipients is the notification list of recipient names or addresses.

Response

200 OK

Working example

If you're considering implementing your own custom mergebot, we strongly recommend that you to have a look at the TrunkBot source code. It's available in a public GitHub repository.

Have a look at the README description. There you'll learn how the TrunkBot behaves and it will provide you insight on how to implement your own custom mergebot!

Wrapping up

We've gone through all the required steps to build a custom mergebot. Mergebots enable you and your team to automatize the DevOps cycle that best suits your team. There are endless possibilities for devops engineers and mergebots enable them to tune their processes according to their needs! The code for our built-in mergebots is already published; use it as a reference to develop your own.

Besides that, remember that you can contact us any time in our forum or at support@codicesoftware.com. Don't get stuck if you bump into any issues or you're unsure about how to proceed at any point! Let us help!

And remember: release early, release often!

Miguel González
Prior to become a Plastic hard-core developer, I worked in a consulting firm in France where I also finished his Computing Engineering master's degree.
I'm a Linux enthusiast (I was the one developing the Plastic SCM linux packages), heavy-metal guitar player on a band, LP collector, youtube expert and talented Plastic hacker.
You can find me at @TheRealMig_El.

0 comentarios: