This series will compare two popular app development automation options, Jenkins and Bitrise. In this post, we’re focusing on their configuration languages, Jenkinsfile and Bitrise YAML. We'll explore their commonalities, unique aspects, and their suitability for diverse project types, beginning with mobile development. Buckle up, because we're about to delve into the heart of their automation magic.
First things first: Jenkinsfile and bitrise.yml are scripts defining the steps your CI/CD pipeline will execute. They tell the platform what tasks to perform and in what order, to build, test, and deploy your code to an app store. They both act as blueprints to execute your CI/CD automation. While their purpose is the same, their approaches are quite different.Â
Groovy vs. YAML: A tale of two languages
Below is a quick overview :
Jenkinsfile
A Jenkinsfile is a text file that contains the definition of a Jenkins Pipeline. Jenkinsfile uses Groovy, a general-purpose, object-oriented language. While Groovy offers flexibility and power for complex workflows, it comes with a learning curve, especially for developers unfamiliar with the syntax. The benefits include:
- Flexibility: Developers across mobile, web, desktop, and other platforms use Jenkins to automate their development workflows. This means that, as a product, Jenkins is generic and unfocussed, but flexible enough to be suitable for various domains. It also means that using Jenkins for mobile will need additional design, development, and maintenance time and will keep the mobile teams busier than they should be.
- Loops and conditionals: Implement complex logic using loops (for, while) and conditional statements (if, else) within the pipeline. This allows for dynamic behavior based on specific conditions like build success/failure or a branch being built.Â
- Error handling and recovery: Define custom actions to handle errors gracefully using try-catch blocks. This allows the pipeline to continue execution, notify stakeholders, or trigger remedial actions.
- Reusable code: Encapsulate common logic or shared functionalities within reusable Groovy functions, promoting modularity and code reuse across different pipelines.
- Scalability: Jenkinsfiles are well-suited for managing complex workflows with numerous steps and integrations.
bitrise.ymlÂ
Bitrise embraces the simplicity of YAML (Yet Another Markup Language, or YAML Ain't Markup Language, depending on whom you ask). This lightweight data serialization format is known for its human-readable, key-value structure. The benefits of using YAML include:
- Simple, readable, and maintainable: The YAML syntax makes learning and managing easier than complex scripting languages. The clear structure and declarative nature enhance understanding and collaboration.
- Pre-built steps and integrations: Connect seamlessly with popular tools and services like GitHub/Gitlab/Bitbucket, Fastlane, App Center, Slack, and more, eliminating the need for custom scripting. Utilize a vast library of pre-built Steps covering common tasks like building, testing, and deploying your app. You can also write your own custom scripts, either via a “Script Step” or via creating a “Step”. Creating a “Step” lets you reuse it across multiple workflows. If you want, you can share this Step with the Bitrise community too.
- Conditionals: Introduce conditional logic in your Bitrise workflows with `run_if` on multiple levels (step - whether the step should run; workflows in pipelines )
- Portability and standardization: YAML files are widely used and easily parsed by various tools and platforms, promoting portability across different CI/CD systems.
- Tailored for mobile apps: Offers features and integrations specifically relevant to mobile app development needs, streamlining your workflow.
Similarities: Holding hands across the CI/CD bridge
Despite their linguistic differences, both configuration files share some core functionalities:
- Code-based definition: Both use code to define the workflow, enabling version control and collaboration.
- Modular structure: Both utilize stages/workflows to group related steps, improving readability and maintainability.
- Declarative nature: They specify what needs to be done rather than the how, promoting a concise and declarative style.
- Structure and hierarchy: Both Jenkinsfiles and bitrise.yml utilize a hierarchical structure to define your CI/CD pipeline, breaking down complex workflows into manageable ones. Semantically, there are two levels of abstractions overall -Â some text
- Phases: Both Jenkinsfile and bitrise.yml allow you to break down your automation into phases, like "build," "test," and "deploy." This modular approach makes understanding and visualizing your CI/CD process easier.
- Tasks: Within each phase, you define self-contained pieces of work such as cloning code, downloading dependencies, building projects, running unit tests, and uploading artifacts.
In essence:
- Steps are the building blocks of your pipeline, specifying the individual actions your CI/CD platform needs to execute. Both configurations use specific commands or function calls, potentially with arguments, to define these steps.
- Stages/Workflows provide a high-level organization for your CI/CD automation. They define the major phases of your CI/CD process. While you configure them differently in Jenkinsfile and Bitrise.yml, they both group-related steps.
- Parallelization is achieved by nesting stages within stages on Jenkins, whereas Bitrise does that by using “Pipelines”, “Stages” and “Workflows”
Inputting Instructions:
While both configurations use code-based definitions, the type of input you provide for each level differs slightly.
Stages/Workflows
- In Jenkinsfile, you don't directly provide input to stages. Instead, you define the stage name and then configure the steps within it using Groovy code blocks.
- bitrise.yml allows you to provide optional configuration options directly for workflows. These options could be environment variables, custom triggers, or conditional logic for workflow execution.
Steps
- Both Jenkinsfile and bitrise.yml use specific commands or function calls within steps to define the actions to be performed. These commands or functions can take arguments or parameters to customize the behavior of the step. For example, for Bitrise’s `Git Clone Repository` step, you would need to input your Git repository URL .
Let’s consider the below examples.Â
Example 1: Running tests on iOS
Imagine you want to run your Xcode tests for your iOS app. Below is how one might achieve this on Jenkins.
stage('Build and Test') {
steps {
script {
// Clone Git repository
git branch: 'master', url: 'https://github.com/your-username/your-repo.git'
// Run Xcode tests
sh 'xcodebuild test -workspace your_workspace.xcworkspace -scheme your_scheme -destination "generic/platform=iOS Simulator,name=iPhone 13"'
// Deploy test results to S3 bucket (replace with your specific details)
sh '''
aws s3 cp test_results.xml s3://your-bucket-name/path/to/results/
'''
}
}
}
That might seem familiar. Now, let’s look at how the Bitrise YAML could look like for the same:
run_tests:
summary: Run your Xcode tests and get the test report.
steps:
- git-clone@%s:
inputs:
- repository_url: https://github.com/your-username/your-repo.git
- xcode-test@%s:
inputs:
- project_path: $BITRISE_PROJECT_PATH
- scheme: $BITRISE_SCHEME
- test_repetition_mode: retry_on_failure
- cache_level: none
- deploy-to-bitrise-io@%s: {}
Here’s an explanation of the syntax:
run_tests:
This is the name of the workflow.- summary: Provides a brief summary or title of what the workflow does.Â
description:
Offers a more detailed description of the workflow's steps.steps
: This is a list of steps that define the actions to be executed as part of the workflow.some textactivate-ssh-key@%s:
This step likely activates an SSH key for accessing a repository or a server. The %s is a placeholder for the version or identifier of this step.git-clone@%s
: This step clones the Git repository. Again, %s is a placeholder.xcode-test@%s
: This step runs Xcode tests. It can take a few several inputs:some text- project_path: The path to the Xcode project or workspace.
- scheme: The scheme within the Xcode project to use for testing.
- test_repetition_mode: Specifies how to handle test repetitions. In this case, it's set to "retry_on_failure," meaning tests will be retried if they fail.
- cache_level: Specifies the caching level. In this case, it's set to "none," indicating that no caching will be used.
deploy-to-bitrise-io@%s
: This step deploys the test report or some artifacts to Bitrise.io.@%s:Â
%s:
This is a placeholder that gets replaced with the actual version number of the step during workflow execution.@:
This symbol acts as a separator between the step name and the version.- For instance, if you're using version 1.2.3 of the
activate-ssh-key
step, the complete step definition in your workflow YAML file would look like:[email protected]
- It's a valid syntax that means "use the latest version". We usually we don't recommend it and the best practice is major version pinning.
Example 2: Running tests on AndroidÂ
Imagine you want to run your Xcode tests for your iOS app. Below is what the syntax looks like for bitrise yaml
run_tests:
summary: Run your Android unit tests and get the test report.
steps:
- activate-ssh-key@%s: {}
- git-clone@%s: {}
- android-unit-test@%s:
inputs:
- project_location: $PROJECT_LOCATION
- variant: $VARIANT
- cache_level: none
- save-gradle-cache@%s: {}
- deploy-to-bitrise-io@%s: {}
android-unit-test@%s
:This step runs Android unit tests. It can take a few inputs:some text- project_location: The location or path of the Android project to be tested.
- Variant: The build variant or flavor of the Android project for which unit tests should be run.
- cache_level: none: The caching level for the step. In this case, it's set to "none," indicating that no caching will be used.
Overall, this configuration defines a workflow that sets up the environment, clones a Git repository, runs Xcode tests with specific settings, and then potentially deploys the test results to Bitrise.io.Understanding these hierarchical structures and input options will help you effectively write and manage your CI/CD pipelines in both Jenkins and Bitrise, ensuring your deployments flow smoothly.
Conclusion: Who wins the configuration file showdown?
There's no clear-cut winner. The "better" choice depends on your specific needs and preferences. Bitrise YAML is like using Legos to build your CI/CD pipeline. Pre-built blocks make it easy and fast to construct common functionalities. Also, the “script-based” legos give plenty room for customization. Jenkinsfile is like using raw wood and tools. It offers more granular control and customization but requires more effort and expertise.
- Choose Jenkinsfile if:some text
- You have a strong preference for and familiarity with Groovy scriptingÂ
- Your project requires complex workflows with heavy customization.
- Choose bitrise.yml if:some text
- You prioritize simplicity and readability in your CI/CD configuration, with enough room for customization later on.
- You want to leverage a rich library of pre-built steps and minimize custom scripting.
- You have a team with diverse technical backgrounds.
In essence:
- Jenkinsfiles offer more flexibility and customization through the power of Groovy scripting, making them suitable for complex workflows across various development domains.
- Bitrise YMLs prioritize simplicity and ease of use with a user-friendly YAML syntax, catering specifically to the needs of mobile app development workflows and offering built-in integrations for common tools.