Applying the DRY Principle to your Maven project using AWS CodeArtifact

Joshua Lindsay
daemon-engineering
5 min readAug 10, 2022

--

This is a step by step implementation of how to create a base Maven project and deploy to AWS CodeArtifact using Bitbucket pipelines, so that common code can be stored as a Maven dependency.

Before creating a project, create a Bitbucket repository and clone to a folder of your choosing.

Creating the Maven project

cd into the directory where your Bitbucket repo is cloned.

Create a Maven project using the below script in a Terminal.

Note: change out the groupId and artifactId to your own preference.

mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

Once created cd into the directory.

cd my-app

Begin adding any relevant code that is shared across multiple projects.

Creating the AWS CodeArtifact repository

Login to AWS using SSO.

Search for AWS CodeArtifact in services and open.

On “Getting Started” click “Create repository”.

Give the repository a name and under “Public upstream repositories” click “Maven-central-store”. Although upstream is an optional step, it is best to apply now as the code you put into the project may need to pull packages from the Maven store.

Select “This AWS account” on the next step. If a domain exists, select the preferred domain. Otherwise, create a new one.

Click “Create repository” on the next step.

Setting up the project so that it can be pushed to CodeArtifact

Open CodeArtifact repo from the AWS console.

Click the “View connection instructions” button on the right hand side. Once opened select “mvn“ from the dropdown.

Note: Leave this window open as it contains all the relevant information to connect to the repo.

Within your Maven project create a settings.xml file with the below template:

<settings>

</settings>

Copy block 3 from the connections window that we opened in the previous step and paste in-between the settings tags.

Example:

<settings>
<servers>
<server>
<id>test-repo-common-library</id>
<username>aws</username>
<password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
</server>
</servers>
</settings>

Copy the block under “Manual setup: pushing to your repository”.

Open the pom.xml and paste the block after the properties closing tag.

<distributionManagement>
<repository>
<id>test-repo-common-library</id>
<name>test-repo-common-library</name>
<url>https://test-repo-000000000000.d.codeartifact.eu-west-1.amazonaws.com/maven/common-library/</url>
</repository>
</distributionManagement>

Setup target project to pull the dependency

Create a settings.xml file within the root of the target project.

Within the same “View connection instructions” window, copy blocks 2 and 3 from “Manual setup: pulling from your repository” and paste into the target project settings.xml like the example below.

<settings>
<profiles>
<profile>
<id>daemn-engineering-common-library</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>test-repo-common-library</id>
<url>https://test-repo-000000000000.d.codeartifact.eu-west-1.amazonaws.com/maven/common-library/</url>
</repository>
</repositories>
</profile>
</profiles>
<servers>
<server>
<id>test-repo-common-library</id>
<username>aws</username>
<password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
</server>
</servers>
</settings>

Configure Semantic Versioning for your project

Head to Semantic Versioning to setup versioning within your project. This step is key, as CodeArtifact requires a new version each time you push an image.

Configure Bitbucket-pipeline.yml

Below is a configuration to setup Bitbucket pipelines to deploy your project to AWS CodeArtifact.

image: maven:3.8.1

definitions:
steps:
- step: &Get-auth-token
name: Get-auth-token
image: atlassian/pipelines-awscli
script:
# Gets authorization token for CodeArtifact and exports it to a pipeline artifact.
- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
- export AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION
- echo "export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain $CODEARTIFACT_DOMAIN --domain-owner $CODEARTIFACT_DOMAIN_OWNER --query authorizationToken --output text`" > shared_vars.sh
artifacts:
- shared_vars.sh

- step: &Build
name: Build
caches:
- maven
script:
# Pulls in the artifact from the previous step, removes snapshot to have full formed versions and deploys to CodeArtifact.
- source shared_vars.sh
- git pull
- echo $CODEARTIFACT_AUTH_TOKEN
- mvn versions:set -DremoveSnapshot
- mvn -s settings.xml clean package deploy

- step: &PatchVersionUpdate
name: Patch Version Update
script:
- git pull
- mvn validate -DbumpPatch # Setting up new project version For our repo using Semantic Versioning.
- VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:3.1.0:evaluate -Dexpression=project.version | sed 's/$/-SNAPSHOT/' | grep -e '^[[:digit:]]') # Extracting the version number from the pom file
- echo ${VERSION} # Printing out the version
- mvn versions:set -DnewVersion=${VERSION} # Adding -SNAPSHOT to the newly created version
- git add pom.xml
- git commit -m "[skip ci] Update the version number in the pom.xml file"
- git push

- step: &Minor/Major_VersionUpdate
name: Minor/Major Version Update
script:
- git pull
- mvn validate $VERSION_BUMP_TYPE # Setting up new project version For our repo using Semantic Versioning.
- VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:3.1.0:evaluate -Dexpression=project.version | sed 's/$/-SNAPSHOT/' | grep -e '^[[:digit:]]') # Extracting the version number from the pom file
- echo ${VERSION} # Printing out the version
- mvn versions:set -DnewVersion=${VERSION} # Adding -SNAPSHOT to the newly created version
- git add pom.xml
- git commit -m "[skip ci] Update the version number in the pom.xml file"
- mvn -B -DlocalCheckout=true release:clean release:prepare release:perform -DdevelopmentVersion=${VERSION} -DscmCommentPrefix="[skip ci]" # Tagging the repo with the new bumped version

- step: &ValidateBumpVariable
name: Validate bump variable input
script:
- if [ $VERSION_BUMP_TYPE != -DbumpMinor ] && [ $VERSION_BUMP_TYPE != -DbumpMajor ] ; then echo "Invalid variable input. -DbumpMinor for MINOR Bump / -DbumpMajor for MAJOR Bump"; exit 1; fi

- step: &ValidateBranchMaster
name: Validate that the pipeline is running on master
script:
- if [ $BITBUCKET_BRANCH != master ] ; then echo "This pipeline can only run on MASTER branch."; exit 1; fi

pipelines:
branches:
master:
- step: *Get-auth-token
- step: *PatchVersionUpdate
- step: *Build

custom:
MINOR/MAJOR Update:
- variables:
- name: VERSION_BUMP_TYPE
- parallel:
- step: *ValidateBranchMaster
- step: *ValidateBumpVariable
- step: *Get-auth-token
- step: *Minor/Major_VersionUpdate
- step: *Build

Note: Ensure you have setup pipeline variables for your AWS credentials:

  • CODEARTIFACT_DOMAIN_OWNER
  • CODEARTIFACT_DOMAIN
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_DEFAULT_DOMAIN

Pulling a dependency from AWS CodeArtifact

Head back to the AWS Console where you previously setup the repository to store your artefacts.

Within the repository you created, there should now be a package.

Click the version of the package, this will take you to a page where you can retrieve the install block for your target project.

Select your chosen method of install and place within the pom.xml of the target project.

Before refreshing the project to pull the dependency from CodeArtifact, an auth token is required using the below script:

export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain test-repo --domain-owner 000000000000 --query authorizationToken --output text`

Note: This token expires after 12 hours, so a new one will need to be generated before refreshing again. This can be rather frustrating but there are tools out there to alleviate this problem.

Intellij IDEA has a plugin which semi automates getting the auth token.

AWS CodeArtifact + Maven — IntelliJ IDEs Plugin | Marketplace

Summary

AWS CodeArtifact is a useful service, and beds well with the rest of the ecosystem; configuration is fairly straight forward but requires a few manual steps. The integration of using Bitbucket Pipelines to deploy the artefact took little effort, but could definitely be done in 1 step with the right image. Compared to services like JFrog’s Artifactory, the setup is more complex but gives the same result. The only bug bear would be that the authorisation token expires every 12 hours.

--

--