We will cover how to make our git-based deployment into a production-ready CI solution, with staged deployment and support for multiple environments. Staging deployment to production was covered earlier. This is going to address how to deploy to several environments in a CI pipeline, and promote a package from one environment to the next from a build server.

Our environments’ instances are each gonna be a slot of a web-app. We’ll create a local git repository for each of the environments, then promote code from one to the next.

The advantages of this method are:

• It’s generic enough that anything with a shell and a git client can run it
• It’s promoting from environment to environment using binaries, increasing confidence of what’s on each environment. Bonus point: binaries are marked with the commit hash that generated them, so you can easily trace back to the code.
• It maintains history of deployments. You can rollback to a given version by re-assigning master to the commit you want, then push to the environment.

The biggest drawback being that all your environments are hosted on the same webapp, which is not applicable to all cases.

# Setting the pipeline up

You need to create something that ressembles this:

You can copy slot configuration from the staging slot, but make sure you deactivate auto swap for environments other than staging.

Then configure each slot to be deployed from local git repository, the same way that’s described here. The trick here is that each environment’s repository is named SERVICENAME-slotname.azurewebsites.net, and being on the same resource-group, they all use the same credentials. To deploy from one environment to the next, we really just need to add environments as remotes, and push the master branch from one to the next. We’re thus going to build and deploy to the first environment (e.g. dev), then push to the next remotes.

Finally, set the pipeline using the scripts that you can find here, you need to do the following:

1. set the environment variables
2. invoke prepare.sh to point the build directory to your seed environment (e.g. dev).
3. build
4. deploy to the seed environment using deploy.sh
5. repeat the following process:
• run required tests and operations
• promote to the next environment with promote.sh.

An example of the whole orchestration looks like this:

#!/bin/bash

export DEPLOYMENT_USER=user-you-configured-in-azure
export DEPLOYMENT_URL='my-app-service-$ENVIRONMENT.scm.azurewebsites.net:443/my-app-service.git' export DEPLOYMENT_FOLDER=bin git config --global user.email "service@build.com" git config --global user.name "Mr. Build Service" chmod +x clean.sh chmod +x build.sh chmod +x deploy.sh chmod +x prepare.sh chmod +x promote.sh ./prepare.sh dev ./build.sh ./deploy.sh dev  Then run your unit tests. If they succeed, we promote to CI: #!/bin/bash ./promote.sh dev ci  The run your integration tests. If they succeed, we promote to prod (through staging and autoswap): # Run some other tests ./promote.sh ci staging  # How it works First off, we changed the way prepare.sh works from what was described here. It now takes an environment name, then: 1. checks if the build folder exists, else creates it and initializes git. 2. checks if the remote exists, adds it else - observe that the url is generated using the $ENVIRONMENT variable, the DEPLOYMENT_URL should be set accordingly (e.g. set to appservicename-$ENVIRONMENT.scm.azurewebsites.net) 3. fetches the latest from the initial git repo1. #!/bin/bash ENVIRONMENT=$1
echo "Preparing environment for $ENVIRONMENT" if [ ! -d "$DEPLOYMENT_FOLDER/.git" ]; then
if [ ! -d "$DEPLOYMENT_FOLDER" ]; then mkdir "$DEPLOYMENT_FOLDER"
fi
cd "$DEPLOYMENT_FOLDER" git init else cd "$DEPLOYMENT_FOLDER"
fi

if [ ! git remote | grep $ENVIRONMENT ]; then URL=eval echo -n$DEPLOYMENT_URL
git remote add $ENVIRONMENT https://$DEPLOYMENT_USER:$DEPLOYMENT_PASSWORD@$URL
fi

git fetch $ENVIRONMENT git clean -xdf git checkout$ENVIRONMENT/master --force
git branch -f master $ENVIRONMENT/master git checkout master cd -  Then we build. This part is on you. Then we deploy to the “seed” environment (e.g. dev), this is done through the deploy.sh script, which really just commits everything and push to the remote: #!/bin/bash ENVIRONMENT=$1
VERSION=git describe --always

echo "Deploying version $VERSION to$ENVIRONMENT"

cd $DEPLOYMENT_FOLDER git add -A git commit -m "Commit$VERSION - Build as of date"
git push $ENVIRONMENT master cd -  Finally, we want to promote from one environment to the next based on your process (test results, manual process, etc.). This is done using promote.sh. This script is basically checking out the source environment, and pushing its master to the target environment: #!/bin/sh FROM=$1
TO=$2 echo "Promoting$FROM to $TO" ./prepare.sh$FROM
./deploy.sh \$TO


And that is it!

# Considerations

• You need some form of cleanup to remove deleted files in your build. Drastically, you could just delete everything (except directory .git) as a first step of your build.
• You need to prevent the situation where a merge conflict would happen. This means that you need to always build to the same environment (say dev)

# Notes

1. If you’re changing the initial build environment (say, go from staging to dev), you need to synchronize your staging repository to the dev repository before you do the first build to prevent merge conflicts.