Drone

Skip directives and GitHub commit statuses

Currently, it seems that builds which are skipped as a result of a skip directive in the commit message (e.g., [ci skip], [skip ci], etc …) do not update the corresponding GitHub commit status. This can create issues for users that rely on the “Required Status Checks” of GitHub’s “Branch Protection” feature.

In the case of a Pull Request where the base branch’s protection settings are configured to require passing of at least one of drone’s status checks, when the HEAD commit contains a skip directive, that commit’s status appears to be in a “pending” state until another build is triggered which does not include a skip directive; this prevents the Pull Request from becoming mergeable. This seems to be the case for either of the following scenarios:

  • the HEAD commit is the only commit on the branch
  • the drone commit status of HEAD’s parent succeeded

The second scenario seems to be the more problematic of the two… Specifically, opening a Pull Request where the only commit on the source branch includes a skip directive seems like an uncommon use case.

On the other hand, the second scenario (commit prior to HEAD passed CI, and build of HEAD was skipped, but Pull Request’s “mergeability” is false since GitHub expects a build status for HEAD) can be relatively common.

Consider the hypothetical pipeline where users perform something like the following set of steps:

  1. npm install
  2. npm version
  3. npm publish

These versioning / publishing steps produce several changes to the source code, which will be automatically written to disk (in this case by npm, but npm can be replaced here with almost any other build / script runner), automatically committed (including a skip directive in the commit’s message), the commit is automatically tagged, then both the commit and the tag(s) are pushed back to the remote… when push event triggers are the only option for a drone user, a skip directive is often required to avoid an infinite loop of builds (otherwise, the automated push would trigger another build, which would trigger another automated push, etc …).

Assuming that there are no great alternatives here (disabling required status check is not an option for us, infinite build loops are not an option, using pull_request / tag / deployment event triggers is not an option), I’d like to propose that for builds which are skipped due to the presence of a skip directive in the commit’s message, drone change trigger.Trigger to update the corresponding commit’s status state to success with a description indicating that the build was skipped and either an empty target_url or a target_url set to some sensible default (e.g., relevant drone docs, the build history view for the given repo, etc).

The following is an example implementation to demonstrate the nature of the change I’m envisioning (example limited in scope to trigger.Trigger, but a complete implementation may also require changes to core.Status and likely others):

func (t *triggerer) Trigger(ctx context.Context, repo *core.Repository, base *core.Hook) (*core.Build, error) {
  // ...

  // source: https://github.com/drone/drone/blob/23642b92d0e5c2940887997a38f29b16ee311d76/trigger/trigger.go#L97-L100
  if skipMessage(base) {
    logger.Infoln("trigger: skipping hook. found skip directive")

    // SendEmpty updates the status of the skipped build's commit; setting the state to `success`.
    // This is a work around for users of GitHub's Branch Protection feature that have configured drone's status checks as "required checks".
    err = t.status.SendEmpty(ctx, user, &core.EmptyStatusInput{
      Repo:  repo,
    })

    if err != nil {
      logger = logger.WithError(err)
      logger.Warnln("trigger: cannot create status")
    }

    return nil, nil
  }

  // ...
}

I’d love to learn that there are alternative solutions which I haven’t yet considered, but if not, a sense for the project’s interest in accepting a change of this nature would be a good starting point for next steps.

related links (1 of 3):

skipMessageEval source

“Required Status Checks” option

related links (2 of 3):

“Branch Protection”

“mergeability”

related links (3 of 3):

relevant drone docs