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
Edited Jan 29, 2019 by Francis Potter (personal)
Assignee Loading
Time tracking Loading