Unlock Efficient CI/CD: How YAML Anchors Transform Your Bitrise Setup
Tags: bitrise, yaml, cicd, mobile
Published: 2024-05-07 09:11:49

In the fast-paced world of software development, efficiency is king. Whether you're building mobile apps or managing backend services, streamlining your processes can dramatically improve your productivity and reduce time-to-market. This is where YAML, a straightforward yet powerful configuration language, comes into play, especially when used in conjunction with Bitrise, a leading Continuous Integration and Continuous Deployment (CI/CD) platform designed specifically for mobile app development.

YAML's simplicity and human-readable format make it an ideal choice for configuring complex workflows. When paired with Bitrise, it enables developers to create reusable, easy-to-understand pipelines that automate the build, test, and deployment processes. This synergy not only simplifies the initial setup but also enhances ongoing workflow management, ensuring that updates, adjustments, and scaling are just as efficient as initial configurations.

In this blog post, we'll dive deep into the world of YAML and explore how using anchoring and other advanced features can transform your Bitrise workflows. By the end of this post, you'll understand how to leverage YAML to optimize your mobile development processes, making them more efficient and error-free. Let's get started on maximizing your productivity with Bitrise and YAML.

**Sample Workflow (Unoptimised) **

```yaml
---
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

project_type: ios
workflows:
  primary:
    steps:
      - activate-ssh-key@4:
          run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'

      - git-clone@6: {}

      - cache-pull@2: {}

      - certificate-and-profile-installer@1: {}

      - xcode-test@3:
          inputs:
            - project_path: "$BITRISE_PROJECT_PATH"
            - scheme: "$BITRISE_SCHEME"
            - simulator_os_version: "latest"
            - simulator_device: iPhone 11 Pro Max

      - xcode-build-for-simulator@0:
          inputs:
            - project_path: "$BITRISE_PROJECT_PATH"
            - scheme: "$BITRISE_SCHEME"
            - simulator_device: iPhone 11 Pro Max

      - cache-push@2:
          inputs:
            - cache_paths: |-
                ./Pods -> ./Podfile.lock
                ./Carthage -> ./Cartfile.resolved

      - slack@3:
          inputs:
            - webhook_url: "$SLACK_WEBHOOK_URL"
            - channel: "#builds"
            - message: "Build finished successfully!"
```

As you can see there are a few steps in this **primary** workflow. The more workflows you have with similar steps the bigger your yaml will get.

**Anchoring in YAML**

YAML, or "YAML Ain't Markup Language", is a human-readable data serialization format. It is commonly used for configuration files and in applications where data is being stored or transmitted. One of the most powerful features of YAML is its support for **anchors** and **aliases**, which allow for the reuse of defined content across multiple parts of the document, significantly reducing repetition and making maintenance easier.

**Defining Anchors and Aliases**

Anchors in YAML are defined by using the **&** symbol followed by a name, and they mark a section of the document for future reference. Aliases, on the other hand, are references to these anchors, made using the * symbol followed by the name of the anchor they refer to.

**How Anchors and Aliases Help**

Anchors and aliases are particularly useful in large configuration files with repeated structures or values. They not only keep the YAML file DRY (Don't Repeat Yourself), but they also simplify updates: change the value at the anchor, and all associated aliases automatically get updated. This is invaluable in environments like CI/CD workflows where multiple, slightly different configurations might be used repeatedly.

**Simple Examples of YAML Anchors and Aliases**

Here’s a simple example to illustrate how anchors and aliases work in YAML:

```yaml

# Define an anchor for a reusable component
reusable_settings: &default_settings
  check_interval: 60
  start_on_boot: true
  retry_limit: 5

# Using alias to reuse the settings
service_one:
  <<: *default_settings
  name: "Service One"

service_two:
  <<: *default_settings
  name: "Service Two"
  check_interval: 90  # Override the check_interval for this service only
```

In this example, **reusable_settings** is defined with an anchor &**default_settings**. It is then used in the configurations for **service_one** and **service_two** using the alias ***default_settings**. The <<: ***default_settings** syntax is a special merge key indicating that the elements defined in the anchor should be merged into the current map.

For service_two, you can see how easy it is to override a specific setting (check_interval) that was included via an alias, demonstrating how flexible this feature can be.

**Applying Anchors and Aliases in Bitrise YAML Configurations**

When applied to Bitrise YAML configurations, anchors and aliases can manage repeated patterns like environment variables, build settings, or even entire workflows efficiently. This allows developers to simplify complex CI/CD configurations and ensure consistency across different stages of the development and deployment process.

Using anchors and aliases effectively can lead to cleaner, more manageable, and less error-prone configuration files, making it a best practice worth adopting in your Bitrise YAML configurations.

Here is a revised version of our bitrise yaml using 2 workflows.

```yaml
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

steps:
  - step: &xcode-test
      xcode-test@3:
          inputs:
            - project_path: "$BITRISE_PROJECT_PATH"
            - scheme: "$BITRISE_SCHEME"
            - simulator_os_version: "latest"
            - simulator_device: iPhone 11 Pro Max
  - step: &xcode-build-for-sim
      xcode-build-for-simulator@0:
          inputs:
            - project_path: "$BITRISE_PROJECT_PATH"
            - scheme: "$BITRISE_SCHEME"
            - simulator_device: iPhone 11 Pro Max
  - step: &cache-push
      cache-push@2:
          inputs:
            - cache_paths: |-
                ./Pods -> ./Podfile.lock
                ./Carthage -> ./Cartfile.resolved
  - step: &slack-message
      slack@3:
          inputs:
            - webhook_url: "$SLACK_WEBHOOK_URL"
            - channel: "#builds"
            - message: "Build finished successfully!"


project_type: ios
workflows:
  primary:
    steps:
      - activate-ssh-key@4:
          run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
      - git-clone@6: {}
      - cache-pull@2: {}
      - certificate-and-profile-installer@1: {}
      - *xcode-test
      - *xcode-build-for-sim
      - *cache-push
      - *slack-message
  secondary:
    steps:
      - activate-ssh-key@4:
          run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
      - git-clone@6: {}
      - cache-pull@2: {}
      - certificate-and-profile-installer@1: {}
      - *xcode-test
      - *xcode-build-for-sim
      - *cache-push
      - *slack-message
```

The use of YAML in conjunction with Bitrise offers a robust framework for automating and optimizing CI/CD pipelines, especially for mobile app development. By incorporating YAML's anchoring and aliasing features, developers can greatly reduce redundancy within their configuration files, leading to cleaner, more manageable, and more reliable setups.

We encourage you to experiment with these YAML techniques in your projects. Start small by refactoring a single workflow, see the benefits, and gradually expand your use of these features. As you become more comfortable with YAML's capabilities, you'll find that many aspects of your CI/CD process can be optimized, not just by automating tasks, but by organizing them more effectively as well.

Happy coding, and may your builds always be successful!