Continuous Integration with Branches and Schedules

You know the drill.

"Use branches", they said. "It'll be fun", they said.

Yeah. But what about building and deploying these branches to a test server without merging a branch first to the staging- or even master-branch only to deploy this fabulous and 100% bug-free feature?

Well, thanks to GitLab and a little YML file, that's easy.

By the way, I'm sure this works quite similar with GitHub, Bitbucket and so on. We're just using GitLab in the office, that's why I'm presenting you with the GitLab solution.

In essence, you have to figure out how you start a continuous integration pipeline or a job within your CI solution first.

In GitLab, you create a .gitlab-ci.yml file and put this into the root directory of your repository, probably next to your .gitignore.

The necessary script looks pretty similar to anything you would put into a batch file.

Want to build a JavaScript project? Probably run npm install first.

Building something in .NET Core? Run dotnet restore and then dotnet publish -c Debug maybe.

Here's a small example for a .NET Core project:

stages:
    - deploy

deploy:dev:
  stage: deploy
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"
    - echo Deploy to your dev-machine
    - '"C:/Program Files (x86)/IIS/Microsoft Web Deploy V3/msdeploy.exe" ...

In this example, stages tells you what kind of stages are to expect in this script. You could also say that every stage is a certain job. Then, deploy:dev is an arbitrary name for the subsequent part.

There you set the current stage and then you write the script. As you can see, the script contains commands you would also run in a batch file or you would type into the terminal of Visual Studio Code manually.

This is the hardest part. Finding the right commands to build and deploy the application with your choice of technology.

The easy part is to build a script that behaves differently depending on the current branch.

Let's add the following line.

rules:
   - if: $CI_COMMIT_REF_NAME == "dev"

The complete script would look like that.

stages:
    - deploy

deploy:dev:
  rules:
   - if: $CI_COMMIT_REF_NAME == "dev"
  stage: deploy
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"
    - echo Deploy to your dev-machine
    - '"C:/Program Files (x86)/IIS/Microsoft Web Deploy V3/msdeploy.exe" ...

With rules you can define when your script should run. In this particular case, it would only run if you have committed your code changes to the dev branch.

The big issue now is, what happens to the other branches?

Do you need different YML files for different branches? And do you have to pay attention when you want to merge these branches?

And as we all know, paying attention ends catastrophically eventually.

Fortunately, this is a problem of the past.

Just add the script for your other branch to the same file.

stages:
    - .pre
    - deploy

build:master:
  rules:
   - if: $CI_COMMIT_REF_NAME == "master"
  stage: .pre
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"

deploy:dev:
  rules:
   - if: $CI_COMMIT_REF_NAME == "dev"
  stage: deploy
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"
    - echo Deploy to your dev-machine
    - '"C:/Program Files (x86)/IIS/Microsoft Web Deploy V3/msdeploy.exe" ...

In the example above, the application would only be built if a change was committed to the master branch.

But maybe you also want to deploy the master branch automatically. Not with every commit, but maybe every night or every week?

Well, in that case, you would configure a scheduler and then add another script that should only be called, when the scheduler started the job.

This is how a schedule looks like in GitLab:

Scheduler

Notice that we define a variable CI_PIPELINE_SOURCE and set the value to schedules. We can then use this variable in a suitable script.

stages:
    - .pre
    - deploy

build:master:
  rules:
   - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE != "schedules"
  stage: .pre
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"

deploy:master:
  rules:
   - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedules"
  stage: deploy
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"
    - echo Deploy to your production machine
    - '"C:/Program Files (x86)/IIS/Microsoft Web Deploy V3/msdeploy.exe" ...

deploy:dev:
  rules:
   - if: $CI_COMMIT_REF_NAME == "dev"
  stage: deploy
  script:
    - "dotnet restore"
    - echo Build Debug
    - "dotnet publish -c Debug"
    - echo Deploy to your dev-machine
    - '"C:/Program Files (x86)/IIS/Microsoft Web Deploy V3/msdeploy.exe" ...

With $CI_PIPELINE_SOURCE == "schedules" we know that the job was started by a scheduler. And since this scheduler would probably run at 3 am, we can calmly deploy our build to production without interrupting the flow of our colleagues.

Now, before the experts are yelling at me. I know that this script isn't perfect. There are more details to explain, like the .pre stage or why I wouldn't connect the jobs in the example scripts. I know, I know, I know!

This should just give you an idea of how things could be configured. For details, please have a look at the official documentation of your DevOps platform. Thank you.

Alright. I hope this helped and you can finally merge your branches without fear.

Happy coding!

Image created by katemangostar on freepik.com.


But wait, there’s more!

© 2020 Patrick God - Legal | Privacy