If you've ever found yourself tangled up in the complexities of setting up CI/CD pipelines, you're familiar with the challenge of mapping code events with build triggers. At times, you may wish to initiate a few checks, while at other times, you prefer to execute several checks at once. It can be slow and, at times, frustrating to ensure that only those CI checks are triggered that are relevant to the code change.
A better, more scalable approach? Target-based triggering.
What is target-based triggering?
In a nutshell, it’s a smarter way to run your builds based on specific code events happening in your Git repositories. Whether it's a code push, a pull request, or even a comment on a pull request, you can now trigger multiple workflows or pipelines independently, based on conditions you define. This lets you run all the relevant parts of your CI together based on the scope of code changes. For DevOps teams juggling multiple platforms (like iOS and Android) or large monorepos, this targeted approach brings greater control and efficiency to your CI/CD process.
What’s new in Bitrise
New “triggers” property for workflows or pipelines: Workflows and pipelines now include a dedicated triggers property, which organizes triggers under specific event types. For example
1workflows:
2 ui_tests:
3 triggers:
Example pull request trigger:
1workflows:
2 ui_tests:
3 triggers:
4 pull_request:
5 - target_branch: "main"
6 - comment: "[workflow: ui_tests]"
The above example shows two PR triggers for the ui_tests
workflow: one that targets the main
branch and another for a PR comment [workflow: ui_tests]
.”
You can check out detailed documentation here.
You can also set up triggers via the UI by navigating to any workflow or a graph pipeline. The right-hand panel will display a “Triggers” tab. Add Push, Pull request, or Tag triggers for the selected workflow or graph pipeline. Check out documentation here.
Benefits of target-based triggering
Let's dive into the benefits that make target-based triggering a must-have in your CI/CD toolkit:
- Smart multitriggering of workflows or pipelines: You can now independently trigger multiple workflows or pipelines based on very specific conditions, enabling more granular control over build processes. You will no longer need to write complex config to achieve this.
- Simplified configuration: Trigger-related configuration is now cleaner and more intuitive. Workflow/pipeline context is inherent, so you no longer need to specify it within each trigger. Field names are also more straightforward – we removed event-specific prefixes from filter field names, such as
pull_request_
orpush_
, simplifying them tobranch
,commit_message
, etc., since they are already within a context-specific section. - Better support for complex projects: Particularly beneficial for projects with monorepos, the new model allows for precise targeting of builds based on changes to specific parts of the codebase, such as different platforms or shared components.
- Increased efficiency: Configuring multiple workflows from a single Git event can save time and reduce maintenance efforts. This eliminates duplicate steps and allows you to focus on improving your codebase.
Some real-world examples of multitriggering (where target-based triggering wins)
Example 1: Triggering multiple pipelines from the same branch
Let's break down the scenario:
You have three pipelines:
pipeline-tests
: Runs tests (triggered by pushes to feature branches).pipeline-builds
: Creates builds (triggered by pull requests).pipeline-release
: Handles releases (triggered by release tags).
Now, when you push to the release
branch, you want all three pipelines to run independently. This allows you to test, build, and deploy in one smooth, automated process. Previously, this was difficult, often requiring you to merge the pipelines into one, which, as we discussed earlier, leads to complexity and inflexibility.
The good news? Bitrise now allows you to easily configure this. You can define separate triggers for each pipeline, even if they all point to the same branch. This means you can have your pipeline-tests
, pipeline-builds
, and pipeline-release
pipelines all triggered simultaneously by a single push to the release
branch. This unlocks powerful CI/CD workflows, enabling parallel processing, customized workflows, and independent pipeline management. No more monolithic pipelines!
The YAML configuration for this is surprisingly simple. Let's take a look:
1pipelines:
2 pipeline-tests:
3 triggers:
4 push:
5 - branch: "feature/*"
6 - branch: "release"
7 stages:
8 -
9
10 pipeline-builds:
11 triggers:
12 pull_request:
13 - target_branch: "*"
14 push:
15 - branch: release
16 stages:
17 -
18
19 pipeline-releases:
20 triggers:
21 push:
22 - branch: release
23 tag:
24 - name: "*"
25 stages:
26 -
Example 2: Selective multi-triggers for libraries in a monorepo
Let's say you have a monorepo with two libraries, library-a
and library-b
, each with its own pipeline: pipeline-a
for library-a
and pipeline-b
for library-b
. Seems simple, right? But how do you ensure the right pipeline runs when changes are made? Let's look at some common scenarios and how to get Bitrise to behave as expected.
Scenario 1: Change in library-a
only
What should happen: Only pipeline-a runs. pipeline-b should remain untouched.
Scenario 2: Changes in both library-a
and library-b
What should happen: Both pipeline-a
and pipeline-b
should run independently. pipeline-a
builds and tests the updated library-a
, while pipeline-b
builds and tests the updated library-b
Scenario 3: Changes in a shared library (library-shared)
What should happen: Both pipeline-a
and pipeline-b
should run independently. Since both depend on the shared library, both need to be rebuilt and tested.
So, how do we solve this? The key is to give each library its own, independent trigger, based on changes within its own directory (and the shared library if applicable). This way, Bitrise can correctly identify which pipelines need to run, regardless of the order of changes. Here’s the YAML config in a covers-all-the-scenarios kind of a setup.
1pipelines:
2 pipeline-a:
3 triggers:
4 push:
5 - branch: develop
6 changed_files: path/to/library-a/*
7
8 - branch: develop
9 changed_files: path/to/library-shared/*
10
11 pipeline-b:
12 triggers:
13 push:
14 - branch: develop
15 changed_files: path/to/library-b/*
16 - branch: develop
17 changed_files: path/to/library-shared/*
What does good look like for using target-based triggers?
1. Define clear and specific trigger conditions:
- Be precise: Avoid overly broad triggers that fire unnecessarily. Specify branches, tags, and file changes as accurately as possible. Don't trigger a full build if only documentation has changed.
- Use wildcards and regex effectively: Wildcards (
*
) are great for simple pattern matching (e.g.,feature/*
). Regular expressions (regex:
) offer more powerful pattern matching for complex scenarios (e.g., semantic versioning for tags). - Combine conditions: Use multiple conditions (e.g., branch and changed files) to create very targeted triggers. All conditions must be met for the trigger to fire.
- Use
changed_files
strategically: This is your best friend for monorepos or projects with distinct modules. Trigger builds only when relevant files are modified.
Example 1: Feature branch builds with file filtering
1workflows:
2 feature-build:
3 triggers:
4 push:
5 - branch: 'feature/*' # Wildcard for any feature branch
6 changed_files:'src/components/*' # Only if changes in component directory
Why it's good: This trigger targets any branch starting with "feature/" but only triggers if changes are made within the src/components
directory . This is efficient because it avoids building for changes in documentation or other unrelated parts of the repository.
Example 2: Pull request validation with specific label
1workflows:
2 pr-validation:
3 triggers:
4 pull_request:
5 - target_branch: main
6 label: "ready-for-review"
Why it's good: This trigger only runs the pr-validation
workflow when a pull request is targeted at the main
branch and has the "ready-for-review" label. This allows developers to control when the validation process runs, preventing it from running on draft or work-in-progress pull requests.
2. Organize your workflows intuitively
- Platform-specific workflows: For multi-platform projects (iOS, Android, etc.), create separate workflows for each platform. Use
changed_files
to trigger the appropriate workflow based on file changes. - Modular workflows: Break down complex build processes into smaller, more manageable workflows. This improves readability and makes it easier to debug and maintain.
- Step bundles: Use Step Bundles for reusing your config blocks in a clever way. For instance, repetitive tasks like cloning your repo or installing dependencies can be wrapped into a Step Bundle and reused effectively.
- Meaningful names: Give your workflows and triggers descriptive names. This makes it easier to understand their purpose at a glance.
Example 1: Parallel iOS and Android builds on push
1workflows:
2 ios-build:
3 triggers:
4 push:
5 - branch: main # Same trigger for both workflows
6 # ... iOS build steps ...
7
8 android-build:
9 triggers:
10 push:
11 - branch: main # Same trigger for both workflows
12 # ... Android build steps ...
Why it's good: This setup uses the same push trigger (on the main
branch) for both the ios-build
and android-build
workflows. This ensures that whenever a change is pushed to main
, both builds are triggered in parallel. This keeps the iOS and Android versions in sync and streamlines the development process. It's clean, efficient, and easy to understand.
Example 2: Platform-specific feature branch builds
1workflows:
2 ios-feature:
3 triggers:
4 push:
5 - branch: 'feature/*' # Triggered by any feature branch push
6 changed_files: 'ios/**' # Only if changes in the iOS directory
7 # ... iOS feature build steps ...
8
9 android-feature:
10 triggers:
11 push:
12 - branch: 'feature/*' # Triggered by any feature branch push
13 changed_files: 'android/*' # Only if changes in the Android directory
14 # ... Android feature build steps ...
Why it's good: This example is more sophisticated. It still uses a shared trigger (feature/*
branch), but it uses changed_files
to filter which platform-specific workflow is executed. If a change is pushed to a feature branch and the changes are within the ios directory, only the ios-feature
workflow runs. The same applies to the android
directory. This keeps builds focused and efficient.
Example 3: Monorepo with multiple apps
1workflows:
2 app-a-build:
3 triggers:
4 push:
5 - branch: main
6 changed_files: 'apps/app-a/**' # Only if changes in app-a directory
7 # ... build steps for app-a ...
8
9 app-b-build:
10 triggers:
11 push:
12 - branch: main
13 changed_files: 'apps/app-b/*' # Only if changes in app-b directory
14 # ... build steps for app-b ...
Why it's good: This is a good example for monorepos. Even though the trigger is the same (push
to main
), the workflows are separated based on which application's code was changed. Only the relevant application's build is triggered. This is efficient and keeps builds focused.
3. Manage triggers effectively
enabled
property: Use theenabled: false
property to temporarily disable triggers without deleting them. This is useful for testing or when you need to pause certain builds.- Avoid overly complex logic: Keep your trigger conditions as simple and understandable as possible. Complex logic can make it difficult to troubleshoot issues.
- Test your triggers: After setting up or modifying triggers, test them thoroughly to ensure they behave as expected. Push changes to a test branch to verify the correct workflows are triggered.
4. Use webhook environment variables for deeper automation
Some juicy, new environment variables that allow using the information in the webhook are now available. They provide detailed information about build triggers, pull request comments, labels, changed files, and commit messages, enabling you to optimize automation, enhance workflow transparency, and streamline resource management.
Cool automation examples you can set up using these new variables
- Report back to the PR comment: Use the captured comment ID to post a status update directly to the original comment on the pull request. This can include details such as success, failure, logs, or any other relevant information
- Conditional test execution: Run different test suites based on files changed in a pull request. Use
BITRISE_GIT_CHANGED_FILES
to identify modified parts of the codebase. Based on these changes, trigger specific test suites, such as running frontend tests for UI changes and backend tests for API modifications. - Dynamic documentation updates: Automatically update project documentation when relevant files are changed. Monitor
BITRISE_GIT_CHANGED_FILES
for documentation-related files. If changes are detected, trigger a build step that regenerates and deploys the updated documentation. - Intelligent deployment decisions: Deploy to different environments based on commit messages or labels. Use
BITRISE_GIT_COMMIT_MESSAGES
andBITRISE_GIT_PULL_REQUEST_LABELS
to determine the deployment environment. For instance, if a commit message includes "[staging]" or a label "ready-for-staging" is present, deploy the code to the staging environment. - Slack notifications for specific events: Send customized Slack notifications when certain actions occur on a pull request. Use
BITRISE_GIT_PULL_REQUEST_COMMENT
to detect specific keywords in PR comments (e.g., "urgent" or "blocker"). Trigger a Slack notification to alert the team about these critical updates. - Security scans on code changes: Automatically run security scans when sensitive files are modified. Monitor
BITRISE_GIT_CHANGED_FILES
for changes in security-related files. Trigger a security scan to ensure no vulnerabilities are introduced with the new changes. - Automatic changelog generation: Generate a changelog entry from commit messages in a pull request. Use
BITRISE_GIT_COMMIT_MESSAGES
to extract commit messages and format them into a changelog entry. This can be automated to update the project's changelog file or release notes. - Pull request quality checks: Enforce PR quality standards by analyzing comments or commit messages. Use
BITRISE_GIT_PULL_REQUEST_COMMENT
andBITRISE_GIT_COMMIT_MESSAGES
to ensure comments and commit messages adhere to predefined quality guidelines. If not, trigger a notification or request changes before merging.
Time to get started with target-based triggers?
By now, you’re no doubt imagining a multitude of ways that you can incorporate target-based triggering into your Bitrise setup to simplify your workflows, enhance control, and increase efficiency.
Dive into Bitrise's new capabilities, and don't hesitate to share your feedback.
By following these best practices, you can ensure your CI/CD pipeline is robust and reliable, allowing you to focus more on creating great software and less on managing complex configurations. Together, let's build something amazing!