-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Branch protection: Possibility to not use whitelist but allow anyone with write access #9055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Branch protection: Possibility to not use whitelist but allow anyone with write access #9055
Conversation
Codecov Report
@@ Coverage Diff @@
## master #9055 +/- ##
==========================================
- Coverage 41.08% 41.03% -0.06%
==========================================
Files 555 556 +1
Lines 72403 72535 +132
==========================================
+ Hits 29750 29764 +14
- Misses 38912 39028 +116
- Partials 3741 3743 +2
Continue to review full report at Codecov.
|
|
Sorry if I didn't get this correctly; the original issue was kind of unclear, but what I understood is that the OP asked for something like...?:
This PR seems very useful nonetheless. |
|
Reading the issue again (and also the comment that was in Chinese), I see I misunderstood what the original poster wanted. I though the problem was that you wanted to easily disable only force pushing for everyone but allowing pushing (without having to enter a whitelist), and maybe also that it shall be enforced for all branches (which can be solved by wildcard protected branches). |
|
Please resolve conflicts. |
|
Migration number has changed +1 |
0fa8144 to
d48fcb4
Compare
# Conflicts: # public/js/index.js # public/js/index.js.map
models/branches.go
Outdated
| if review.Type != ReviewTypeApprove { | ||
| continue | ||
| } | ||
| if !protectBranch.EnableApprovalsWhitelist { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO approvals should "remember" if the users were whitelisted at the moment they approved and this value should not be recalculated. Two reasons for this:
- This is not "list friendly"; it's very difficult to optimize as we want to have the PR list showing the number of approvals. If possible, we should resolve the list with a simple join/select.
- If rules change, history should not be rewritten. Non-counting approvals should remain non-counting and vice-versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your argumentation makes sense, but that would require quite large code change (need new database columns). I think it should be a different PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's that big of a change. I think you only need a new "Verified bool" column to the review table and move this check to the code that inserts the review. One day that "verified" column can reflect a 2FA, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be called OfficialReviewer (bool)?
You mean there will be an option to only accept 2FA users as reviewers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it can be called OfficialReviewer, although that would lock the meaning. What I mean is that what today is a bool (verified/not verified) tomorrow can be an enum (not verified/verified/verified+signed, etc.). But whatever we may want in the future can be solved with a migration no problem, so feel free to choose whatever you feel that fits best. 👍
# Conflicts: # public/js/index.js # public/js/index.js.map
# Conflicts: # public/js/index.js # public/js/index.js.map
128f1ca to
b63974e
Compare
models/migrations/v110.go
Outdated
| } | ||
|
|
||
| var pageSize int64 = 20 | ||
| qresult, err := sess.QueryInterface("SELECT max(id) as max_id FROM pull_request") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try:
var max_id int64
if _, err = sess.Select("max(id) as `max_id`").Table("pull_request").Get(&max_id); err != nil {
return err
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current query seem to work, although your suggestion maybe looks a bit simpler.
models/review.go
Outdated
| if x.Dialect().DBType() == core.MSSQL { | ||
| err = x.SQL(`SELECT [user].*, review.type, review.official, review.review_updated_unix FROM | ||
| (SELECT review.id, review.type, review.reviewer_id, max(review.updated_unix) as review_updated_unix | ||
| (SELECT review.id, review.type, review.reviewer_id, review.official, max(review.updated_unix) as review_updated_unix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line I don't understand. If you have review.id in the row, all lines are already unique (unless there were multiple users with the same id which should never happen). A MAX(updated_unix) will be the same as updated_unix without the Group By. 🤔
What is it that you are trying to accomplish? Bring up the latest review? The query from the migration should achieve that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function shall return the latest review of each reviewer (not necessary official reviewers). I see that I actually don't use the change in this function, probably rewrote something. I think later on the information on which reviews are "official" should be shown on the pull request page.
The max(review.updated_unix) as review_updated_unix is used to sort the list and ensure the group by gets the latest review. However it might have been enough to get the max(id) instead, I don't see how reviews from same reviewer could change order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function shall return the latest review of each reviewer (not necessary official reviewers). I see that I actually don't use the change in this function, probably rewrote something. I think later on the information on which reviews are "official" should be shown on the pull request page.
The
max(review.updated_unix) as review_updated_unixis used to sort the list and ensure the group by gets the latest review. However it might have been enough to get themax(id)instead, I don't see how reviews from same reviewer could change order.
This kind of query will not work. If you have two different review.type (e.g. comment + approval) from the same user, you'll get two rows because Group By will see them as different values. To get the latest record you need to do a select in select like the one used in the migration:
if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND type in (?, ?) GROUP BY issue_id, reviewer_id)",
issueID, models.ReviewTypeApprove, models.ReviewTypeReject).
Find(&reviews); err != nil {
return err
}Of course the example above requires some adaptation to get the JOIN and the columns you're expecting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is actually solved further down in the function (I don't say it is a good solution 😄)
// We need to group our results by the fields we want, otherwise some databases will fail (only_full_group_by).
// But becaus we're doing this, we need to manually filter out multiple reviews of different types by the
// same person because we only want to show the newest review grouped by user. Thats why we're using a map here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your query might work... although it doesn't work to just remove some of the group by fields for pgsql and mysql (due to some only_full_group_by setting).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I didn't want to touch this function more than needed (and actually I can revert the changes I done). But the only thing I added was the Official field to the results, and hence added in SQL where necessary. The multiple rows is handled by the code later already.
I think maybe this function tries to do too much in SQL which makes it a bit complex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To exemplify this, let me give you one example. Suppose you have this in the review table:
| review ID | user_id | review_type | review_date |
|---|---|---|---|
| 1 | 1 | comment | Jan 1 |
| 2 | 2 | request-changes | Jan 2 |
| 3 | 1 | comment | Jan 3 |
| 4 | 3 | comment | Jan 4 |
| 5 | 2 | approval | Jan 5 |
If you do:
select max(review_id), user_id, review_type, max(review_date)
from review
group by user_id, review_type;This is what you will get:
| review ID | user_id | review_type | review_date |
|---|---|---|---|
| 2 | 2 | request-changes | Jan 2 |
| 3 | 1 | comment | Jan 3 |
| 4 | 3 | comment | Jan 4 |
| 5 | 2 | approval | Jan 5 |
As you see, the two reviews from user #1 were consolidated in one row, as the fields user_id and review_type are the same for all records; user #3 is not a problem, but the two reviews from user #2 show up as different rows.
That's why the only way to get this SQL right is with a sub-query:
select max(id), user_id from review group by user_id;Will give you:
| review ID | user_id |
|---|---|
| 3 | 1 |
| 4 | 3 |
| 5 | 2 |
So, these are the records that you're looking for:
select review_id, user_id, review_type, review_date
from `review`
where review_id in (
select max(id), user_id from `review` as `review_inner` group by user_id
)returns:
| review ID | user_id | review_type | date |
|---|---|---|---|
| 3 | 1 | comment | Jan 3 |
| 4 | 3 | comment | Jan 4 |
| 5 | 2 | approval | Jan 5 |
which is what you want. With this method you can perform joins too:
select review_id, user_id, review_type, review_date, `user`.*
from `review`
INNER JOIN `user` ON `user`.`id` = `review`.`user_id`
where review_id in (
select max(id), user_id from `review` as `review_inner` group by user_id
)There's probably a nice way to write all of this with a couple of xorm directives (e.g. I think you can write the subquery in builder and use it in the In() operator, but I'm not sure), so I think it will be just quicker to do it with sess.SQL() like you did in migrations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very long informative response 😄.
I just wanted to point out that the existing code actually works, although not very efficient or elegant. I will see if I can rewrite the SQL query in a better way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry! I didn't realize that the code wasn't yours. It's my tendency of checking the right column more thoroughly than the left one. 😋
If you just don't feel like doing that change just let me know, because that's the only thing I was waiting for to give my 👍.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote the code and think it is easier to understand now although there will be a few more sql queries, but normally there will not be that many reviews for a PR.
8423e2f to
b1537d4
Compare
9d7d184 to
72044ff
Compare
72044ff to
8198531
Compare
|
So finally I think this PR is ready.... |
4515ccd to
6ca746a
Compare
guillep2k
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the good work!
# Conflicts: # models/migrations/migrations.go # models/migrations/v110.go # models/review.go # public/js/index.js # public/js/index.js.map
fixes #8288. Related to #5731 (does not solve exactly what poster wants)
Gives three options for pushing to protected branch:
For approvals a new checkbox is added to enable whitelist. Otherwise anyone with write access is considered as a valid approval.
Screenshot