GitLab CI/CD Pipeline for CloudHub 2.0 Deployment (with Auto Incrementing Version)

This document covers GitLab CI/CD Pipeline Script for deploying Mule App to CloudHub 2.0. If you are experienced in setting up CI/CD Pipeline for CloudHub 1.0 and have tried to re-use the same script for CloudHub 2.0, then you must have found that it doesn't work. The fact is that Mulesoft's CloudHub 2.0 deployments are tightly coupled with Anypoint Exchange, viz., every Mule App that is deployed in Runtime Manager must be linked with a corresponding instance in Anypoint Exchange.

One common hurdle that Mule Developers face when setting up CloudHub 2.0 specific pipelines is how Mule App's version can be automatically incremented whenever a pipeline is started. This is again due to CloudHub 2.0 being tightly coupled with Anypoint Exchange. This document also covers logic to handle that in POM file.

Let’s begin...

Pre-requisites

  • Mule App
  • GitLab Account with available Runners & configured Deployment/Environment Variables
  • Connected App in Anypoint Platform with Correct Permissions

Pipeline Script

GitLab Pipeline Steps

Following is an example .gitlab-ci.yml script. In the variables section, there is a predefined CI/CD variable $CI_PIPELINE_IID which is build/pipeline number in GitLab (If you are working in BitBucket, then the equivalent predefined variable there is $BITBUCKET_BUILD_NUMBER) It keeps on incrementing whenever a pipeline is started. We pass that to our Mule code's pom.xml file while invoking the mvn command, and it takes care of our auto-incrementing hurdle. Please note that all other CI/CD variables in this script are defined in the GitLab Deployment/Environment variables. Learn more about GitLab variables here - Link.

This script has two important steps.

First, in the publish to exchange step, the Mule App is published to Anypoint Exchange with a mule-application-example classifier. Because of this classifier, we don't have to pass all deployment variables in the mvn command.

Second, in the deployment step, Mule App gets deployed to Runtime Manager CloudHub 2.0 with a mule-application classifier. Here, the application needs to be in its full form, and hence it will require all deployment variables in the mvn command.

variables:
 MAVEN_OPTS:
  -Dpipeline_uid=$CI_PIPELINE_IID
  -Dmaven.repo.local=.m2/repository
  -Dconnectedapp_client_id=$AP_CONNECTED_APPS_CLIENT_ID
  -Dconnectedapp_client_secret=$AP_CONNECTED_APPS_CLIENT_SECRET
  -Dcloudhub_target=$CH_TARGET
  -Dreplicas=$CH_NO_OF_REPLICAS
  -Dv_cores=$CH_VCORES

image: maven:3.8.6-eclipse-temurin-11

cache:
 paths:
  - .m2/repository

publish to exchange:
 stage: .pre
 script:
  - echo "Publish To Exchange"
  - mvn clean deploy $MAVEN_OPTS -e -s settings.xml -Dmule_app_classifier="mule-application-example" -Danypoint_platform_environment=$PIPE_VAR_CH_ENV -DskipTests

deployment:
 stage: deploy
 script:
  - echo "Maven Deploy To Cloudhub 2.0 Starts"
  - mvn deploy $MAVEN_OPTS -e -s settings.xml -DmuleDeploy -Dmule_app_classifier="mule-application" -Danypoint_platform_environment=$PIPE_VAR_CH_ENV -Dmule_env=$PIPE_VAR_MULE_ENV -DskipTests
 artifacts:
  name: "app-snapshot"
  paths:
   - target/*.jar

POM File Snippet

Corresponding to the pipeline script above, we setup our pom.xml file as shown below. We make the content of <version> tag a variable deployment.version. This may add a warning to the logs when the mvn command executes, but that's alright. The value of this variable will be picked from the properties section.

In this example, the deployment.version variable contains static MAJOR and MINOR versions, while the PATCH version is dynamic as it is defined using another variable pipeline_uid. This is done so that the Mule App can be deployed locally without any errors. When pipeline is started, this value gets overridden in the mvn command arguments, and this is how the version is incremented automatically.

<groupId>organization-id</groupId>
<artifactId>mule-app-name</artifactId>
<version>${deployment.version}</version>
<packaging>mule-application</packaging>

<name>mule-app-name</name>

<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <pipeline_uid>0</pipeline_uid>
 <deployment.version>1.0.${pipeline_uid}</deployment.version>
 <app.runtime>4.4.0</app.runtime>
 <mule.maven.plugin.version>3.8.2</mule.maven.plugin.version>
 <munit.version>2.3.14</munit.version>
</properties>

<build>
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-clean-plugin</artifactId>
   <version>3.2.0</version>
  </plugin>
  <plugin>
   <groupId>org.mule.tools.maven</groupId>
   <artifactId>mule-maven-plugin</artifactId>
   <version>${mule.maven.plugin.version}</version>
   <extensions>true</extensions>
   <configuration>
    <classifier>${mule_app_classifier}</classifier>
    <cloudhub2Deployment>
     <uri>https://anypoint.mulesoft.com</uri>
     <muleVersion>${app.runtime}</muleVersion>
     <target>${cloudhub_target}</target>
     <provider>MC</provider>
     <connectedAppClientId>${connectedapp_client_id}</connectedAppClientId>
     <connectedAppClientSecret>${connectedapp_client_secret}</connectedAppClientSecret>
     <connectedAppGrantType>client_credentials</connectedAppGrantType>
     <environment>${anypoint_platform_environment}</environment>
     <replicas>${replicas}</replicas>
     <vCores>${v_cores}</vCores>
     <applicationName>${mule_env}-${project.name}</applicationName>
     <properties>
      <mule.env>${mule_env}</mule.env>
     </properties>
    </cloudhub2Deployment>
   </configuration>
  </plugin>
  ···
 </plugins>
</build>
···
<distributionManagement>
 <repository>
  <id>my-exchange-v2</id>
  <name>Corporate Repository</name>
  <url>https://maven.anypoint.mulesoft.com/api/v2/organizations/${project.groupId}/maven</url>
  <layout>default</layout>
 </repository>
</distributionManagement>

Note that we are using V2 of the Exchange Maven Facade API (Check the <url> inside <distributionManagment> tag above) to upload the example to Exchange because the latest version V3 doesn't support dynamic versioning in POM.

Settings File Snippet

There isn't much happening in the settings.xml file here, but it has been included in this guide so that pipeline script arguments make sense to newcomers.

<servers>
 <server>
  <id>my-exchange-v2</id>
  <username>~~~Client~~~</username>
  <password>${connectedapp_client_id}~?~${connectedapp_client_secret}</password>
 </server>
</servers>

That's it.

Comments