Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expression-based job filters #9046

Merged
merged 10 commits into from
Nov 13, 2024
138 changes: 126 additions & 12 deletions jekyll/_cci2/configuration-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1761,7 +1761,7 @@ While this improves performance in most cases, if a downstream step requires tho
----
====

A special step used to check out source code to the configured `path` (defaults to the `working_directory`). The reason this is a special step is because it is more of a helper function designed to make checking out code easy for you. If you require doing git over HTTPS you should not use this step as it configures git to checkout over SSH.
A special step used to check out source code to the configured `path` (defaults to the `working_directory`). The reason this is a special step is because it is more of a helper function designed to simplify the process of checking out code. If you require doing git over HTTPS you should not use this step as it configures git to checkout over SSH.

[.table.table-striped]
[cols=4*, options="header", stripes=even]
Expand Down Expand Up @@ -2700,6 +2700,11 @@ An approval job can have any name. In the example above the approval job is name

[#jobfilters]
====== *`filters`*
Filter job execution within a workflow based on the following:

* Branch
* Tag
* Expression-based condition

Job filters can have the keys `branches` or `tags`.

Expand All @@ -2713,10 +2718,10 @@ NOTE: Workflows will ignore job-level branching. If you use job-level branching
| `filters`
| N
| Map
| A map defining rules for execution on specific branches
| A map or string to define rules for job execution. Branch and tag filters require a map. Expression-based filters require a string.
|===

The following is an example of how the CircleCI documentation uses a regex to filter running a workflow for building PDF documentation:
The following is an example of how the CircleCI documentation project uses a regular expression to filter running a job in a workflow only on a specific branch:

[,yaml]
----
Expand All @@ -2728,19 +2733,128 @@ workflows:
- build_server_pdfs: # << the job to conditionally run based on the filter-by-branch-name.
filters:
branches:
only: /server\/.*/
only: /server\/.*/ # the job build_server_pdfs will only run when the branch being built starts with server/
----

You can read more about using regular expressions in your config in the xref:workflows#using-regular-expressions-to-filter-tags-and-branches[Using workflows to schedule jobs] page.

'''

====== Expression-based job filters
Expression-based job filters allow you to conditionally run jobs based on the following:

* xref:variables#pipeline-values[Pipeline values]
* xref:pipeline-variables#pipeline-parameters-in-configuration[Pipeline parameters]

An expression-based job filter is a rule that is evaluated against pipeline values and parameters to decide whether a job should run.

Using expression-based job filters is one way to optimize your pipelines to lower costs, decrease time to feedback, or run specific jobs based on the context of the source of change.

[,yml]
----
workflows:
deploy:
jobs:
- init-service
- build-service-image:
requires:
- init-service
- dry-run-service:
requires:
- init-service
filters: pipeline.git.branch != "main" and pipeline.git.branch != "canary"
- publish-service:
requires:
- build-service-image
- test-service
filters: pipeline.git.branch == "main" or pipeline.git.tag starts-with "release"
- deploy-service:
context:
- org-global
requires:
- publish-service
filters: pipeline.git.branch == "main" and pipeline.git.commit.subject starts-with "DEPLOY:"
----

'''

**Examples**

Only run the job on the project's `main` branch:

[source,yml]
----
filters: pipeline.git.branch == "main"
----

Only run the job on the project's `main` branch, or branches starting with `integration-test`:

[source,yml]
----
filters: pipeline.git.branch == "main" or pipeline.git.branch starts-with "integration-test"
----

The above snippet causes the job `build_server_pdfs` to only be run when the branch being built starts with "server/".
Only run the job on the `main` branch, and disallow use with pipelines xref:vs-code-extension-overview#test-run-your-config-from-vs-code[triggered with unversioned configuration]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about

Only run the job on the main branch, but not if the pipeline was triggered with unversioned configuration

?


You can read more about using regex in your config in the xref:workflows#using-regular-expressions-to-filter-tags-and-branches[Using workflows to schedule jobs] page.
[source,yml]
----
filters: pipeline.git.branch == "main" and not (pipeline.config_source starts-with "api")
----

Use pipeline parameters and the pipeline value `pipeline.git.branch` to run a job only on specific branches **or** when triggered via the API with a pipeline parameter set to true:

[source,yml]
----
version: 2.1

parameters:
run-storybook-tests:
type: boolean
default: false

...
# jobs configuration ommitted for brevity

workflows:
build:
jobs:
- setup
- storybook-tests:
requires:
- setup
filters: |
pipeline.parameters.run-storybook-tests
or pipeline.git.branch == "dry-run-deploy"
or pipeline.git.branch starts-with "deploy"
----

You can use the API to trigger a pipeline with a pipeline parameter set to true:

[source,yml]
----
curl -X POST https://circleci.com/api/v2/project/circleci/<org-id>/<project-id>/pipeline/run \
--header "Circle-Token: $CIRCLE_TOKEN" \
--header "content-type: application/json" \
--data {
"definition_id": "<pipeline-definition-id>",
"config": {"branch": "<your-branch-name>"},
"checkout": {"branch": "<your-branch-name>"},
"parameters": {"run-storybook-tests": "true"}
}
----

**Operators**

The operators you can use for expression-based job filters are described in the following table. You can also group sub-expressions with parentheses `(`, `)`. as in the examples above.

include::../_includes/partials/using-expressions/operators.adoc[]

'''

[#branches]
====== *`branches`*

Branches can have the keys `only` and `ignore`, which either map to a single string naming a branch. You may also use regular expressions to match against branches by enclosing them with slashes, or map to a list of such strings. Regular expressions must match the *entire* string.
The branches filter can have the keys `only` and `ignore`, which map to a single string naming a branch. You may also use regular expressions to match against branches by enclosing them with slashes, or map to a list of such strings. Regular expressions must match the *entire* string.

* Any branches that match `only` will run the job.
* Any branches that match `ignore` will not run the job.
Expand All @@ -2755,17 +2869,17 @@ Branches can have the keys `only` and `ignore`, which either map to a single str
| `branches`
| N
| Map
| A map defining rules for execution on specific branches
| A map defining rules for execution on specific branches.

| `only`
| N
| String, or List of Strings
| Either a single branch specifier, or a list of branch specifiers
| String, or list of strings
| Either a single branch specifier, or a list of branch specifiers.

| `ignore`
| N
| String, or List of Strings
| Either a single branch specifier, or a list of branch specifiers
| String, or list of strings
| Either a single branch specifier, or a list of branch specifiers.
|===

[source,yaml]
Expand Down
31 changes: 1 addition & 30 deletions jekyll/_cci2/contexts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -362,36 +362,7 @@ Any errors evaluating an expression will _fail closed_ and prevent use of the co

The operators you can use are described in the following table. You can also group sub-expressions with parentheses `(`, `)`. as in the example above.

[.table.table-striped]
[cols=3*, options="header", stripes=even]
|===
| Operator type | Operators | Description

| Logical
|`and`, `or`
| These are short-circuiting boolean operators.

| Equality
| `==`, `!=`
| String, numeric, and boolean equality. If the operands are of different types then `==` will evaluate `false`, and `!=` will evaluate `true`.

| Equality
| `starts-with`
| String prefix equality, `"hello world" starts-with "hello"` evaluates as `true`. It is an error to use a non-string type as an operand.

| Numeric comparison
| `>=`, `>`, `<=`, `<`
| Numeric comparisons. It is an error to use a non-numeric type as an operand.

| Negation
| `not`
a| Boolean negation.

Note that `not` has very high precedence and so binds very tightly. Use sub-expressions to apply `not` to more complex expressions. For example, with `foo` being `true` and `bar` being `false`:

* `not foo and bar` evaluates to `false`
* `not (foo and bar)` evaluates to `true`
|===
include::../_includes/partials/using-expressions/operators.adoc[]

=== Precedence
The following table shows operator precedence table, from weakest to strongest binding.
Expand Down
30 changes: 30 additions & 0 deletions jekyll/_includes/partials/using-expressions/operators.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[.table.table-striped]
[cols=3*, options="header", stripes=even]
|===
| Operator type | Operators | Description

| Logical
|`and`, `or`
| These are short-circuiting boolean operators.

| Equality
| `==`, `!=`
| String, numeric, and boolean equality. If the operands are of different types then `==` will evaluate `false`, and `!=` will evaluate `true`.

| Equality
| `starts-with`
| String prefix equality, `"hello world" starts-with "hello"` evaluates as `true`. It is an error to use a non-string type as an operand.

| Numeric comparison
| `>=`, `>`, `<=`, `<`
| Numeric comparisons. It is an error to use a non-numeric type as an operand.

| Negation
| `not`
a| Boolean negation.

Note that `not` has very high precedence and so binds very tightly. Use sub-expressions to apply `not` to more complex expressions. For example, with `foo` being `true` and `bar` being `false`:

* `not foo and bar` evaluates to `false`
* `not (foo and bar)` evaluates to `true`
|===