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.
7 thoughts on “Build & deploy ASP.NET Core applications with Jenkins and Octopus Deploy”
Thanks for this article.
I’d like to ask, how did you install/get octopus (/opt/octopus/cli/Octo.dll) on linux? I’ve installed dotnet core but could’nt get the octopus library for linux.
It’s listed under the Prerequisites section of the article.
Go to: https://octopus.com/downloads -> Command Line -> Select platform -> .NET Core (GZip), either download it and extract manually or if you’re using a terminal, copy the download link and do the following:
Create the directory structure: /opt/octopus/cli/
tar -xf OctopusTools.4.22.0.portable.tar.gz -C /opt/octopus/cli/
dotnet $octo_dll push –package $created_nuget_package –server $octopus_server –apiKey $octopus_api_key
I get the error:
Octopus Server returned an error: Package does not contain a manifest.
Error from Octopus server (HTTP 500 InternalServerError)
Exit code: -7
Could it be an issue with my old octopus version? I’m using 22.214.171.1244
Octopus Deploy used to only support NuGet packages which requires a manifest file. This changed in Octopus 3.3 when they added support for ZIP packages.
Hey Ben, i’m trying to utilize the dotnet build command however it isn’t even picking it up in my output in jenkins. I set an env variable pointing to the dotnet.exe on the server :
%dotnet_exe% build –configuration “Release”
Try putting some arbitrary command in before dotnet build and see if it executes it just to rule out the build step being weird. Have you tried not using an env variable?
FYI, if you want to do this in TeamCity, Windows agents…(as a stop-gap we installed the dotnetcore octo.exe to c:\tools\octopuscli on each agent)
Simple way to test this and prove it works – create a new build step (powershell) and dump this in.
$buildNum = "%build.number%"
$projectName = "ProjectName.API"
$projectDir = ".\$projectName"
dotnet build --configuration "Release"
dotnet publish --configuration "Release" $projectName.csproj --output $projectName
dotnet c:\tools\octopuscli\Octo.dll pack --id $projectName --version "$buildNum" --basePath $projectName --format=zip
dotnet c:\tools\octopuscli\Octo.dll push --package "$projectName.$buildNum.zip" --server https://octopus.domain.com/ --apiKey API-KEY12345
Thanks for doing the heavy lifting!!