In the previous post, I explained the basics of getting Bitrise up and running with a very basic build. I explained the various default steps that are included and the basics of triggers. Now I want to get more in-depth on workflows and triggers.
What is a workflow?
At the very heart of every workflow is the YAML file, bitrise.yml. You can access this file through the Manage Workflow option. This is, in essence, your Bitrise configuration. You can freely distribute this file if you want a client to setup your same build configuration elsewhere; we actually just did this for a client that we finished an engagement with.
Here is a chunk from the workflow we created previously:
If you read through this and consider the current configuration, you can see that parts easily map to the Web components you have been interacting with. I want to call attending to default_step_lib_source.
One of the great things about the way Bitrise is organized is the way steps are used. They are basically downloaded when each step is executed its source is downloaded from a Git repo (in this case the default is listed above). This being the case, you can add your own steps or customizations of existing steps from your own repository; this is also what allows Bitrise to run locally via its CLI implementation. My plan is to go through how to create your own custom steps in a later article; however, this is still something I am learning.
At the end of the day, a workflow really is just a collection of these steps downloaded on command and executed against the code stored on the virtual machine.
Building a a Workflow
Bitrise does a great job giving you a working workflow out of the box. The default one does what you expect and can serve well for continuously integrating your code. In general, the start of a workflow must do the obvious things such as authenticate itself, get the code, and then perform any other operations ahead of building it; for example with a Xamarin project you are going to need to use the User Management step. However, once you get beyond the basic case, you might want to consider a few of the features that Bitrise offers.
The first of these is the idea of subworkflows. With subworkflows, larger workflows can be comprised of smaller workflows; its like coding, we want to reuse things where appropriate. And if something changes with the build process, better to change it in one area than across many.
To show you what I mean, click the + icon next to primary in the Manage Workflow view. Name of the new workflow whatever you like (I will name mine prepare). Once this is complete you will see this new workflow listed next to primary.
By default, Bitrise copies the steps in the currently selected workflow into the new one, so this will come prepopulated and look the same as primary.
For this exercise, go back to primary and delete all of the steps (select each one individually and use the trash can icon) with the exception of Certificates and profile installer, Xamarin Builder (assuming your repo is a Xamarin project) and Deploy to Bitrise.io.
Hit Save and then use the interface to rename the primary workflow to CI. To do this, simply click on the workflow name in the tab, Bitrise will allow you to rename it inline.
Before we go farther, I want to point out a naming convention that I like to use. Main workflows (those that will be targeted by triggers) are capitalized, sub workflows (those that will not be targeted by workflows) use lowercase. This is my personal convention, you can use it if you like or develop your own; consistency is the most important thing.
Go now to the prepare workflow and remove the steps that are listed in prepare. Return to the CI workflow and create a pre-workflow phase. This is done by adding a subworkflow to run before the Main steps. (Add prepare).
I recognize, and so does the Bitrise team, that this looks awkward as the very first step in ANY workflow (sub or otherwise) is a “Preparation” step (there is also a clean up at the end). These steps are both smart enough to detect the presence of pre and post workflow phases and WILL NOT run until those workflows complete.
Hit Save and you are just about ready, head over the to Triggers configuration and clean up any extraneous triggers created by Bitrise (by default a trigger is created for each workflow name and that name is assumed to match a remote branch in your Git repo). For now you can add a specific branch name from your repo or stick with * to match all. The important thing is that this trigger kick off the CI branch (so specify CI for Triggers Workflow).
About Pull Requests and Order of Operations
While adding your triggers you may have noticed a couple things: 1) Triggers can be added in any order you desire and 2) when creating a trigger you have the option to select whether a pull request triggers the workflow. Let’s talk about these.
Bitrise triggers are order based which is to say the first pattern matched is the one that fires. So in this case, having * first would prevent any other workflow from ever firing; * matches all so the matcher would never get passed this match.
Many teams utilize pull requests (West Monroe certainly does) as a way perform code reviews of code entering the code base. At West Monroe, we often do these when code enters feature branches allowing Senior Developers to review the code and ensure that nothing heinous is being coded. When you check this box, these pull requests will triggers a build as well. This is a very useful feature as it allows the build server to validate the code will be valid if allowed into the code base (including running the unit tests). This greatly speeds up our process and allows the reviewers to focus on the code and not have to worry about whether it builds or not.
How does West Monroe use workflows?
At West Monroe our workflows are triggered with this configuration, though it varies project to project:
- task/* – triggers CI – used to validate a remote task branch being developed for a feature. This is optional and only used on certain projects
- feature/* – triggers CI (pull requests build) – validates the feature and generates a developer build that can be downloaded by local QA staff (not distributed to the client)
- bugfix/* – triggers CI – similar to task, identifies code which is applied to fix a known bug found in a stable version of the code
- QA – triggers QA – kicked off by a merge of code into itself. Generally this is one or more features being added to this branch. This process generates a build that the client receives for QA
- stable – triggers Stable – kicked off by a merge of code into itself. This code is one or more approved feature branches (code that has passed QA testing). This generates a build for client stakeholders and the team demos from this code at the conclusion of each sprint
By utilizing workflows and subworkflows, West Monroe is able to managed an automated build and distribution process driven by the fundamentals of Scrum and Agile. The clients receives builds throughout the sprint and provides validation and feedback that guides the team. At the end, the client receives a stable build which can be shown to stakeholders to yield additional feedback. Because only Done items reach this build, the client is able to dictate the features in each build.
App vs Workflow Environment Variables
The final point for this entry is in regard to variables. When you setup your Bitrise project the process forces you to define environment variables, depending on your target platform, for Xamarin they include: BITRISE_PROJECT_PATH, BITRISE_XAMARIN_CONFIGURATION, BITRISE_XAMARIN_PLATFORM. But you can define more, and I often do, especially when you start including other integrations outside the norm (talk about this next time). But what is important is to understand scoping.
My general practice is the default values for my variables are based on what I would need in CI (my lowest level build). Once I get into other workflows (QA, stable, master, etc) I would need different values that I want to feed the build process (for example, a different Xamarin Configuration to force my code to build a certain way) to alter it for that build. In cases like these you can use Manage env. vars to override your existing variables.
Regrettably, and Bitrise knows this, this interface is awful. I recommend copying the name of the variable you wish to override before entering since you will have to specify it exactly to get the override. Remember, even in subworkflows, it is the local Environment variables which take precedence. This being the case, the best way to avoid confusion is NEVER define environment variables for subworkflows, it will cause you pain and create confusion.
You can also define static values in your steps directly, this makes sense in some cases, but its something I try to avoid. It should be noted that Bitrise does not, as far as I know, have a secure way to store credentials, so for now the best way is to limit access to the build server.
So that is it for workflows, hopefully that helps explain them. Until next time.