Automating MuleSoft deployments with CI/CD improves reliability and accelerates delivery. This guide covers building a complete pipeline with Maven and GitHub Actions for CloudHub and Runtime Fabric.
CI/CD Architecture Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ MULESOFT CI/CD PIPELINE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ DEVELOPER GITHUB │
│ ┌─────────────┐ ┌────────────────────────────────────────┐ │
│ │ Commit │───push────►│ │ │
│ │ Code │ │ ┌──────────────────────────────────┐ │ │
│ └─────────────┘ │ │ GITHUB ACTIONS WORKFLOW │ │ │
│ │ │ │ │ │
│ │ │ 1. Checkout Code │ │ │
│ │ │ 2. Setup JDK 8/11 │ │ │
│ │ │ 3. Maven Cache │ │ │
│ │ │ 4. Run MUnit Tests │ │ │
│ │ │ 5. Package Application │ │ │
│ │ │ 6. Deploy to Anypoint │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────┘ │ │
│ │ │ │ │
│ └──────────────┼─────────────────────────┘ │
│ │ │
│ ▼ │
│ ANYPOINT PLATFORM ┌────────────────────────────────────────┐ │
│ ┌─────────────────────────┤ DEPLOYMENT TARGETS │ │
│ │ └────────────────────────────────────────┘ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ CloudHub │ │ CloudHub │ │ Runtime │ │
│ │ │ (DEV) │ │ (PROD) │ │ Fabric │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Maven Project Configuration
pom.xml Setup
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.devidevs</groupId>
<artifactId>orders-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>mule-application</packaging>
<name>Orders API</name>
<description>Order Management System API</description>
<properties>
<!-- Mule Runtime Version -->
<app.runtime>4.6.0</app.runtime>
<!-- Plugin Versions -->
<mule.maven.plugin.version>4.1.1</mule.maven.plugin.version>
<munit.version>3.1.0</munit.version>
<!-- Deployment Properties -->
<cloudhub.environment>Sandbox</cloudhub.environment>
<cloudhub.region>us-east-1</cloudhub.region>
<cloudhub.workers>1</cloudhub.workers>
<cloudhub.workerType>MICRO</cloudhub.workerType>
<!-- Connected App Credentials (from CI/CD secrets) -->
<anypoint.client.id>${env.ANYPOINT_CLIENT_ID}</anypoint.client.id>
<anypoint.client.secret>${env.ANYPOINT_CLIENT_SECRET}</anypoint.client.secret>
<anypoint.org.id>${env.ANYPOINT_ORG_ID}</anypoint.org.id>
</properties>
<build>
<plugins>
<!-- Mule Maven Plugin -->
<plugin>
<groupId>org.mule.tools.maven</groupId>
<artifactId>mule-maven-plugin</artifactId>
<version>${mule.maven.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<!-- CloudHub Deployment Configuration -->
<cloudHubDeployment>
<uri>https://anypoint.mulesoft.com</uri>
<muleVersion>${app.runtime}</muleVersion>
<connectedAppClientId>${anypoint.client.id}</connectedAppClientId>
<connectedAppClientSecret>${anypoint.client.secret}</connectedAppClientSecret>
<connectedAppGrantType>client_credentials</connectedAppGrantType>
<applicationName>${project.artifactId}-${cloudhub.environment}</applicationName>
<environment>${cloudhub.environment}</environment>
<businessGroupId>${anypoint.org.id}</businessGroupId>
<region>${cloudhub.region}</region>
<workers>${cloudhub.workers}</workers>
<workerType>${cloudhub.workerType}</workerType>
<objectStoreV2>true</objectStoreV2>
<persistentQueues>true</persistentQueues>
<properties>
<env>${cloudhub.environment}</env>
<api.autodiscovery.id>${api.id}</api.autodiscovery.id>
</properties>
<secureProperties>
<db.password>${env.DB_PASSWORD}</db.password>
<api.client.secret>${env.API_CLIENT_SECRET}</api.client.secret>
</secureProperties>
</cloudHubDeployment>
</configuration>
</plugin>
<!-- MUnit Plugin -->
<plugin>
<groupId>com.mulesoft.munit.tools</groupId>
<artifactId>munit-maven-plugin</artifactId>
<version>${munit.version}</version>
<executions>
<execution>
<id>test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
<goal>coverage-report</goal>
</goals>
</execution>
</executions>
<configuration>
<coverage>
<runCoverage>true</runCoverage>
<failBuild>true</failBuild>
<requiredApplicationCoverage>80</requiredApplicationCoverage>
<requiredFlowCoverage>80</requiredFlowCoverage>
<formats>
<format>html</format>
<format>json</format>
</formats>
</coverage>
<runtimeVersion>${app.runtime}</runtimeVersion>
</configuration>
</plugin>
</plugins>
</build>
<!-- Maven Repositories -->
<repositories>
<repository>
<id>anypoint-exchange-v3</id>
<name>Anypoint Exchange</name>
<url>https://maven.anypoint.mulesoft.com/api/v3/maven</url>
<layout>default</layout>
</repository>
<repository>
<id>mulesoft-releases</id>
<name>MuleSoft Releases Repository</name>
<url>https://repository.mulesoft.org/releases/</url>
<layout>default</layout>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>mulesoft-releases</id>
<name>MuleSoft Releases Repository</name>
<url>https://repository.mulesoft.org/releases/</url>
<layout>default</layout>
</pluginRepository>
</pluginRepositories>
<!-- Deployment Profiles -->
<profiles>
<!-- Development Profile -->
<profile>
<id>dev</id>
<properties>
<cloudhub.environment>Sandbox</cloudhub.environment>
<cloudhub.workers>1</cloudhub.workers>
<cloudhub.workerType>MICRO</cloudhub.workerType>
</properties>
</profile>
<!-- QA Profile -->
<profile>
<id>qa</id>
<properties>
<cloudhub.environment>QA</cloudhub.environment>
<cloudhub.workers>1</cloudhub.workers>
<cloudhub.workerType>SMALL</cloudhub.workerType>
</properties>
</profile>
<!-- Production Profile -->
<profile>
<id>prod</id>
<properties>
<cloudhub.environment>Production</cloudhub.environment>
<cloudhub.workers>2</cloudhub.workers>
<cloudhub.workerType>SMALL</cloudhub.workerType>
<cloudhub.region>us-east-1</cloudhub.region>
</properties>
</profile>
<!-- Runtime Fabric Profile -->
<profile>
<id>rtf</id>
<build>
<plugins>
<plugin>
<groupId>org.mule.tools.maven</groupId>
<artifactId>mule-maven-plugin</artifactId>
<version>${mule.maven.plugin.version}</version>
<configuration>
<runtimeFabricDeployment>
<uri>https://anypoint.mulesoft.com</uri>
<muleVersion>${app.runtime}</muleVersion>
<connectedAppClientId>${anypoint.client.id}</connectedAppClientId>
<connectedAppClientSecret>${anypoint.client.secret}</connectedAppClientSecret>
<connectedAppGrantType>client_credentials</connectedAppGrantType>
<applicationName>${project.artifactId}</applicationName>
<target>${rtf.target}</target>
<environment>${rtf.environment}</environment>
<businessGroupId>${anypoint.org.id}</businessGroupId>
<provider>MC</provider>
<replicas>2</replicas>
<publicUrl>${rtf.public.url}</publicUrl>
<lastMileSecurity>true</lastMileSecurity>
<clusteringEnabled>true</clusteringEnabled>
<deploymentSettings>
<http>
<inbound>
<publicUrl>${rtf.public.url}</publicUrl>
</inbound>
</http>
<resources>
<cpu>
<limit>500m</limit>
<reserved>100m</reserved>
</cpu>
<memory>
<limit>1000Mi</limit>
<reserved>500Mi</reserved>
</memory>
</resources>
</deploymentSettings>
</runtimeFabricDeployment>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>Maven Settings (settings.xml)
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<!-- Anypoint Exchange Server -->
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${env.ANYPOINT_CLIENT_ID}~?~${env.ANYPOINT_CLIENT_SECRET}</password>
</server>
<!-- MuleSoft Releases -->
<server>
<id>mulesoft-releases</id>
<username>${env.MULESOFT_NEXUS_USERNAME}</username>
<password>${env.MULESOFT_NEXUS_PASSWORD}</password>
</server>
<!-- Private Exchange Repository -->
<server>
<id>exchange-private</id>
<username>~~~Client~~~</username>
<password>${env.ANYPOINT_CLIENT_ID}~?~${env.ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
<profiles>
<profile>
<id>mulesoft</id>
<repositories>
<repository>
<id>anypoint-exchange-v3</id>
<name>Anypoint Exchange</name>
<url>https://maven.anypoint.mulesoft.com/api/v3/maven</url>
</repository>
<repository>
<id>mulesoft-releases</id>
<name>MuleSoft Releases</name>
<url>https://repository.mulesoft.org/releases/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>mulesoft-releases</id>
<name>MuleSoft Releases</name>
<url>https://repository.mulesoft.org/releases/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>mulesoft</activeProfile>
</activeProfiles>
</settings>GitHub Actions Workflows
Main CI/CD Workflow
# .github/workflows/mule-cicd.yml
name: MuleSoft CI/CD Pipeline
on:
push:
branches: [main, develop, 'release/*']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'dev'
type: choice
options:
- dev
- qa
- prod
env:
JAVA_VERSION: '11'
MAVEN_VERSION: '3.9.5'
jobs:
# Build and Test Job
build-and-test:
name: Build and Test
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
<server>
<id>mulesoft-releases</id>
<username>${MULESOFT_NEXUS_USERNAME}</username>
<password>${MULESOFT_NEXUS_PASSWORD}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
MULESOFT_NEXUS_USERNAME: ${{ secrets.MULESOFT_NEXUS_USERNAME }}
MULESOFT_NEXUS_PASSWORD: ${{ secrets.MULESOFT_NEXUS_PASSWORD }}
- name: Cache Maven Dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
!~/.m2/repository/com/devidevs
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build Application
run: mvn clean compile -DskipTests
- name: Run MUnit Tests
run: mvn test
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: |
target/surefire-reports/
target/munit-reports/
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: target/site/munit/coverage/
- name: Package Application
run: mvn package -DskipTests
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: mule-artifact
path: target/*.jar
retention-days: 5
# Security Scan Job
security-scan:
name: Security Scan
runs-on: ubuntu-latest
needs: build-and-test
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Run OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'mulesoft-api'
path: '.'
format: 'HTML'
args: >
--failOnCVSS 7
--enableRetired
- name: Upload Security Report
uses: actions/upload-artifact@v4
if: always()
with:
name: security-report
path: reports/
# Deploy to Development
deploy-dev:
name: Deploy to Development
runs-on: ubuntu-latest
needs: [build-and-test, security-scan]
if: github.ref == 'refs/heads/develop' || github.event.inputs.environment == 'dev'
environment:
name: development
url: https://orders-api-dev.cloudhub.io
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Deploy to CloudHub (Dev)
run: |
mvn deploy -DmuleDeploy -Pdev -DskipTests \
-Danypoint.client.id=${{ secrets.ANYPOINT_CLIENT_ID }} \
-Danypoint.client.secret=${{ secrets.ANYPOINT_CLIENT_SECRET }} \
-Danypoint.org.id=${{ secrets.ANYPOINT_ORG_ID }} \
-Denv.DB_PASSWORD=${{ secrets.DEV_DB_PASSWORD }} \
-Denv.API_CLIENT_SECRET=${{ secrets.DEV_API_CLIENT_SECRET }}
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Verify Deployment
run: |
echo "Waiting for deployment to complete..."
sleep 60
curl -f https://orders-api-dev.cloudhub.io/api/health || exit 1
# Deploy to QA
deploy-qa:
name: Deploy to QA
runs-on: ubuntu-latest
needs: deploy-dev
if: github.ref == 'refs/heads/develop' || github.event.inputs.environment == 'qa'
environment:
name: qa
url: https://orders-api-qa.cloudhub.io
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Deploy to CloudHub (QA)
run: |
mvn deploy -DmuleDeploy -Pqa -DskipTests \
-Danypoint.client.id=${{ secrets.ANYPOINT_CLIENT_ID }} \
-Danypoint.client.secret=${{ secrets.ANYPOINT_CLIENT_SECRET }} \
-Danypoint.org.id=${{ secrets.ANYPOINT_ORG_ID }} \
-Denv.DB_PASSWORD=${{ secrets.QA_DB_PASSWORD }} \
-Denv.API_CLIENT_SECRET=${{ secrets.QA_API_CLIENT_SECRET }}
# Deploy to Production
deploy-prod:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy-qa
if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'prod'
environment:
name: production
url: https://orders-api.cloudhub.io
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Deploy to CloudHub (Production)
run: |
mvn deploy -DmuleDeploy -Pprod -DskipTests \
-Danypoint.client.id=${{ secrets.ANYPOINT_CLIENT_ID }} \
-Danypoint.client.secret=${{ secrets.ANYPOINT_CLIENT_SECRET }} \
-Danypoint.org.id=${{ secrets.ANYPOINT_ORG_ID }} \
-Denv.DB_PASSWORD=${{ secrets.PROD_DB_PASSWORD }} \
-Denv.API_CLIENT_SECRET=${{ secrets.PROD_API_CLIENT_SECRET }}
- name: Verify Production Deployment
run: |
echo "Waiting for deployment to complete..."
sleep 90
curl -f https://orders-api.cloudhub.io/api/health || exit 1
- name: Create Release Tag
if: success()
run: |
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
git tag "v${VERSION}-$(date +%Y%m%d%H%M%S)"
git push origin --tagsRuntime Fabric Deployment Workflow
# .github/workflows/mule-rtf-deploy.yml
name: Deploy to Runtime Fabric
on:
workflow_dispatch:
inputs:
environment:
description: 'RTF Environment'
required: true
type: choice
options:
- rtf-dev
- rtf-prod
target:
description: 'RTF Target Name'
required: true
default: 'rtf-cluster-1'
jobs:
deploy-rtf:
name: Deploy to RTF
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Build Application
run: mvn clean package -DskipTests
- name: Deploy to Runtime Fabric
run: |
mvn deploy -DmuleDeploy -Prtf -DskipTests \
-Danypoint.client.id=${{ secrets.ANYPOINT_CLIENT_ID }} \
-Danypoint.client.secret=${{ secrets.ANYPOINT_CLIENT_SECRET }} \
-Danypoint.org.id=${{ secrets.ANYPOINT_ORG_ID }} \
-Drtf.target=${{ github.event.inputs.target }} \
-Drtf.environment=${{ github.event.inputs.environment }} \
-Drtf.public.url=${{ secrets.RTF_PUBLIC_URL }}
- name: Wait for Deployment
run: |
echo "Waiting for RTF deployment to stabilize..."
sleep 120
- name: Verify RTF Deployment
run: |
curl -f ${{ secrets.RTF_PUBLIC_URL }}/api/health || exit 1Publish to Exchange Workflow
# .github/workflows/publish-exchange.yml
name: Publish to Anypoint Exchange
on:
release:
types: [published]
workflow_dispatch:
jobs:
publish:
name: Publish to Exchange
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Configure Maven Settings
run: |
mkdir -p ~/.m2
cat << EOF > ~/.m2/settings.xml
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
<server>
<id>exchange-private</id>
<username>~~~Client~~~</username>
<password>${ANYPOINT_CLIENT_ID}~?~${ANYPOINT_CLIENT_SECRET}</password>
</server>
</servers>
</settings>
EOF
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}
- name: Set Version from Tag
if: github.event_name == 'release'
run: |
VERSION=${GITHUB_REF#refs/tags/v}
mvn versions:set -DnewVersion=$VERSION
- name: Publish to Exchange
run: |
mvn deploy -DskipTests \
-DaltDeploymentRepository=exchange-private::default::https://maven.anypoint.mulesoft.com/api/v3/organizations/${{ secrets.ANYPOINT_ORG_ID }}/maven
env:
ANYPOINT_CLIENT_ID: ${{ secrets.ANYPOINT_CLIENT_ID }}
ANYPOINT_CLIENT_SECRET: ${{ secrets.ANYPOINT_CLIENT_SECRET }}MUnit Testing Configuration
Sample MUnit Test
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:munit="http://www.mulesoft.org/schema/mule/munit"
xmlns:munit-tools="http://www.mulesoft.org/schema/mule/munit-tools"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core
http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/munit
http://www.mulesoft.org/schema/mule/munit/current/mule-munit.xsd
http://www.mulesoft.org/schema/mule/munit-tools
http://www.mulesoft.org/schema/mule/munit-tools/current/mule-munit-tools.xsd
http://www.mulesoft.org/schema/mule/http
http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">
<munit:config name="orders-api-test-suite.xml"/>
<!-- Test: Create Order - Success -->
<munit:test name="create-order-success-test"
description="Test successful order creation"
doc:id="test-1">
<munit:behavior>
<!-- Mock database insert -->
<munit-tools:mock-when processor="db:insert" doc:name="Mock DB Insert">
<munit-tools:with-attributes>
<munit-tools:with-attribute
attributeName="doc:name"
whereValue="Insert Order"/>
</munit-tools:with-attributes>
<munit-tools:then-return>
<munit-tools:payload value="#[1]"/>
</munit-tools:then-return>
</munit-tools:mock-when>
<!-- Mock external API call -->
<munit-tools:mock-when processor="http:request" doc:name="Mock HTTP Request">
<munit-tools:with-attributes>
<munit-tools:with-attribute
attributeName="doc:name"
whereValue="Call Inventory Service"/>
</munit-tools:with-attributes>
<munit-tools:then-return>
<munit-tools:payload value="#[{available: true, quantity: 100}]"/>
</munit-tools:then-return>
</munit-tools:mock-when>
</munit:behavior>
<munit:execution>
<set-payload value='#[{
"customerId": "CUST-001",
"items": [
{"productId": "PROD-001", "quantity": 2},
{"productId": "PROD-002", "quantity": 1}
]
}]' mimeType="application/json"/>
<flow-ref name="create-order-flow"/>
</munit:execution>
<munit:validation>
<!-- Assert HTTP status -->
<munit-tools:assert-that
expression="#[attributes.statusCode]"
is="#[MunitTools::equalTo(201)]"
message="Expected 201 Created status"/>
<!-- Assert response contains orderId -->
<munit-tools:assert-that
expression="#[payload.orderId]"
is="#[MunitTools::notNullValue()]"
message="Response should contain orderId"/>
<!-- Assert response status -->
<munit-tools:assert-that
expression="#[payload.status]"
is="#[MunitTools::equalTo('CREATED')]"
message="Order status should be CREATED"/>
</munit:validation>
</munit:test>
<!-- Test: Create Order - Validation Error -->
<munit:test name="create-order-validation-error-test"
description="Test order creation with invalid data"
expectedErrorType="APP:VALIDATION_ERROR">
<munit:execution>
<!-- Missing required fields -->
<set-payload value='#[{
"items": []
}]' mimeType="application/json"/>
<flow-ref name="create-order-flow"/>
</munit:execution>
</munit:test>
<!-- Test: Get Order - Found -->
<munit:test name="get-order-found-test"
description="Test retrieving existing order">
<munit:behavior>
<munit-tools:mock-when processor="db:select" doc:name="Mock DB Select">
<munit-tools:then-return>
<munit-tools:payload value="#[[{
order_id: 'ORD-12345',
customer_id: 'CUST-001',
status: 'PROCESSING',
total: 150.00
}]]"/>
</munit-tools:then-return>
</munit-tools:mock-when>
</munit:behavior>
<munit:execution>
<set-variable variableName="orderId" value="ORD-12345"/>
<flow-ref name="get-order-flow"/>
</munit:execution>
<munit:validation>
<munit-tools:assert-that
expression="#[attributes.statusCode]"
is="#[MunitTools::equalTo(200)]"/>
<munit-tools:assert-that
expression="#[payload.orderId]"
is="#[MunitTools::equalTo('ORD-12345')]"/>
</munit:validation>
</munit:test>
<!-- Test: Get Order - Not Found -->
<munit:test name="get-order-not-found-test"
description="Test retrieving non-existent order"
expectedErrorType="APP:NOT_FOUND">
<munit:behavior>
<munit-tools:mock-when processor="db:select">
<munit-tools:then-return>
<munit-tools:payload value="#[[]]"/>
</munit-tools:then-return>
</munit-tools:mock-when>
</munit:behavior>
<munit:execution>
<set-variable variableName="orderId" value="NON-EXISTENT"/>
<flow-ref name="get-order-flow"/>
</munit:execution>
</munit:test>
</mule>GitHub Secrets Configuration
Required Secrets
# GitHub Repository Secrets Configuration
# Anypoint Platform Credentials
ANYPOINT_CLIENT_ID: "connected-app-client-id"
ANYPOINT_CLIENT_SECRET: "connected-app-client-secret"
ANYPOINT_ORG_ID: "org-business-group-id"
# MuleSoft Nexus (for EE dependencies)
MULESOFT_NEXUS_USERNAME: "nexus-username"
MULESOFT_NEXUS_PASSWORD: "nexus-password"
# Environment-specific secrets
DEV_DB_PASSWORD: "dev-database-password"
DEV_API_CLIENT_SECRET: "dev-api-secret"
QA_DB_PASSWORD: "qa-database-password"
QA_API_CLIENT_SECRET: "qa-api-secret"
PROD_DB_PASSWORD: "prod-database-password"
PROD_API_CLIENT_SECRET: "prod-api-secret"
# Runtime Fabric (if using RTF)
RTF_PUBLIC_URL: "https://api.yourdomain.com"Best Practices Summary
cicd_best_practices:
pipeline_design:
- "Use branch-based deployment (develop → QA, main → Prod)"
- "Implement environment approvals for production"
- "Cache Maven dependencies to speed up builds"
- "Run tests in parallel when possible"
- "Fail fast on test failures"
security:
- "Store all credentials in GitHub Secrets"
- "Use Connected Apps instead of user credentials"
- "Run OWASP dependency checks"
- "Scan for secrets in code"
- "Use environment-specific secrets"
testing:
- "Enforce minimum 80% code coverage"
- "Mock external dependencies in tests"
- "Run integration tests in CI"
- "Upload test reports as artifacts"
deployment:
- "Use Maven profiles for environment configuration"
- "Implement health checks post-deployment"
- "Tag releases after successful production deployment"
- "Use blue-green or rolling deployments for zero downtime"
monitoring:
- "Configure deployment notifications (Slack, Teams)"
- "Set up deployment tracking in monitoring tools"
- "Log deployment metadata for traceability"
- "Monitor deployment success/failure rates"Conclusion
Automating MuleSoft CI/CD with Maven and GitHub Actions streamlines deployments across CloudHub and Runtime Fabric. Key practices include using Connected Apps for authentication, environment-specific profiles, comprehensive MUnit testing, and security scanning. This pipeline ensures reliable, repeatable deployments with proper governance.