Releases, deployments and variable snapshots
As an automated release management and deployment solution, Octopus makes a distinction between a project, a release and a deployment.
A project is kind of like a template for what to deploy. It contains:
- Steps: the NuGet packages you plan to deploy, and where you want to deploy them to. You only select the NuGet package ID, not the version number.
- Variables: per-environment configuration parameters that are made available to PowerShell scripts or XML configuration files.
The idea is that you define your project once, and then create many releases of that project.
A release, then, is simply a selection of NuGet packages (ID and version). Since a project can have many steps, a single release will often have many packages. For example, release "1.0" of the "Contoso Website" project might reference NuGet package "Contoso.Web.1.0.315.nupkg" and "Contoso.Mailer.1.0.201.nupkg".
A deployment is what happens when a release is deployed to an environment (such a Dev, Test, Staging, or Production). A single release can be deployed many times to many environments.
The nice thing about this design is that you can be guaranteed that the packages you are deploying to Test are the same as the packages you are deploying to Staging and Production. The release is really a "release candidate", which has a lifecycle that the system tracks. The Octopus web UI allows you to view this lifecycle of a release - who created it, and who deployed it to each environment.
When a release is created, it is read-only. You can edit the release number (since it's really just a label) and the release notes, but you can't change the package versions once a release has been deployed.
A project, on the other hand, can be edited. You might decide to change the project variables. Or you may decide to add a new step, i.e., a new NuGet package to deploy.
The problem is, what happens when you try to deploy an old release, after editing a project? You might have created release 1.0 with two NuGet packages, and deployed that to Staging. Then, someone edits the project by adding a new NuGet package to deploy. Your 1.0 release didn't have a selected version for that NuGet package, and you didn't deploy it in Staging, so you don't want to deploy it to Production either.
To solve this, Octopus creates a "snapshot" of the project steps and variables when a release is created. You can create release
1.0, and then modify the project to prepare for release
1.1, without breaking release
1.0. This seems pretty intuitive to most people, as it allows old releases to be unaffected by changes for new releases. The snapshot contains:
- A copy of all of the variables
- A copy of all of the steps (NuGet package ID's and versions)
The surprising part
Although this seems like a logical approach when you think about it, it is a little unexpected and tends to catch people out. Someone might create a release, and then edit a variable, only to find that the new variable isn't having any effect when they re-deploy the release. The answer is simply to create a new release (e.g.,
1.0-patch1), which will cause any changes to be picked up in a new snapshot.
There are two changes that are coming to Octopus to help resolve this problem. Firstly, when editing the steps or variables, we'll show a note to tell people they need to create a new release. This way, it won't come as such a surprise when re-deployment doesn't have any effect.
Secondly, I'm working on ways to reduce the amount of times a project needs to be edited in the first place. A common problem is that when you add a new machine - say, a new production front-end web server to help handle load - you need to edit the project steps to specify that your web package must be deployed to the new machine. This also means creating a whole new release.
In future, the steps will reference user-definable "roles", such as "web-server", "email-sender", "calculation-worker" and so on. When you add machines to an environment, you'll be able to "tag" a machine as serving one or more of those roles. Variables will work the same, as a variable can be scoped to a role. This way, machines can come and go from an environment without affecting any existing releases. Just add the machine to a role, re-deploy the current release, and you'll be good to go.
This introduction of "roles" is quite a big change to the architecture of Octopus, especially because we need to make sure existing releases (which contain snapshots of steps pointing directly at machines) aren't broken in the process. I'm working on this change in a branch and I'll continue to blog more about the change as it progresses.