palantir / policy-bot

A GitHub App that enforces approval policies on pull requests
Apache License 2.0
727 stars 104 forks source link

Add support for allowing skipped checks in `has_successful_status` #789

Open iainlane opened 5 days ago

iainlane commented 5 days ago

The has_successful_status predicate currently requires predicates to be present and successful in order to pass. But workflows can be run conditionally - for example only if certain paths change - and it is currently not very convenient to write a policy which considers such skipped workflows as passing. The only feasible workaround is to duplicate the path filters in the policy, and this quickly gets unwieldy and prone to getting out-of-sync in large repositories.

Here we add direct support for specifying such rules. This is done by introducing a new alernative form that has_successful_status predicates can take:

has_successful_status:
  options:
    skipped_is_success: true
  statuses:
    - "status 1"
    - "status 2"

In this mode, we will consider the skipped result as acceptable. The current form:

has_successful_status:
  - "status 1"
  - "status 2"

remains supported. We have done this by implementing a custom unmarshaling function to be able to handle both forms.

Closes: #760

palantirtech commented 5 days ago

Thanks for your interest in palantir/policy-bot, @iainlane! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request.

iainlane commented 5 days ago

I'll square off the CLA question internally as soon as I can, but in the meantime hopefully we can proceed with reviews 😁

bluekeyes commented 5 days ago

Thanks for this proposal! Looking at an actual implementation, I'm now thinking this could work better as a new predicate that deprecates has_successful_status. This new has_status predicate would let you specify both the contexts and the allowed conclusions:

# Option 1: same conclusions for all contexts
has_status:
  conclusions: ["success", "skipped"]
  contexts:
    - "status 1"
    - "status 2"

# Option 2: conclusions per context
has_status:
  "status 1": ["success"]
  "status 2": ["success", "skipped"]

Internally, we can convert the has_successful_status predicate in to this new one to avoid duplicating logic. The new predicate also avoids having two forms of a single predicate and allows policies that can work with failure, neutral, and other conclusions. What do you think?

iainlane commented 2 days ago

Cheers for the review 👍

Thanks for this proposal! Looking at an actual implementation, I'm now thinking this could work better as a new predicate that deprecates has_successful_status.

Sure, that makes sense. It was a bit awkward that we had two forms in one predicate.

Take a look at what I've got now? It's fundamentally the same as what I had before, except now:-

  1. has_successful_status is kept separate
  2. Any conclusion can be given, instead of only allowing skipped as we did before

We've still got a custom unmarshal function here, which handles unpacking either form into the same struct so they can share the implementation.

A bit more stuff has appeared to handle the list of statuses but it's not too bad.

# Option 1: same conclusions for all contexts
has_status:
  conclusions: ["success", "skipped"]
  contexts:
    - "status 1"
    - "status 2"

# Option 2: conclusions per context
has_status:
  "status 1": ["success"]
  "status 2": ["success", "skipped"]

I went for the first one. You can express the second in terms of it as far as I can see, and it feels to me like it'll be more common to want to apply the same conclusions to a whole predicate than select them individually. But I don't feel that strongly about this - it can be changed to the other form easily enough.

I've used statuses rather than contexts. That's only because I've not used that word in the context of GitHub CI and I found it bounced off me a bit. But it also doesn't matter too much.