Is there an option to reference the deployment variables at the pipeline level rather than step?
Asked by Rick Measham:

I have a pipeline on my main branch that deploys to production. It has three key steps: build, deploy, and invalidate cache. All three of these steps are production steps, but I can only put \`deployment: production\` in one step.

If I put it in the \`build\` step then the build is correct and has the right configuration, but gets deployed using the incorrect AWS keys and the wrong cache is invalidated.

If I put it in the \`deploy\` step then the app gets deployed with the right keys, but isn't built for production and doesn't invalidate production cache.

If I put it in the \`invalidation\` step then the wrong build gets deployed using the wrong keys, but the production cache is invalidated.

I'm not sure why it would be more useful in a single step than in the pipeline itself, so I assume I'm missing some method for referencing deployment variables at the deployment pipeline stage.


Answers:
author: Theodora Boudale
body: Hi Rick,

As you have noticed, a deployment environment can be used only once in the bitbucket-pipeline.yml file and the deployment variables are environment specific, which means they can only be used from the step that does this specific deployment.

We have a feature request for what you are asking in our issue tracker:

<https://jira.atlassian.com/browse/BCLOUD-18261>

Please feel free to leave any feedback for our product managers to take into account, upvote the issue and add yourself as a watcher if you'd like to get notified on updates.

In the meantime, the suggestions I can make are the following:

If you don't have the requirement that only admins should run deployments, you could skip the deployments and set repository variables instead of deployment variables. These can be used from all steps in the repository.

Another workaround would be to include all commands in a single step (the deployment step) instead of three. If you need to use a different image for each of the 3 steps, this probably won't work for you. It's more suitable if you use the same image in all three steps, although I understand that it removes a bit of the build granularity.

Please feel free to let me know if you have any other questions.

Kind regards,  
Theodora


author: ktomk
body: Hi Rick,

you described your situation (three step pipeline: build, deploy and cache cleanup) and then asked:
> I'm not sure why it would be more useful in a single step than in the pipeline itself, so I assume I'm missing some method for referencing deployment variables at the deployment pipeline stage.

It is not totally clear to me why it appears like that, so I can only assume / do some guesswork, so please take it more as a comment or feedback and not like "the way to go". Every project is different and this comment is opening up a broader picture (albeit in the hope it allows you to find a solution that works well for your specific setup).

So I try to create a gist out of the question first:
> Why is it useful to have deployment a specific (single) step?

When building a software project (from a revision that normally is), I find it useful to build the overall software package (revision package) and one revision package is the result of the package build function applied to a repository revision.

This package/artifact is (most) useful if it is independent to any kind of deployment. This allows to change deployment over time, also to have more than one way to deploy a package as well to have different deployment targets. So the main reason here is modularity and separation of concerns but also to divide and conquer.

It depends on the project if this is already possible or there is more work to do. E.g. when a project always was deployed to only one target, most often there is no need to do any packaging at all. E.g. there is only deployment, and within the deployment there is the preparation of a (checkout of a) revision, fire up some tooling to create production assets (e.g. if this is a dynamic language project) and then just ship it (deploy).

This all might work fine and does not create any questions/needs. Until it does not scale any longer. Maybe there comes a second system to deploy to, for example a testing system, or commonly earlier, one realizes to actually test a deployment on a development environment.

Even with two deployment targets only it starts to already make much sense to separate the build from the deployment. If the build results in a single binary file (e.g. the revision package) it has a clear interface any following step (here: the deployment step) can follow-up with.

This has two major wins:

1. Speed: Any build revision can be cached
2. Flexibility: The deployment can be changed independent to the build

Drawing the line between building and deploying also makes leaking / hidden abstractions/dependencies more visible / carves them out. For example it might turn out that some deployment target environments hostname or other configuration parameters are part of the revision build still. But they should not, as these are not a build or revision packaging problem but a configuration problem of the deployment or the deployment target environment.

One straight forward way to solve these after they become visible while splitting revision package building from deploying is to create a deployment package in the deployment step. That is the revision package is taken as the input, configuration per the deployment target environment is being applied, the deployment package is created and deployed to the target system.

*(In your scenario this effectively means that building in the first step does not need to have any of the deployment variables.)*

Drawing a distinct line between building the revision package and the deployment step allows to clearly differ between application configuration (e.g. there needs to be an application domain name for the application to work) and environment configuration (in the production environment the application domain name is example.com). Same applies for secrets, database credentials etc (if they are still part of the build step).

When having this look onto things and compare with the steps you outlined in your question:

1. build
2. deploy
3. cache cleanup

Then it would perhaps be that the cache-cleanup step is not necessary.

The first build step produces the revision package as an artifact (which can be archived so that the same revision must not build again next time it's going to be deployed and already in the archives).

The second deploy step takes that artifact, applies the deployment configuration and builds the deployment package and deploys it.

This is just an outline, your mileage may vary and most likely you have some "buts" and "not this" already in mind. From the projects where I encountered such "buts" and especially the woes where the configuration management for the deployment configuration was, let's say it lightly, not so well defined, separating the package build from the deployment routines helped a lot to apply changes and improvements much faster to all areas, be it the project build (which results in the revision package), be it the deployment routines or be it the overall configuration handling of the application. It allowed to better test all of these steps and also sandbox them. In the end doing that what these days is coined as "shift left". Such projects in the end were ported from some rudimentary git push deployment to some unmaintained virtual machine blackbox to multi-stage build packaging (easily cache-able), running bare-metal/dev-box (development) or containerized (Docker/Bitbucket Pipelines), deployments (plural, also deploying a package into development, not just from git checkout as it was the only option earlier but also to test, staging and production environments where it was production only earlier).

When doing this, the main learning for me was to separate the build from the deployment step. This allowed me not only to gain more speed by iterating faster, but also I was able to do the whole packaging locally, which IMHO is the best and fastest build: local build is the best build. Porting the local build to a remote one is pretty straight forward with build containers, this is entirely compatible with Bitbucket Pipelines.

To skip the local build part in context of Bitbucket Pipelines with a bit of setup/run-time overhead is possible with [a tool like pipelines (which executes the pipelines locally)](https://ktomk.github.io/pipelines/). It spares to write scripts for steps / managing docker and allows to test/verify Bitbucket Pipelines steps quickly/iteratively without requiring commits even. That just for some tooling options.

I hope this now quite lengthy answer is good to read and illustrates where I could really benefit from having a dedicated deployment step.

/E: Regardless of why I may think that it's fine to have a single deployment step, limitations on deployment steps/variables is also outlined in:

* [Usage of Deployment variables on multiple steps](https://community.atlassian.com/t5/Bitbucket-questions/Usage-of-Deployment-variables-on-multiple-steps/qaq-p/1103443) (Jun 2019; Atlassian Community Bitbucket Questions)


Tags: bitbucket-pipelines