Comingling of "simplified" `only` options causes unexpected behavior
The documentation defines some special keywords such "branches" and "tags" which can be included alongside refs (either as strings or regexps) in the array version of only:
entries.
The Problem
The problem is that the "special keywords" are also treated as strings and used to match refs, which results in some unexpected behavior.
For example, if a job says:
only: ['tags']
... the developer might expect that the job will only run when the push is a tag. But in fact it will also run if the push is a branch with the name "tags".
In fact, because the refs themselves are an overloaded group (by referring to either a branch or a tag), that means that, for example, if your job is set to only: ['web']
it will actually run under 3 conditions:
- When run using the "Run pipeline" button in the GitLab UI (as described in the documentation)
- When run on a branch called 'web'
- When run on a tag called 'web'
This is demonstrated in a sample project which includes the following job:
only-web:
only: ['web']
script: 'echo "Done"'
The job is run when pushing a tag named "web", pushing a branch named "web", and using the "Run Pipeline" button. It is not run when pushing to a tag or branch with another name such as "not-web" or "master".
Two notes about the example project referenced above:
- I made the example project private; not sure if it should be public; seeking advice from other GitLabbers
- I had to delete the "web" tag in order to add the "web" branch in the example.
The behavior is particularly alarming when combined with protected tags and branches. For example, I had a project where I protected all tags ("*") then used only: ['tags']
on a job, thinking that would mean that it gave me control over which pushes would trigger that job. But a savvy hacker could trigger that job using a carefully crafted branch push.
Workaround
There is a workaround, which I think we should recommend as a best practice, and that is to use the "complex" form with the variables:
key for all tag and branch limitations. For example, to restrict the job to a specific tag, one could use:
only:
variables:
- $CI_COMMIT_TAG =~ /^my\-tag\-pattern$/
Or to restict a job to run only on tags, this seems to work:
only:
variables:
- $CI_COMMIT_TAG
Restricting by branch is harder, until there is a $CI_COMMIT_BRANCH
variable, but in the meantime this will probably work (I didn't test):
only:
variables:
- $CI_COMMIT_REF_NAME == "master"
except:
variables:
- $CI_COMMIT_TAG
This is obviously verbose but does actually work AFAIK.
Proposal
In my opinion, this behavior is weird enough that the entire comingling of pipeline triggers (like 'web' and 'branches') with refs (like 'master') should be deprecated entirely and replaced with more specific ways of identifying the conditions under which the job should run.
But, adhering to the value of MVC, and keeping in mind that the "simplified" only/except handling is GA, I propose simply that the "special keywords" in only:
and except:
lists be treated only as special keywords, and not be treated as branch or tag names. Those words are:
branches
tags
api
external
pipelines
pushes
schedules
triggers
web
merge_requests
Note I'm recommending a different approach for the refs:
block in the "complex" form (#57076 (moved))