golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.91k stars 17.65k forks source link

github: branch protections #59048

Open seankhliao opened 1 year ago

seankhliao commented 1 year ago

As someone in the "go-approvers" github group, I'm always slightly concerned about accidentally pressing the "Merge Pull Request" button when going through PRs.

Can we have branch protection to disable the button by default? I believe matching the master branch with Require a pull request before merging with sub-options Require approvals and Require review from Code Owners will be sufficient to prevent accidental presses.

There's also a Lock branch option, though I'm unsure how it'll interact with the sync from upstream.

cc @golang/release

hyangah commented 1 year ago

I think another option is set a branch protection rule for all branches (*), and use "Restrict who can push to the matching branches" and allow only the bot. We tried the Lock branch option for vscode-go before and that broke gitmirror :-(

BTW even when a PR or commit is accidentally merged, I've seen the next gitmirror force push and sync with the upstream.

heschi commented 1 year ago

I've applied branch protections to the main Go repository. Any others you'd like locked down?

dmitshur commented 1 year ago

We can do all golang.org/x GitHub mirrors too (and the special case without x, golang.org/dl). At least I can't think of a downside to treating them the same as the main Go repo mirror for the purpose of this issue. That'll leave various non-mirror repos or less clear cases (e.g. protobuf, leveldb, cwg, go-get-issue-15410, etc.).

heschi commented 1 year ago

I mostly just don't want to do it manually. Seems like there's no CLI, you have to use graphql: https://github.com/cli/cli/issues/3528

I assume that the overwhelming majority of PRs go to the main repo, so I think this is done enough, personally. If someone wants to figure out the graphql I'm happy to run it as an admin.

seankhliao commented 1 year ago

This should do it, I tested it on a few of my repos: GH_TOKEN=a-token-with-org-admin go run . -r $repo1 -r $repo2

package main

import (
    "context"
    "flag"
    "log"
    "os"

    "github.com/google/go-github/v53/github"
    "golang.org/x/oauth2"
)

func main() {
    var repos []string
    flag.Func("r", "", func(s string) error {
        repos = append(repos, s)
        return nil
    })
    flag.Parse()

    ctx := context.Background()
    ts := oauth2.StaticTokenSource(
        &oauth2.Token{AccessToken: os.Getenv("GH_TOKEN")},
    )
    tc := oauth2.NewClient(ctx, ts)
    client := github.NewClient(tc)

    for _, repo := range repos {
        log.Println("setting branch protection for", repo)
        _, _, err := client.Repositories.UpdateBranchProtection(ctx, "golang", repo, "master", &github.ProtectionRequest{
            RequiredPullRequestReviews: &github.PullRequestReviewsEnforcementRequest{
                RequireCodeOwnerReviews:      true,
                RequiredApprovingReviewCount: 1,
            },
        })
        if err != nil {
            log.Println("error updating branch protections for", repo, err)
            continue
        }
        log.Println("set branch protection for", repo)
    }
}

The list of public repos for the golang org is (I'm not sure if all of them should have this set?)

go
tools
pkgsite
net
website
build
vulndb
govulncheck-action
pkgsite-metrics
crypto
oauth2
vscode-go
term
image
sys
arch
vuln
debug
telemetry
dl
proposal
benchmarks
text
exp
gofrontend
review
mod
appengine
sync
mobile
protobuf
example
perf
geo
playground
glog
freetype
groupcache
tour
.allstar
time
snappy
vgo
.github
scratch
xerrors
sublime-config
sublime-build
talks
blog
cwg
heschi commented 1 year ago

Sorry for the slow response. For the repos in question which are just Gerrit mirrors, only GopherBot should be able to push to them. So this is the (only) rule I applied:

image

Is it easy to update the script to do that?

seankhliao commented 1 year ago

I think this should work

package main

import (
    "context"
    "flag"
    "log"
    "os"

    "github.com/google/go-github/v53/github"
    "golang.org/x/oauth2"
)

func main() {
    var repos []string
    flag.Func("r", "", func(s string) error {
        repos = append(repos, s)
        return nil
    })
    flag.Parse()

    ctx := context.Background()
    ts := oauth2.StaticTokenSource(
        &oauth2.Token{AccessToken: os.Getenv("GH_TOKEN")},
    )
    tc := oauth2.NewClient(ctx, ts)
    client := github.NewClient(tc)

    for _, repo := range repos {
        log.Println("setting branch protection for", repo)
        _, _, err := client.Repositories.UpdateBranchProtection(ctx, "golang", repo, "master", &github.ProtectionRequest{
            Restrictions: &github.BranchRestrictionsRequest{
                Users: []string{"gopherbot"},
                Teams: []string{},
                Apps:  []string{},
            },
        })
        if err != nil {
            log.Println("error updating branch protections for", repo, err)
            continue
        }
        log.Println("set branch protection for", repo)
    }
}
heschi commented 1 year ago

2023/07/13 12:36:42 error updating branch protections for arch PUT https://api.github.com/repos/golang/arch/branches/master/protection: 404 Not Found []

Maybe I'm missing something, but arch is a real repo and the request seems appropriate based on the API docs.

(Also I'd like to set it on all branches, but that really does require graphql apparently...)