CI/CD as code vs. configuration file

  • Published

To remain competitive as a company, it is crucial to deliver software quickly and reliably. Continuous Integration and Continuous Delivery CI/CD can help with this. It enables developers to continuously integrate changes and perform tests. This requires a pipeline, which is often defined via a configuration file or clicked together via a graphical interface. If complex projects have to be processed, configuration files lead to a lot of work. CI/CD-as-code is an alternative to overcome this challenge. We show how this works and how we have implemented it at Cloudomation in this blog post.

The CI/CD configuration file

For completeness, an overview of what a configuration file is in the CI/CD context:

A CI/CD configuration file is a file typically used by a CI/CD tool to configure a continuous integration (CI) and continuous delivery (CD) software pipeline. This file usually contains instructions for the CI/CD tool to automate the build, test, deployment and release process for the software.

The CI/CD configuration file can be in different formats depending on the CI/CD tool used. Some examples of CI/CD tools are Jenkins, Travis CI, CircleCI, GitLab CI/CD and GitHub Actions. These tools each have their own configuration file formats.

The configuration file contains instructions such as

  • which build tools to use,
  • which tests to run and which environments to use for deployment.

Here’s an example, how such a file could look like (GitLab CI/CD (.gitlab-ci.yml)):

stages:
  - build
  - test

build-code-job:
  stage: build
  script:
    - echo "Check the ruby version, then build some Ruby project files:"
    - ruby -v
    - rakestages:
  - build
  - test

build-code-job:
  stage: build
  script:
    - echo "Check the ruby version, then build some Ruby project files:"
    - ruby -v
    - rake

test-code-job1:
  stage: test
  script:
    - echo "If the files are built successfully, test some files with one command:"
    - rake test1

test-code-job2:
  stage: test
  script:
    - echo "If the files are built successfully, test other files with a different command:"
    - rake test2

In addition to configuration via configuration file, it is also possible to set up pipelines via a graphical user interface (GUI). According to Gitlab, this poses several challenges:

  • Developers cannot work together
  • It is difficult to fix problems
  • It is difficult to undo changes to the last known configuration.
  • Pipelines are prone to failures

Configuration files are already more flexible here. But let’s take a step back.

Why CI/CD configuration files are difficult for complex processes

CI refers to the build of a component. It is common for the configuration to be available as a configuration file in the component’s repository.

CD stands for Continuous Delivery. This can mean that an artefact is stored. CD also stands for Continuous Deployment. This is about the installation of a component. And this is where it gets complicated. In a configuration file, it is often not possible to map the complexity and logic that is needed. For example, if several components have to be installed. The following questions arise: Are the components compatible with each other? Is a configuration needed? How should errors be handled? How do rollbacks work, etc.? If several software versions are added, which in turn consist of different components, the complexity increases further.

How can this be solved better? This is where configuration as code comes into play.

What does CI/CD-as-code mean?

The definition from Gitlab:

Pipeline as code is a practice of defining deployment pipelines through source code…

In a nutshell: “As Code” means that a file is created for the configuration, which is defined by source code.

What is the difference and the advantage of defining the pipeline this way and not by configuration file?

Difference configuration file vs. as code

As described earlier, a CI/CD configuration file is a file that defines the steps for the build, test and deployment processes of a software and is usually used by a CI/CD tool to control the automated build and deploy process.

These configuration files are subject to a fixed schema predefined by the manufacturer. In the example above (.gitlab-ci.yml), each block contains an optional “stage” key and a “script” key. The maximum complexity that can be mapped in this way is limited by the schema. Pipelines that contain loops, conditions or error handling can often only be mapped with difficulty or not at all with the configuration file schema.

CI/CD-as-code, on the other hand, has no fixed, predefined schema. Due to the flexible syntax of a programming language, even high complexity can be mapped elegantly.

Here is an example of our internal pipeline. This script runs after a component was successfully built. Its purpose is to find Cloudomation installations which are configured to be automatically updated with new builds of the component from a specific branch:

"""
Create deploy records for all auto-update installations which can be updated with the provided build
"""

""" # Cloudomation metadata:
project_id_project:
   name: CI/CD
"""

import flow_api

def handler(system: flow_api.System, this: flow_api.Execution):
   inputs = this.get('input_value')
   push_info = inputs['push_info']
   build_id = inputs['build_id']
   is_nightly_build = inputs.get('is_nightly_build', False)
   repository_name = push_info['repository_name']
   
   if repository_name.endswith('-content'):
       installations = this.connect(
           'masterdata',
           name='find updateable installations',
           fetch=(
               '''
               SELECT installation.id
               FROM content
               JOIN workspace
               ON content.workspace_id = workspace.id
               JOIN installation
               ON workspace.installation_id = installation.id
               WHERE installation.is_auto_update IS TRUE
               AND content.branch_name = $1
               AND content.repository_name = $2
               '''
           ),
           params=[
               push_info['branch_name'],
               push_info['repository_name'],
           ],
       ).get('output_value')['result']
   else:
       installations = this.connect(
           'masterdata',
           name='find updateable installations',
           fetch=(
               f'''
               SELECT installation.id
               FROM installation
               WHERE installation.is_auto_update IS TRUE
               AND "{repository_name}_branch" = $1
               '''
           ),
           params=[
               push_info['branch_name'],
           ],
       ).get('output_value')['result']

   deploy_executions = []
   for installation in installations:
       # ensure the installation is not powersaved
       try:
           this.flow(
               'powersave-installation',
               installation_id=installation['id'],
               scale=1,
           )
       except flow_api.DependencyFailedError:
           # we want to be able to deploy to an installation which is not running (eg broken)
           pass
       deploy_executions.append(this.flow(
           'create-deploy',
           build_id=build_id,
           installation_id=installation['id'],
           is_nightly_build=is_nightly_build,
           wait=False,
       ))
   this.wait_for(*deploy_executions)
   return this.success('all done')

In summary:

  • A CI/CD configuration file is limited by the given schema and cannot map any -or only very limited- additional logic.
  • CI/CD-as-code uses the syntax of a programming language and can map any additional logic.

Advantages of CI/CD-as-code

  • The CI/CD pipeline can be seen as another component of the software.
  • The pipeline has versions/branches. All changes can be tracked. A roll back is possible and all other advantages of a version control system are present.
  • Most importantly, the code can contain user-defined logic. Several configuration files often need to be created for different processes, whereas when used as code, only one additional “if statement” is sufficient.

Cloudomation and CI/CD-as-Code

At Cloudomation, for each major version there’s a pipeline. For example: If we release version 1, the pipeline is also released internally. Now the work for version 2 (software and pipeline) starts. If there’s a change in version 2, only the respective pipeline has to be changed.

The pipeline is defined as code and synced via Git. The content from Git can be loaded into the Cloudomation-Workspace at anytime. This means that the desired pipeline is immediately available.

You can also use Cloudomation as CI/CD tool. Check out this comparison: Cloudomation as alternative to jenkins.

Summary

  • A CI/CD configuration file is a file typically used by a CI/CD tool to configure a pipeline. This file usually contains instructions for the CI/CD tool to automate the build, test, deployment and release process for the software. These configuration files are subject to a fixed schema predefined by the manufacturer.
  • “As code” means that a file is created for the configuration, which is defined by source code. CI/CD-as-code has no fixed, predefined schema. Due to the flexible syntax of a programming language, even high complexity can be mapped elegantly.
  • At Cloudomation, we work with Pipelines as code. Are you searching for the right CI/CD software? Get in touch with us – we show you how Cloudomation can help you.

Subscribe to the Cloudomation newsletter​

Become a Cloudomation Insider. Always receive new news on “Remote Development Environments” and “DevOps” at the end of the month. 




    Johannes Ebner

    Marketing Manager