Deploy AEM packages with Jenkins Like a Boss | Perficient Digital

Deploy AEM packages with Jenkins Like a Boss

You’ve probably seen this curl command in many articles online: [1] 

curl -u admin:admin -F file=@"name of zip file" -F name="name of package" -F force=true -F install=true http://localhost:4505/crx/packmgr/service.jsp

It allows you to upload and install an package to an AEM server. This is can also be used in Jenkins builds to deploy a package after the maven build completes. How about we get a little fancy with a script that will do this for us in a cleaner manner. One that will fail if the package deployment fails and will also fail if the AEM server returns anything but 200:

I added comments everywhere to make this easier to understand, if anything is unclear, let me know in the comment section!

#!/bin/bash

# Doc
HELP_TEXT="
    A shell script to install packages to AEM

    Params:
      -f, --file     The zip file to instal (cannot be used with -m, --module param)
      -m, --module   The folder name of maven module that produces a zip package (cannot be used with -f, --file param)
      -h, --host     The instance host
      -p, --port     The instance port
      -u, --user     The user name to use for auth

    Password should be set via env variable:
      CRX_PASSWORD   Set from env variable, default to 'admin'

    Other variables you can set via environment variables:
      CRX_USER       Set via user argument above, defaults to 'admin'
      CRX_PORT       Set via port argument above, defaults to '4502'
      CRX_HOST       Set via host argument above, defaults to 'localhost'

"
# if the first arg is "--help", "help" or "-h" print help message and exit
if [ $1 = "--help" ] || [ $1 = "help" ] || [ $1 = "-h" ]; then
    printf "$HELP_TEXT"
    exit 1
fi

# parse params as per: https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    # parse file argument
    -f|--file)
        FILE="$2"
        shift # past argument
        shift # past value
    ;;
    # parse file argument
    -m|--module)
        MVN_MODULE="$2"
        shift # past argument
        shift # past value
    ;;
    # parse host argument
    -h|--host)
        CRX_HOST="$2"
        shift # past argument
        shift # past value
    ;;
    # parse port argument
    -p|--port)
        CRX_PORT="$2"
        shift # past argument
        shift # past value
    ;;
    # parse user argument
    -u|--user)
        CRX_USER="$2"
        shift # past argument
        shift # past value
    ;;
    # unknown option
    *)
        echo "WARNING: unknown option: $1"
        shift # past argument
    ;;
esac
done

# use defaults if param was not passed
CRX_USER="${CRX_USER:-admin}"
CRX_PASSWORD="${CRX_PASSWORD:-admin}" # password should be passed as an env variable
CRX_HOST="${CRX_HOST:-localhost}"
CRX_PORT="${CRX_PORT:-4502}"

# If an module folder name is passed, use that to find the zip package to install
if [ ! -z "$MVN_MODULE" ]
then
    if [ -d "$MVN_MODULE" ]
    then
      ZIP_PATTERN="$MVN_MODULE/target/*.zip"
      ZIP_PACKAGES=( $ZIP_PATTERN )
      FILE="${ZIP_PACKAGES[0]}"
    else
      echo "ERROR: The module folder: $MVN_MODULE does not exist}"
      exit 1
    fi
else
    echo "INFO: no module param was passed. Using file param if exists"
fi


# sets INSTALL_COMMAND, first arg is the password
setInstallCommand(){
    # --fai           : https://curl.haxx.se/docs/manpage.html#-f fails on server error
    # -u              : user:password for basic auth
    # -F force=true   : force install
    # -F install=true : install after upload
    # -F strict=true  : fails if package fails to deploy
    eval "INSTALL_COMMAND='curl --fail -u $CRX_USER:$1 -F file=@$FILE -F name=$FILE  -F force=true -F install=true -F strict=true http://$CRX_HOST:$CRX_PORT/crx/packmgr/service.jsp'"
}

# print command that will be executed, sub password with "*****"
setInstallCommand "*****"
echo "Executing command: $INSTALL_COMMAND"
# Execute command
setInstallCommand "$CRX_PASSWORD"
eval "$INSTALL_COMMAND"

Copy the code and paste it into a file, call it:

deploy.sh

Make it executable:

chmod +x deploy.sh

Now, let’s explore what we can do with it (You should first copy paste it into the root of your AEM project):

run `./deploy help` to see the help message and all available params:

    A shell script to install packages to AEM

    Params:
      -f, --file     The zip file to instal (cannot be used with -m, --module param)
      -m, --module   The folder name of maven module that produces a zip package (cannot be used with -f, --file param)
      -h, --host     The instance host
      -p, --port     The instance port
      -u, --user     The user name to use for auth

    Password should be set via env variable:
      CRX_PASSWORD   Set from env variable, default to 'admin'

    Other variables you can set via environment variables:
      CRX_USER       Set via user argument above, defaults to 'admin'
      CRX_PORT       Set via port argument above, defaults to '4502'
      CRX_HOST       Set via host argument above, defaults to 'localhost'

Let’s say you want to deploy the ui.apps package after the build is done on your local machine running at localhost:4502

./deploy.sh -m ui.apps

Now let’s say you want to deploy it to your local publisher:

./deploy.sh -m ui.apps -p 4503

Wait a sec! What just happened there? We specified the maven module via “-m ui.apps” the deploy script will then go to the folder `ui.apps/target` and pick the first .zip file it finds and will deploy that. This assumes that your module produces one package, as is often the case. 

OK, now let’s say you want to deploy a very specific package in a very specific location in your repo: “packages/my-favorite-package.zip”. You can use -f for that:

./deploy.sh -f packages/my-favorite-package.zip

Now let’s move to Jenkins (or your favorite CD). Things get a little interesting. You have a secret password that you don’t want to share with everyone who can see the build. Fear not! You can use secret environment variables! There are a few ways to do this, which I won’t get into. Instead, refer to this and this and many others to see how you can bind a password to an environment variable. In our case, the goal is to bind your password to `CRX_PASSWORD` environment variable.

Note: you can also specify the username, host and port via CRX_USER, CRX_HOST and CRX_PORT respectively.

Now in your Jenkins build, you can add a post build script that looks like:

./deploy.sh -m ui.apps -u adminusername -h dev-author.mycompany.com -p 4502

If you have more than one module that produces packages you can keep calling the deploy script. For example, you want to deploy both ui.apps and ui.content packages to both your author and publish instance:

./deploy.sh -m ui.apps -u adminusername -h dev-author.mycompany.com -p 4502
./deploy.sh -m ui.apps -u adminusername -h dev-publish.mycompany.com -p 4503
./deploy.sh -m ui.content -u adminusername -h dev-author.mycompany.com -p 4502
./deploy.sh -m ui.content -u adminusername -h dev-publish.mycompany.com -p 4503

Hopefully this will help your next Jenkins build setup go much easier!

Leave a Reply