After recently getting to grips with Jenkins I wanted to expand into deployment automation as that was the only stage missing from my workflow. At my current place of work, we use Octopus Deploy for all of our in-house application deployments and it works flawlessly so I threw up an Octopus VM in Azure to play with. What we want to end up with is Jenkins building our ASP.NET Core application from source, packaging the published output and then pushing that package to Octopus for it to deploy.
Disclaimer: This is my first educational blog post so hopefully I’ve made it relatively easy to follow. I’m always looking to enhance my writing skills so let me know in the comments if I can improve anywhere or if I’ve made any mistakes.
First, if you haven’t already got the .NET Core SDK installed on your build server(s), follow the .NET Core installation guide for your OS. Remember to install the version your application targets. If you don’t have admin access to do this, you can look into the dotnet-install scripts which can be used to perform a non-admin install of the CLI toolchain and the shared runtime.
Next, we need to grab the Octo.exe command line tool to package our build output. Typically, we would use the OctoPack NuGet package but it does not support .NET Core. Instead, we can use the Octo command line tool which just so happens to be built on .NET Core so we can run it on almost anything!
Hop on over to the downloads page for Octopus Deploy and download the relevant Command Line package for your distribution. As of January 2017 the Octo CLI tool is currently targeting .NET Core 1.0.0 so you may need to install the .NET Core 1.0.0 runtime if you haven’t already done so in the previous step. Optionally, you can grab the .NET Core package which includes the required runtimes.
Extract the package to a place of your choosing. As my build server is running Linux I’ve gone with: /opt/octopus/cli/
That’s all of the prerequisites out of the way, now let’s head on over to Jenkins and set up a new build.
Setting up Jenkins Pt. 1
I started off by creating a new freestyle project job for my ASP.NET Core application and pointed it at my git repository. I’ve also set this job up to be executed by GitLab when I push new code. I am going to assume you already know how to set up the SCM parts of the build and jump straight to the actual build itself.
The first thing you’ll want to do is add a new execute shell build step which we’ll use to build and publish the ASP.NET application. You can also run your unit tests here if you have them. This is what my build step looks like:
Here’s a breakdown of each step:
- Restore the dependencies and tools of the project.
- Build all projects with an extension ending in proj using the Release configuration.
- Publish the ASP.NET Core application using the Release configuration to the “published-website” directory.
There’s various other commands and options you can use here so be sure to check out the documentation.
At this stage, it would be a good idea to test the build job to make sure everything works so far. Hopefully everything went okay for you, if not, leave a comment here and I’ll try to help you out. There’s always Google too.
Setting up Octopus Pt. 1
Now that we have a successfully building application we next need to package up our published ASP.NET Core application and push it to the Octopus built-in repository. I use the built-in repository as it doesn’t require any set up and it just makes sense to keep the things we want to deploy with the deployment tool. We’ll need to set up a few things in Octopus Deploy to achieve this:
- Create a new user and set it up as a service account. I called mine Jenkins.
- Create a new API key for this user and note it down as we’ll need to plug it into the Jenkins build.
- Finally, create a new team with the Package publisher role and add the newly created user to it.
Setting up Jenkins Pt. 2
Let’s head back on over to Jenkins and add a second Execute shell build step:
What we’re doing here is first packing our application into a .ZIP file by passing the Octo CLI tool a package ID, version, directory path to package and the package format. I’ve used the Jenkins BUILD_NUMBER here for the version but you can use whatever you like. Octopus used to be all about NuGets but they’ve recently started supporting ZIP which is great because I don’t think applications belong in NuGets.
On the next line, we’re pushing the newly created package to the Octopus Deploy built-in repository by passing it the filename of the package we just created, our Octopus server URL and the API key of the user we created earlier on.
You may want to wrap this step in a condition that this job is being executed against the master branch, for example. Otherwise you may end up with random branches being pushed as packages to Octopus. Of course you may actually want this so I recommend you look into channels & branching on the Octopus wiki.
Save & execute the job, wait for it to complete and then head back on over to Octopus.
Setting up Octopus Pt. 2
Navigate to the packages library in Octopus by clicking on the Library link in the navigation bar. You should now see your newly pushed package under the available packages heading!
From here you can create a new project in Octopus. When setting the project up I recommend checking “Use the version number from an included package” to keep the package and release version numbers aligned. Next, add a new step to the deployment process. Many of the default website deployment templates will have the option to deploy a package from the built-in repository. After setting up your deployment process, set up any configuration values in the variables tab. Be sure to check out the documentation for variable substitution in JSON files. Create a new release and hit that big ol’ deploy button to check that everything works as expected.
Lastly, one thing I like to do with my projects is have Octopus Deploy automatically create a new release when a new package is pushed to the repository. I then combine this with a life cycle that has its first phase and environment set to automatically deploy when a release enters it. That way, pushing a package to the built-in repository will trigger both a release, and a deployment to that environment. I find this really useful as I can check in some code and then check the application out on a real server in just a few minutes.