Skip to content

workflows/labels: manage approval labels#415259

Merged
wolfgangwalther merged 3 commits intoNixOS:masterfrom
wolfgangwalther:ci-approvals
Jun 13, 2025
Merged

workflows/labels: manage approval labels#415259
wolfgangwalther merged 3 commits intoNixOS:masterfrom
wolfgangwalther:ci-approvals

Conversation

@wolfgangwalther
Copy link
Contributor

This enables the labels workflow to manage the 12.approvals: ? and 12.approved-by: package-maintainer labels. Two challenges had to be solved for that:

The missing permissions are solved by running the labels workflow on workflow_run - which triggers on completion of a no-op pull_request_review workflow. This also allows us to bring back #413256, so did that in the first commit, verifying the approach.

To get the eval results, the label workflow is now run after Eval as a reusable workflow. This means labeling will happen with a delay, compared to the status-quo, but I don't see this as a problem. The labels workflow is triggered after Eval and after any approval or dismissal of reviews.

Currently, the approach to count approvals is based on pure number of approved reviews, without taking "push after review" as invalidation into account. I think the status-quo, automated (?) by @wegank, is different though. I assume we'll want that behavior and we should implement it as a branch protection rule, which reads:

Dismiss stale pull request approvals when new commits are pushed
New, reviewable commits pushed will dismiss previous pull request review approvals.

This will also give us better feedback in GitHub's UI than right now, where we remove the approvals label, but still see the approvals in the reviewers list. The branch protection rule should dismiss them entirely.

Also to consider: How is 12.approved-by: package-maintainer to be defined? We defined 11.by: package-maintainer as "the PR author must be maintainer of all touched packages". I don't think this definition makes sense for the approval label (and doesn't seem to be used like this in practice right now). So I went with "at least one reviewer needs to maintain at least one of the touched packages" to add this label.

Resolves #307917.

Things done

It's hard to test it in my own fork, because I can't approve my own PRs. I did some basic testing with approvals created by CI, though - this should work. I did not test the different scenarios of "approving while eval is still running", "approval after eval ran", "dismissal" etc. - but they all just trigger the same workflow without any special conditions. So I'm fairly confident it should work.


Add a 👍 reaction to pull requests you find important.

@github-actions github-actions bot added 6.topic: continuous integration Affects continuous integration (CI) in Nixpkgs, including Ofborg and GitHub Actions 6.topic: policy discussion Discuss policies to work in and around Nixpkgs backport release-24.11 labels Jun 9, 2025
@github-actions github-actions bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux. labels Jun 9, 2025
Copy link
Contributor

@MattSturgeon MattSturgeon left a comment

Choose a reason for hiding this comment

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

I think the status-quo, automated (?) by @wegank, is different though.

I believe the current automation has been inconsistent with whether or not it counts stale reviews.

I don't think we need to get too hung up on maintaining the exact status quo of an unofficial labeling bot 🙂

I assume we'll want that behavior and we should implement it as a branch protection rule

Note there are actually two very similar protection/ruleset rules:

  • Dismiss stale pull request approvals when new commits are pushed
    New, reviewable commits pushed will dismiss previous pull request review approvals.

  • Require approval of the most recent reviewable push
    Whether the most recent reviewable push must be approved by someone other than the person who pushed it.

These are related but subtly different; one relates to whether "old" approvals still count, the other relates to "who" can sign off the latest code.


Personally, I find the label more useful when it includes all approvals, even if they didn't approve the very latest code.

I'd be in favour of this PR counting all approvals; outdated or not, and by committers or not. IIUC that is how the PR behaves currently?

I think follow-up PRs/issues could discuss whether we want to add another label for "up-to-date approvals" and/or formalise what exactly counts as an "approval" for the existing label. Whether and what branch protection/rulesets we want should also be a separate discussion.

How is 12.approved-by: package-maintainer to be defined? [...] I went with "at least one reviewer needs to maintain at least one of the touched packages" to add this label.

SGTM

@wolfgangwalther
Copy link
Contributor Author

I don't think we need to get too hung up on maintaining the exact status quo of an unofficial labeling bot 🙂

Not necessarily, no - but it was the only data point I found, so far, about which behavior to prefer.

These are related but subtly different; one relates to whether "old" approvals still count, the other relates to "who" can sign off the latest code.

Yeah, I'm not suggesting to require approvals before merge, so the second option doesn't apply. This would be another different discussion.

I'd be in favour of this PR counting all approvals; outdated or not, and by committers or not. IIUC that is how the PR behaves currently?

Correct.

I think follow-up PRs/issues could discuss whether we want to add another label for "up-to-date approvals" and/or formalise what exactly counts as an "approval" for the existing label. Whether and what branch protection/rulesets we want should also be a separate discussion.

Fine by me, we'd just make sure that this automation doesn't conflict with other automation approaches, flipping labels back and forth. So those would need to be disabled.

@philiptaron
Copy link
Contributor

It's hard to test it in my own fork, because I can't approve my own PRs.

You mean that https://github.com/wolfgangbot/ isn't yet under your control?! 🫢

Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

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

A few curious questions.

This brings back the "minimize CI reviews after dismissal" job that was
previously removed. The first time around, we had a single job triggered
by the `pull_request_review` event. This lacks permission to do
meaningful stuff, though.

This time, we trigger an empty no-op job on `pull_request_review` and
then run a second workflow on `workflow_run`. This can run with the
proper permissions.
This moves the actual labeling from the eval workflow to the labels
workflow. At this stage, this only has a disadvantage: Adding the
topic-labels to the pull request will now only happen after eval has
finished, instead of instantly.

We will only benefit from this later, when we manage approval related
events. With this change, we will have the comparison results and thus
the package maintainer info available.
The category 12 labels for number of approvals and approved by package
maintainer are now automatically managed by the labels workflow. The
logic is slightly different from the "by: package-maintainer" label. For
approval, it's enough if *any one* maintainer approves the PR to have
the label added, even if the PR touches multiple packages.

The workflow only counts approved reviews, no matter whether there had
been a push in the meantime or not. To achieve the currently used logic
of "expiring approvals after push", we will have to set up a branch
protection rule, which actually dismissed those reviews automatically.
@wolfgangwalther
Copy link
Contributor Author

Also added the default shell bash introduced in #415680 to the new workflows.

@wegank wegank added 12.approved-by: package-maintainer This PR was reviewed and approved by a maintainer listed in any of the changed packages. 12.approvals: 1 This PR was reviewed and approved by one person. labels Jun 11, 2025
@wolfgangwalther
Copy link
Contributor Author

2025-06-11T12:32:02,340207135+02:00

Makes me smile on this PR :)

But a question related to that, @wegank: Was this automated and if yes, what's the rule you're looking at? This would not be labeled approved-by: package-maintainer with this PR's automation, because there are no package rebuilds.

Thus - are you using codeowners as a reference, too?

@wegank
Copy link
Member

wegank commented Jun 11, 2025

Thus - are you using codeowners as a reference, too?

Yeah, actually I was abusing the label, as it was added when someone pinged by nix-owners gave their approval...

@wolfgangwalther
Copy link
Contributor Author

Ah, interesting logic.

I see multiple options here:

  • Ignore codeowners for approval labels
  • Do the same as previously, and add the by-package-maintainer approval label when any codeowner approves, too
  • Add a new 12.approved-by: codeowner label

Any preference, anyone?

@MattSturgeon
Copy link
Contributor

  • Add a new 12.approved-by: codeowner label

This is my preference, but I'm happy for it do be done in another PR if that unblocks this one and/or makes it easier to review

@wolfgangwalther
Copy link
Contributor Author

Yeah, to be able to handle codeowners, the codeowner workflow would first have to be integrated with eval/reviewers/labelling. I have some preliminary work in #411409, but that has stalled a bit with no clear path forward.

@wolfgangwalther
Copy link
Contributor Author

I think this is as good as it gets for now - we will get the best feedback by just running it. Will merge later today, when I have the time to monitor this in action.

@wolfgangwalther wolfgangwalther merged commit 67adad2 into NixOS:master Jun 13, 2025
26 of 28 checks passed
@wolfgangwalther wolfgangwalther deleted the ci-approvals branch June 13, 2025 10:14
@nixpkgs-ci
Copy link
Contributor

nixpkgs-ci bot commented Jun 13, 2025

Successfully created backport PR for release-24.11:

@nixpkgs-ci
Copy link
Contributor

nixpkgs-ci bot commented Jun 13, 2025

Successfully created backport PR for release-25.05:

@github-actions github-actions bot added the 8.has: port to stable This PR already has a backport to the stable release. label Jun 13, 2025
@wolfgangwalther
Copy link
Contributor Author

So one thing that happens: External or first-time contributors (only saw it for the latter, not sure, yet) can't run those pull_request_review workflows unless someone approves the workflow to run. Example: https://github.com/NixOS/nixpkgs/actions/runs/15632302375.

This only affects the labeling, and only after a review submission. On creation of the PR and force push, the labels (including approvals) will still be added, but not for reviews - until those workflows are approved. This affects all contributors, even committers, posting a review on those PRs - whether this needs approval or not is about the PR creator.

I'm not sure, yet, whether a one-time approval is enough or whether that needs to be repeated after a force push.

Whose PRs are affected depends on this setting in Actions / General:

2025-06-13T13:00:35,506782913+02:00

@infinisil could you clarify what this is set to for nixpkgs?

@wolfgangwalther
Copy link
Contributor Author

I'm not sure, yet, whether a one-time approval is enough or whether that needs to be repeated after a force push.

This needs to be repeated after each force-push, just tested.

This is annoying, because it means that approved PRs of first-time contributors will regularly not have approval labels set :/

@MattSturgeon
Copy link
Contributor

So one thing that happens: External or first-time contributors (only saw it for the latter, not sure, yet) can't run those pull_request_review workflows unless someone approves the workflow to run. Example: NixOS/nixpkgs/actions/runs/15632302375.

This is annoying, I expect "require approval" is enabled because some workflow events are insecure when run from untrusted forks; the same reason pull_request_target is preferred over pull_request.

I assume the scenario GitHub is protecting us from is a user modifying a pull_request_review workflow on their fork, so that when run it does something unexpected that our non-fork workflow wouldn't?

I believe users could also add entirely new workflows to their fork and run them on PRs to the main repo, if not for these security settings?

Although from a security perspective there's little difference between a fork having additional workflows and a fork having modified workflows.

The details are documented under pull_request_review. They actually explicitly mention:

When a first-time contributor submits a pull request to a public repository, a maintainer with write access may need to approve running workflows on the pull request. For more information, see Approving workflow runs from public forks.

Based on that wording, it sounds like pull_request_target would also not trigger on events in forks, but IIUC that's not correct because the workflow is read from the non-fork repo for _target events?

@wolfgangwalther
Copy link
Contributor Author

wolfgangwalther commented Jun 13, 2025

I assume the scenario GitHub is protecting us from is a user modifying a pull_request_review workflow on their fork, so that when run it does something unexpected that our non-fork workflow wouldn't?

I believe users could also add entirely new workflows to their fork and run them on PRs to the main repo, if not for these security settings?

Both correct.

Although from a security perspective there's little difference between a fork having additional workflows and a fork having modified workflows.

I think the difference is in terms of whose resources are used. Anybody could create pull_request workflows in a PR and do bad things like mining crypto or whatever.

Based on that wording, it sounds like pull_request_target would also not trigger on events in forks, but IIUC that's not correct because the workflow is read from the non-fork repo for _target events?

Correct. pull_request is the same as pull_request_review - it would require approval. pull_request_target does not, because it runs "in the context of the base branch".

So yeah, the very same reason why we need to make this whole dance with workflow_run to get a workflow with secrets/permissions to run causes this "require approval" thing.

I see two ways to deal with that here:

  • We can either say we're confident with the first option in the screenshot "first-time contributor and new on GitHub" - and then for those that still need their workflows approved manually, we live with that.
  • Or we can switch to a periodic workflow approach, where we don't trigger on the review event, but just scan those PRs every now and then. We probably don't need to scan too many PRs every time, because we can sort them by updated, and then paginate through the result until we get to PRs that were updated before we last ran the action.

The latter would also be required if we want to manage the "merge conflict" label via actions as well.

Edit: Of course, a hybrid approach would also work. Still keep the review event, so we get quick labeling for known contributors - but then come back periodically and update PRs for first-time contributors as well. Not sure whether we could get rid of the "Some workflows need approval" button in the PRs, though.

@MattSturgeon
Copy link
Contributor

  • Or we can switch to a periodic workflow approach, where we don't trigger on the review event, but just scan those PRs every now and then. We probably don't need to scan too many PRs every time, because we can sort them by updated, and then paginate through the result until we get to PRs that were updated before we last ran the action.

The latter would also be required if we want to manage the "merge conflict" label via actions as well.

This sounds reasonable. It's frustrating that we can't use events to trigger adding labels without running into permission/security issues, but using on: schedule would certainly simplify the implementation. In practice, we could limit the result to all PRs updated since the last scheduled run and maybe do a pass each hour?

Edit: Of course, a hybrid approach would also work. Still keep the review event, so we get quick labeling for known contributors - but then come back periodically and update PRs for first-time contributors as well. Not sure whether we could get rid of the "Some workflows need approval" button in the PRs, though.

This could also work. Maybe start of with a simple on: schedule workflow and consider adding back the pull_request_review triggers once it is working well?

@wolfgangwalther
Copy link
Contributor Author

using on: schedule would certainly simplify the implementation. In practice, we could limit the result to all PRs updated since the last scheduled run and maybe do a pass each hour?

Yes, I was thinking about "1 hour" as well. This workflow could also implement something like a "one hour self-merge cooldown" mentioned in NixOS/org#122 (comment), which also only works with a scheduled workflow. Plenty of things we can make use of a scheduled workflow for!

This could also work. Maybe start of with a simple on: schedule workflow and consider adding back the pull_request_review triggers once it is working well?

Well, we already have the pull_request_review stuff running, so I'd just keep it that way for now. No need to back it out again, imho, at least not before we got feedback on whether "first-time contribution and new to github" would be an acceptable setting.

I'd still look into setting up a scheduled workflow at the same time.

@philiptaron
Copy link
Contributor

I’m really excited about the scheduled workflow for efficiency reasons as well. Being able to batch these and similar low priority jobs together is absolutely a win for efficiency which lets them do substantially more work across a substantially greater set of PRS then they would otherwise. The fact that it might be required for correctness or availability reasons is just another motivating factor.

@MattSturgeon
Copy link
Contributor

Being able to batch these and similar low priority jobs together is absolutely a win for efficiency which lets them do substantially more work across a substantially greater set of PRS then they would otherwise.

To really benefit from the efficiency gain we'd have to drop the pull_request_review triggers and rely exclusively on schedule.

This may be safer from a security perspective too, since we won't be running workflows from the fork's context.

I don't feel strongly on if we should have a hybrid approach or exclusively use schedule, but the latter does sound simpler to reason about.

@wolfgangwalther
Copy link
Contributor Author

but the latter does sound simpler to reason about.

That's true and tips the scale for me - the workflow_run stuff is really a bit odd.

@wolfgangwalther
Copy link
Contributor Author

Also, the workflow_run stuff doesn't seem to be that reliable:

2025-06-13T17:32:39,266835429+02:00

It should have added the label after the approval already, but only did after the force push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: continuous integration Affects continuous integration (CI) in Nixpkgs, including Ofborg and GitHub Actions 6.topic: policy discussion Discuss policies to work in and around Nixpkgs 8.has: port to stable This PR already has a backport to the stable release. 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux. 12.approvals: 1 This PR was reviewed and approved by one person. 12.approved-by: package-maintainer This PR was reviewed and approved by a maintainer listed in any of the changed packages.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automate approval badges update through GitHub action

4 participants