Ruairí O'Brien
Posted on July 27, 2021
I wanted a thing to only happen when a pull request is opened. I also wanted to do some cleanup when the pull request is closed. In my last place we used GitHub actions and this was super easy.
Now I am using CircleCI and this wasn't so easy.
In this post we will look at how to only run a job on a pull request in CircleCI. There is one major caveat. We also need a way to trigger the job on a pull request. We will look at how to do this with the CircleCI web api.
Conditionally run a job
There are a few options you can use to only run a job on a pull request in CircleCI. There is the option to
only ever build on a pull request but this is all or nothing
i.e. you can never run a build on a branch without opening a pull request.
Another option is, within a job, you can inspect the environment variables to see if there is a pull request number like so:
if [ "${CIRCLE_PULL_REQUEST##*/}" != "" ];then
echo "Is a pull request"
fi
This is OK but it would be nice to conditionally run a whole job instead. It is not possible to read environment variables when the pipeline is loaded. It is only possible when a job is run.
To work around this we can use the circleci/continuation orb.
If you are trying this out, make sure to update your project settings in Advanced Settings -> Enable dynamic config using setup workflows.
CircleCI expects all your configuration in one file called .circleci/config.yml
. The continuation orb takes over as the entry point giving you access to the environment variables and then runs the pipeline using whatever configuration you tell it to.
It's a little bit weird but it works.
This is an example of using the continuation orb to conditionally run a job only on a pull request.
.circleci/config.yml
setup: true
version: 2.1
orbs:
continuation: circleci/continuation@0.2.0
workflows:
setup:
jobs:
- continuation/continue:
configuration_path: ".circleci/main.yml"
parameters: /home/circleci/params.json
pre-steps:
- run:
command: |
if [ -z "${CIRCLE_PULL_REQUEST##*/}" ]
then
IS_PR=false
else
IS_PR=true
fi
echo '{ "is_pr": '$IS_PR' }' >> /home/circleci/params.json
Note, we mentioning PR here but you could do more or less anything to configure your pipeline there. /home/circleci/params.json
is written to and specified with parameters: /home/circleci/params.json
.
.circleci/main.yml
version: 2.1
parameters:
is_pr:
type: boolean
default: false
jobs:
do_something:
docker:
- image: cimg/base:2021.04
steps:
- run:
name: something
command: echo 'You get the picture'
workflows:
version: 2
whence-pr:
when: << pipeline.parameters.is_pr >>
jobs:
- do_something:
name: something
We called the file main.yml
here but it could be any file. You just need to specify it in the parameter called configuration_path
. This post also shows another way to generate the configuration on the fly.
Now we have passed the is_pr
parameter to the pipeline. We can conditionally run things using when: << pipeline.parameters.is_pr >>
.
There is one major issue with this approach. Our build may have run before a PR (pull request) was ever opened. Opening a PR will not trigger a build in CircleCI.
Triggering CircleCI pipeline when a pull request is opened
First thing you must do is grab a CircleCi API token. A personal API token will do for this example.
You can trigger a pipeline run like so:
SCM=github
ORG=your-org-here
PROJECT=your-project-here
CIRCLE_BRANCH=a-derived-branch
curl -X POST \
-H "Circle-Token: ${CIRCLE_TOKEN}" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d "{\"branch\":\"${CIRCLE_BRANCH}\"}" \
https://circleci.com/api/v2/project/${SCM}/${ORG}/${PROJECT}/pipeline
Hopefully it's clear what values you need to change there. How you will run this bit depends on what tools you have available to you. I was using GitHub and even though we use CircleCI, there are enough free GitHub Action minutes for me to setup an action like this:
.github/workflows/pr.yml
name: Trigger Build on PR
on:
pull_request:
types: [opened, reopened]
jobs:
trigger-build:
runs-on: ubuntu-latest
steps:
- name: Trigger CircleCI
env:
CIRCLE_BRANCH: ${{ github.head_ref }}
CIRCLE_TOKEN: ${{ secrets.CIRCLE_TOKEN }}
ORG: your-org-here
PROJECT: your-project-here
run: |
curl -X POST \
-H "Circle-Token: ${CIRCLE_TOKEN}" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d "{\"branch\":\"${CIRCLE_BRANCH}\"}" \
https://circleci.com/api/v2/project/github/${ORG}/${PROJECT}/pipeline
This feels like an incredible hack but it works.
A side note on doing something when a PR is merged
This has nothing to do with CircleCI but if you happen to have access to GitHub actions this might be useful.
.github/workflows/pr-closed.yml
name: On PR Closed
on:
pull_request:
types: [closed]
jobs:
on-pr-closed:
runs-on: ubuntu-latest
steps:
- name: Print PR number
env:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "${PR_NUMBER}"
Posted on July 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.