Skip GitLab CI jobs in forks
In this post, you will learn one approach to disabling jobs on forked projects. This approach only works on GitLab Premium or Ultimate. If you are in a rush, check the “Final Condition” section.
Recently, I had to solve an issue at work: we have E2E tests running in our CI pipeline. These jobs need secrets. The secrets are stored in our CI/CD config as environmental variables. All merge requests from fork projects had these tests failing because the forked project was missing these secrets.
We had two options:
- Ask all forked projects to create a test account. However, the account would have to have several paid subscriptions for our tests to pass
- Not run E2E tests on forks and only run them once when a maintainer tries to merge the MR
The E2E tests are very basic, and there is a good chance that some unit test will catch the failure first, so we opted for the second option, only running E2E tests on merge.
CI settings explained
We use merge trains and merge result pipelines. Because this approach relies on merge trains, it won’t work on Free GitLab
Naive attempt
I thought that I could use existing environment variables to figure out where the pipeline runs. My first attempt was the condition:
rules:
- if: $CI_PROJECT_ID == "46519181" || $GITLAB_TEST_TOKEN != ""
The condition says to run the tests if the project is the main (forked) project or if the fork added the token to the CI config
As the heading suggests, this didn’t work because since the MR is targeting the main project, the $CI_PROJECT_ID
environmental variable is always set to the main project ID
Investigation
I created a test project in my viktomas
namespace and forked it. I set CI to use merge trains and run a simple job
test:
script:
- printenv
Then, I run two pipelines
- one when the MR was created
- one when the MR was merged
I noticed that the only useful difference between the two printouts was the $CI_MERGE_REQUEST_EVENT_TYPE
variable
- MR created - the variable had
detached
value - MR merged - the variable had
merge_train
value
Extra confusion
I didn’t realise that even if I create the MR from fork, if I have developer access to the main project, the CI variables from the main project are accessible to the pipeline. That complicated my investigation because I had to use a separate user with lower privileges to create the MR from fork.
Final condition
rules:
- if: $GITLAB_TEST_TOKEN || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train"
This condition says: run the pipeline if the token is present, or if the job runs in a pipeline that tries to merge the MR.
This condition takes advantage of two things:
- When you run merge train (i.e. a maintainer of the project started merging the MR), the
$CI_MERGE_REQUEST_EVENT_TYPE
is se tomerge_train
. So there is no way for the MR to get merged without at least one successful pipeline that has$CI_MERGE_REQUEST_EVENT_TYPE == "merge_train"
- When a user with developer or higher privileges in the main project runs the pipeline, it will have access to the main project’s CI/CD variables (here
$GITLAB_TEST_TOKEN
)
These two facts combined mean that the condition achieves exactly what we set out to do at the beginning. Community contributors with forks don’t have to set up their CI with extra variables and their pipeline can still pass. However, once a maintainer in the main project runs merge train to merge the MR, the E2E tests will run with the needed variables, ensuring the MR didn’t break E2E tests.