Skip to content

tide: add configurable GitHub merge blocks enforcement#579

Open
Prucek wants to merge 1 commit intokubernetes-sigs:mainfrom
Prucek:review-blocks-fix
Open

tide: add configurable GitHub merge blocks enforcement#579
Prucek wants to merge 1 commit intokubernetes-sigs:mainfrom
Prucek:review-blocks-fix

Conversation

@Prucek
Copy link
Copy Markdown
Member

@Prucek Prucek commented Dec 19, 2025

Adds a new github_merge_blocks_policy configuration option that allows
Tide to check GitHub's mergeStateStatus to prevent merging PRs that are
blocked by GitHub due to branch protection rules, rulesets, required
reviews, etc.

The configuration can be set globally with '*' or per-org/repo level.
Defaults to "permit".
Valid values:

  • "ignore": Ignore BLOCKED status entirely, attempt to merge anyway (risky)
  • "permit": Allow merging but log warnings and show "In merge pool (despite BLOCKED)"
    status for monitoring. Use this to identify repos needing BP/ruleset fixes.
  • "block": Respect BLOCKED status and prevent Tide from merging (safest)

Fixes #575, #269
Previous attempt: #537

🤖 Assisted by Claude.

@netlify
Copy link
Copy Markdown

netlify Bot commented Dec 19, 2025

Deploy Preview for k8s-prow ready!

Name Link
🔨 Latest commit 2c14b70
🔍 Latest deploy log https://app.netlify.com/projects/k8s-prow/deploys/69f32174524c19000817c3cf
😎 Deploy Preview https://deploy-preview-579--k8s-prow.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@k8s-ci-robot k8s-ci-robot added the area/tide Issues or PRs related to prow's tide component label Dec 19, 2025
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Prucek
Once this PR has been reviewed and has the lgtm label, please assign petr-muller for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Dec 19, 2025
@petr-muller
Copy link
Copy Markdown
Contributor

Thanks for following up on this! Opt-in config seems like a reasonable short-term way forward. Some thoughts (have not yet looked at the code):

I'm not sure I feel comfortable leaving default-configured (and even explicitly configured) instances vulnerable to the class of #269 -like bugs. Some repos may have policies that help them not getting affected by some instances of this problem but not necessarily to all, and the "tide silently fails to merge and never surfaces the problem" error is really annoying to leave some instances open to it. So to me it seems we'd still need to invent some way to propagate the actual merge failure, post-fact, to PR author somehow, I don't think we could consider #269 -like issues fully fixed 🤔

Configuration booleans are always a bit of a smell, for similar reasons like API Conventions states:

Think twice about bool fields. Many ideas start as boolean but eventually trend towards a small set of mutually exclusive options. Plan for future expansions by describing the policy options explicitly as a string type alias (e.g. TerminationMessagePolicy).

For this specific case, I think we may prefer treating this as a three-value for starters:

  • ignore: ignore the BLOCKED entirely in decisions
  • permit: merge BLOCKED PRs but make some noise about it, possibly in the log, possibly through some PR-exposed message like "In Merge Pool (despite BLOCKED)". This would be a good default to set us up for eventual flip the default to enforcing, because we'd help Prow and/or repo admins to monitor repos that either need BP/Ruleset fixes or explicit ignore/permit config
  • block: respect BLOCKED

We'd start with permit as a default and possibly eventually flip the default to block?

@Prucek Prucek force-pushed the review-blocks-fix branch from 2a09353 to 8560319 Compare January 5, 2026 12:49
@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jan 5, 2026
@Prucek
Copy link
Copy Markdown
Member Author

Prucek commented Jan 5, 2026

Thanks @petr-muller for the guidance! That is definitely a good point. I updated the PR based on your comments.

@Prucek Prucek force-pushed the review-blocks-fix branch from 8560319 to 7f08e03 Compare January 5, 2026 12:59
@k8s-triage-robot
Copy link
Copy Markdown

The Kubernetes project currently lacks enough contributors to adequately respond to all PRs.

This bot triages PRs according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the PR is closed

You can:

  • Mark this PR as fresh with /remove-lifecycle stale
  • Close this PR with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Apr 5, 2026
@Prucek
Copy link
Copy Markdown
Member Author

Prucek commented Apr 14, 2026

/remove-lifecycle stale

@k8s-ci-robot k8s-ci-robot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Apr 14, 2026
Comment thread pkg/config/prow-config-documented.yaml Outdated
# required reviews, etc.).
# Use '*' as key to set this globally. Defaults to "permit" which allows merging but logs warnings.
# Valid values: "ignore", "permit", "block"
# Note: "ignore" may cause issues with repos using GitHub Rulesets that restrict updates
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of issues may ignore cause? "Restrict updates" in what sense?

Comment thread pkg/config/tide.go
// Valid values: "ignore", "permit", "block"
// Note: "ignore" may cause issues with repos using GitHub Rulesets that restrict updates
// but have Tide on the bypass list.
GitHubMergeBlocksPolicyMap map[string]GitHubMergeBlocksPolicy `json:"github_merge_blocks_policy,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think config load and checkconfig should validate the value is one of the three

Comment thread pkg/tide/github.go
Comment on lines +645 to +647
case config.GitHubMergeBlocksIgnore:
// Ignore BLOCKED status entirely
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do something loud in default:, meaning an unexpected value somehow slipped through (programmer error or botched validation)

Comment thread pkg/tide/status.go Outdated
policy := tide.GitHubMergeBlocksPolicy(orgRepo)
switch policy {
case config.GitHubMergeBlocksBlock:
// Block merge by adding to diff
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is misleading, this is status controller - does not actually block merges

Comment thread pkg/tide/status.go Outdated
// Block merge by adding to diff
diff += 100
if desc == "" {
desc = " Blocked by GitHub (branch rulesets or protection)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not something that should be addressed right away in this PR, but we'll want to investigate if GH gives the actual reason somewhere in the API. It gives Tide a fairly informative message on an actual denied merge attempt, like

At least 2 approving reviews are required by reviewers with write access.

It would be great if Tide showed that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no API to get it before the merge. We can maybe add the status after it tries to merge, from the mergeError.

Comment thread pkg/tide/status.go Outdated
Comment on lines +267 to +271
orgRepo := config.OrgRepo{
Org: string(pr.Repository.Owner.Login),
Repo: string(pr.Repository.Name),
}
policy := tide.GitHubMergeBlocksPolicy(orgRepo)
Copy link
Copy Markdown
Contributor

@petr-muller petr-muller Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requirementDiff only needs the policy, but we give it the full tide config and we even need to reconstruct an OrgRepo to call the method to get the policy. The requirementDiff can receive just the policy, and the callsite (expectedStatus on L340) even has the necessary OrgRepo struct so ti can easily do

mergeBlocksPolicy := sc.config().Tide.GitHubMergeBlocksPolicy(orgRepo)
for _, q := range queryMap.ForRepo(repo) {
  diff, diffCount := requirementDiff(pr, &q, cc, mergeBlocksPolicy)

Comment thread pkg/tide/status.go Outdated
// poolStatus returns the appropriate status message for a PR that is in the merge pool.
// If the PR has BLOCKED merge state and the policy is "permit", it returns a warning message.
// This also logs a warning for monitoring purposes.
func poolStatus(pr *PullRequest, tide *config.Tide, log *logrus.Entry) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also only needs the policy, not the full tide config, and both callsites have access to the policy computed to call requirementDiff (we'd just need to compute the policy earlier, before the conditional on L316

Comment thread pkg/tide/github.go Outdated
// Check GitHub's mergeStateStatus which reflects all GitHub-side merge blocking conditions
// including branch protection rules, rulesets, required reviews, status checks, etc.
// This is controlled by the github_merge_blocks_policy configuration.
if pr.MergeStateStatus == "BLOCKED" {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"BLOCKED" should be a constant somewhere, maybe close to PullRequest type

@Prucek Prucek force-pushed the review-blocks-fix branch from 7f08e03 to 2c14b70 Compare April 30, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/tide Issues or PRs related to prow's tide component cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR #537 prevents tide from merging PRs in repos which Restrict updates via Github Rulesets

4 participants