Multi Maven & Git Repositories App Service Azure DevOps Deployment

Donnie Z
5 min readFeb 21, 2022

--

Azure DevOps Pipeline provides CI/CD service including support for Maven packages build and deployment. Azure pipelines is flexible, yet this writer unable to find working example of maven deployment pipeline that depends on maven artifact generated by other pipeline, without using maven repository manager such as JFrog or Nexus. This article will present one among many other approaches to achieve this goal.

The goal

  • Deploy into Azure App Service using DevOps pipeline

The conditions

  • The App Service is a spring boot project in a git repository (lets call it ms-storage-app).
  • The ms-app-storage has pom dependency to other project in separate git repository (sca-point).

The constraints

  • Does not use maven repository manager tools such as Nexus or JFrog.
  • Does not use mounted managed disk.
  • Basically I want to use Azure F1:Free App Service Plan (always free?) only.

The Plan

After studying the features provided by Azure DevOps, and lots of trial and error, this is the flow to achieve it:

  1. Build sca-point and publish it as pipeline artifact.
  2. Get sca-point artifact into local maven repository.
  3. Build ms-storage-app
  4. Deploy into App Service

Details speak louder than words

sca-point

pom.xml

<project ... >  <groupId>com.samaicita.point</groupId>
<artifactId>sca-point</artifactId>
<version>0.1.8</version>

We will use the local .m2 repository of the maven artifact as directory to be published as pipeline artifact.

ms-storage-app

pom.xml

<project ...>  <groupId>com.sca.springboot</groupId>
<artifactId>storage-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>com.samaicita.point</groupId>
<artifactId>sca-point</artifactId>
<version>0.1.8</version>
</dependency>

</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</...>

sca-point is a pom dependency of ms-storage-app. ms-storage-app shall be deployed into Azure App Service.

pipeline: sca-point

Create new pipeline UI flow:

The maven task is already created on the yaml file. On Edit pipeline YAML page, click Show Assistant to expand Task wizard. We will add publish task to publish the generated artifacts from local repository. The other pipeline (ms-storage-app) will download the artifact into its .m2/repository directory. To make the pipeline artifact can be used, we need to remove file _remote.repositories from the artifact directory. Hence ‘delete task’ will be added before ‘publish task’. In addition to that, we will add caching to improve the build process, and also will attempt to retain previous built artifacts to keep older jar versions.

Use Artifact Caching

To speed up subsequent built process, add Cache Task. Once created artifact cache is immutable. So change the cache key on case you want to ‘update’ your cache.

Cache Task is available from Task Assistant.

Delete task

This task shall deletes _*.repositories files on the to be published directory $(Pipeline.Workspace)/.m2/repository/com/samaicita/point/sca-point and its subdir.

Previous Build

Pipeline execution will start from fresh environment, meaning the previous built artifact will not be exists on local m2 repository. On case you need to keep the older version, we need to download published artifact from previous execution before building the current version.

Download Build Artifact Task shall be explained on Deploying App Service section.

Publish Build Artifacts

Add publish build artifacts from the task assistants, set artifact name to sca-point, and path to publish to the local maven artifact location $(Pipeline.Workspace)/.m2/repository/com/samaicita/point/sca-point. We are not using the default /target because the pom xml does not exists there.

Final Pipeline YAML

trigger:
- development
pool:
vmImage: ubuntu-latest
steps:- task: Cache@2
inputs:
key: 'v2 | maven | "$(Agent.OS)" | **/pom.xml'
restoreKeys: |
v2 | maven | "$(Agent.OS)"
v2 | maven
path: $(MAVEN_CACHE_FOLDER)
displayName: Cache Maven local repo
- task: DownloadBuildArtifacts@1
inputs:
buildType: 'specific'
project: 'XX0afaXX-XXXX-XXXX-XXXX-XX0b830758XX'
pipeline: '3'
buildVersionToDownload: 'latest'
downloadType: 'specific'
downloadPath: '$(Pipeline.Workspace)/.m2/repository/com/samaicita/point'
- task: Maven@3
inputs:
mavenPomFile: 'pom.xml'
mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'
javaHomeOption: 'JDKVersion'
jdkVersionOption: '1.8'
jdkArchitectureOption: 'x64'
publishJUnitResults: true
testResultsFiles: '**/surefire-reports/TEST-*.xml'
mavenAuthenticateFeed: true
goals: 'install'
- task: DeleteFiles@1
inputs:
SourceFolder: $(Pipeline.Workspace)/.m2/repository/com/samaicita/point/sca-point
Contents: '**/_*.repositories'
- task: PublishPipelineArtifact@1
inputs:
targetPath: $(Pipeline.Workspace)/.m2/repository/com/samaicita/point/sca-point
artifactName: sca-point

Click “Save and Run” to finish.

pipeline: ms-storage-app

This deployment pipeline created by selecting Maven package Java project Web App On Azure. By default there will be two stages: build and deploy. On the build stage we will add cache task and download artifact build task before executing maven package.

Download Build Artifact Task

Before pipeline’s maven package task executed, download sca-point build artifact. On the download build artifact task form set Destination directory to $(Pipeline.Workspace)/.m2/repository/com/samaicita/point. Notice that the pom artifact name is omitted because the azure artifact already contains the folder name.

This task also can be added into sca-point pipeline to retain the other jar versions generated by previous build.

Final Pipeline YAML

trigger:
- master
variables:
# Azure Res Mgr connection created during pipeline creation
azureSubscription: 'XX2a5XXX-XXXX-XXXX-XXXX-XXXXc5ecXXXX'
# Web app name
webAppName: 'storage-app'
# Environment name
environmentName: 'storage-app'
# Agent VM image name
vmImageName: 'ubuntu-latest'
MAVEN_CACHE_FOLDER: $(Pipeline.Workspace)/.m2/repository
MAVEN_OPTS: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: MavenPackageAndPublishArtifacts
displayName: Maven Package and Publish Artifacts
pool:
vmImage: $(vmImageName)
steps:
- task: Cache@2
inputs:
key: 'maven | "$(Agent.OS)" | **/pom.xml'
restoreKeys: |
maven | "$(Agent.OS)"
maven
path: $(MAVEN_CACHE_FOLDER)
displayName: Cache Maven local repo
- task: DownloadBuildArtifacts@1
inputs:
buildType: 'specific'
project: 'XX0afaXX-XXXX-XXXX-XXXX-XX0b830758XX'
pipeline: '3'
buildVersionToDownload: 'latest'
downloadType: 'single'
downloadPath: '$(Pipeline.Workspace)/.m2/repository/com/samaicita/point'
- task: Maven@3
displayName: 'Maven Package'
inputs:
mavenPomFile: 'pom.xml'
mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'
javaHomeOption: 'JDKVersion'
jdkVersionOption: '1.11'
jdkArchitectureOption: 'x64'
publishJUnitResults: true
testResultsFiles: '**/surefire-reports/TEST-*.xml'
- task: CopyFiles@2
displayName: 'Copy Files to artifact staging directory'
inputs:
SourceFolder: '$(System.DefaultWorkingDirectory)'
Contents: '**/target/*.?(war|jar)'
TargetFolder: $(Build.ArtifactStagingDirectory)
- upload: $(Build.ArtifactStagingDirectory)
artifact: drop
- stage: Deploy
displayName: Deploy stage
dependsOn: Build
condition: succeeded()
jobs: - deployment: DeployLinuxWebApp
displayName: Deploy Linux Web App
environment: $(environmentName)
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Azure Web App Deploy: storage-app'
inputs:
azureSubscription: $(azureSubscription)
appType: webAppLinux
appName: $(webAppName)
package: '$(Pipeline.Workspace)/drop/**/target/*.?(war|jar)'

Run it

Pipeline execution log

Both pipeline works as expected.

Conclusion

It is possible to create Azure Pipeline for maven deployment that depends on artifact generated by other pipeline without using maven repository manager.

--

--