From 0e18d1f7b799548e2a636baa31fce72414b6eeaa Mon Sep 17 00:00:00 2001 From: Johan Lindell Date: Sun, 25 Apr 2021 13:59:04 +0200 Subject: [PATCH] feat: added Gitea support --- cmd/root.go | 80 ++++++- docs/README.template.md | 6 +- go.mod | 1 + go.sum | 5 + internal/scm/gitea/gitea.go | 422 ++++++++++++++++++++++++++++++++++++ internal/scm/gitea/util.go | 28 +++ 6 files changed, 531 insertions(+), 11 deletions(-) create mode 100644 internal/scm/gitea/gitea.go create mode 100644 internal/scm/gitea/util.go diff --git a/cmd/root.go b/cmd/root.go index 6a514e7f..74432240 100755 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,6 +11,7 @@ import ( "github.com/lindell/multi-gitter/internal/domain" "github.com/lindell/multi-gitter/internal/http" "github.com/lindell/multi-gitter/internal/multigitter" + "github.com/lindell/multi-gitter/internal/scm/gitea" "github.com/lindell/multi-gitter/internal/scm/github" "github.com/lindell/multi-gitter/internal/scm/gitlab" @@ -55,9 +56,9 @@ func configurePlatform(cmd *cobra.Command) { flags.StringSliceP("repo", "R", nil, "The name, including owner of a GitHub repository in the format \"ownerName/repoName\"") flags.StringSliceP("project", "P", nil, "The name, including owner of a GitLab project in the format \"ownerName/repoName\"") - flags.StringP("platform", "p", "github", "The platform that is used. Available values: github, gitlab") + flags.StringP("platform", "p", "github", "The platform that is used. Available values: github, gitlab, gitea") _ = cmd.RegisterFlagCompletionFunc("platform", func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { - return []string{"github", "gitlab"}, cobra.ShellCompDirectiveDefault + return []string{"github", "gitlab", "gitea"}, cobra.ShellCompDirectiveDefault }) // Autocompletion for organizations @@ -215,6 +216,8 @@ func getVersionController(flag *flag.FlagSet, verifyFlags bool) (multigitter.Ver return createGithubClient(flag, verifyFlags) case "gitlab": return createGitlabClient(flag, verifyFlags) + case "gitea": + return createGiteaClient(flag, verifyFlags) } } @@ -223,7 +226,6 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi orgs, _ := flag.GetStringSlice("org") users, _ := flag.GetStringSlice("user") repos, _ := flag.GetStringSlice("repo") - mergeTypeStrs, _ := flag.GetStringSlice("merge-type") // Only used for the merge command if verifyFlags && len(orgs) == 0 && len(users) == 0 && len(repos) == 0 { return nil, errors.New("no organization, user or repo set") @@ -242,13 +244,9 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi } } - // Convert all defined merge types (if any) - mergeTypes := make([]domain.MergeType, len(mergeTypeStrs)) - for i, mt := range mergeTypeStrs { - mergeTypes[i], err = domain.ParseMergeType(mt) - if err != nil { - return nil, err - } + mergeTypes, err := getMergeTypes(flag) + if err != nil { + return nil, err } vc, err := github.New(token, gitBaseURL, http.NewLoggingRoundTripper, github.RepositoryListing{ @@ -298,6 +296,50 @@ func createGitlabClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi return vc, nil } +func createGiteaClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.VersionController, error) { + giteaBaseURL, _ := flag.GetString("base-url") + orgs, _ := flag.GetStringSlice("org") + users, _ := flag.GetStringSlice("user") + repos, _ := flag.GetStringSlice("repo") + + if verifyFlags && len(orgs) == 0 && len(users) == 0 && len(repos) == 0 { + return nil, errors.New("no organization, user or repository set") + } + + if giteaBaseURL == "" { + return nil, errors.New("no base-url set") + } + + token, err := getToken(flag) + if err != nil { + return nil, err + } + + repoRefs := make([]gitea.RepositoryReference, len(repos)) + for i := range repos { + repoRefs[i], err = gitea.ParseRepositoryReference(repos[i]) + if err != nil { + return nil, err + } + } + + mergeTypes, err := getMergeTypes(flag) + if err != nil { + return nil, err + } + + vc, err := gitea.New(token, giteaBaseURL, gitea.RepositoryListing{ + Organizations: orgs, + Users: users, + Repositories: repoRefs, + }, mergeTypes) + if err != nil { + return nil, err + } + + return vc, nil +} + func getToken(flag *flag.FlagSet) (string, error) { if OverrideVersionController != nil { return "", nil @@ -310,6 +352,8 @@ func getToken(flag *flag.FlagSet) (string, error) { token = ght } else if ght := os.Getenv("GITLAB_TOKEN"); ght != "" { token = ght + } else if ght := os.Getenv("GITEA_TOKEN"); ght != "" { + token = ght } } @@ -320,6 +364,22 @@ func getToken(flag *flag.FlagSet) (string, error) { return token, nil } +func getMergeTypes(flag *flag.FlagSet) ([]domain.MergeType, error) { + mergeTypeStrs, _ := flag.GetStringSlice("merge-type") // Only used for the merge command + + // Convert all defined merge types (if any) + var err error + mergeTypes := make([]domain.MergeType, len(mergeTypeStrs)) + for i, mt := range mergeTypeStrs { + mergeTypes[i], err = domain.ParseMergeType(mt) + if err != nil { + return nil, err + } + } + + return mergeTypes, nil +} + // nopWriter is a writer that does nothing type nopWriter struct{} diff --git a/docs/README.template.md b/docs/README.template.md index 0d4a7479..644f23ef 100755 --- a/docs/README.template.md +++ b/docs/README.template.md @@ -73,7 +73,7 @@ go get github.com/lindell/multi-gitter ## Token -To use multi-gitter, a token that is allowed to list repositories and create pull requests is needed. This token can either be set in the `GITHUB_TOKEN` or `GITLAB_TOKEN` environment variable, or by using the `--token` flag. +To use multi-gitter, a token that is allowed to list repositories and create pull requests is needed. This token can either be set in the `GITHUB_TOKEN`, `GITLAB_TOKEN`, `GITEA_TOKEN` environment variable, or by using the `--token` flag. ### GitHub [How to generate a GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). Make sure to give to `repo` permissions. @@ -82,6 +82,10 @@ To use multi-gitter, a token that is allowed to list repositories and create pul [How to generate a GitLab personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html). Make sure to give to it the `api` permission. +### Gitea + +In Gitea, access tokens can be generated under Settings -> Applications -> Manage Access Tokens + ## Usage {{range .Commands}} * [{{ .Name }}](#-usage-of-{{ .Name }}) {{ .Short }}{{end}} diff --git a/go.mod b/go.mod index 61206790..fcfe3fce 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/lindell/multi-gitter go 1.16 require ( + code.gitea.io/sdk/gitea v0.14.0 github.com/go-git/go-git/v5 v5.2.0 github.com/google/go-github/v33 v33.0.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 75ce5931..e231cdaf 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0= +code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -77,6 +79,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -173,6 +176,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/internal/scm/gitea/gitea.go b/internal/scm/gitea/gitea.go new file mode 100644 index 00000000..6cebc468 --- /dev/null +++ b/internal/scm/gitea/gitea.go @@ -0,0 +1,422 @@ +package gitea + +import ( + "context" + "fmt" + "net/http" + "net/url" + "sort" + "strings" + + "code.gitea.io/sdk/gitea" + "github.com/pkg/errors" + + "github.com/lindell/multi-gitter/internal/domain" + internalHTTP "github.com/lindell/multi-gitter/internal/http" +) + +// New create a new Gitea client +func New(token, baseURL string, repoListing RepositoryListing, mergeTypes []domain.MergeType) (*Gitea, error) { + gitea := &Gitea{ + RepositoryListing: repoListing, + + baseURL: baseURL, + token: token, + + MergeTypes: mergeTypes, + } + + // Initialize the gitea client to ensure no error will occur when running a function + _, err := gitea.giteaClientErr(context.Background()) + + return gitea, err +} + +func (g *Gitea) giteaClientErr(ctx context.Context) (*gitea.Client, error) { + client, err := gitea.NewClient( + g.baseURL, + gitea.SetHTTPClient(&http.Client{ + Transport: internalHTTP.LoggingRoundTripper{}, + }), + gitea.SetToken(g.token), + gitea.SetContext(ctx), + ) + return client, err +} + +func (g *Gitea) giteaClient(ctx context.Context) *gitea.Client { + client, _ := g.giteaClientErr(ctx) + return client +} + +// Gitea contain Gitea configuration +type Gitea struct { + RepositoryListing + + baseURL string + token string + + MergeTypes []domain.MergeType +} + +// RepositoryListing contains information about which repositories that should be fetched +type RepositoryListing struct { + Organizations []string + Users []string + Repositories []RepositoryReference +} + +// RepositoryReference contains information to be able to reference a repository +type RepositoryReference struct { + OwnerName string + Name string +} + +// ParseRepositoryReference parses a repository reference from the format "ownerName/repoName" +func ParseRepositoryReference(val string) (RepositoryReference, error) { + split := strings.Split(val, "/") + if len(split) != 2 { + return RepositoryReference{}, fmt.Errorf("could not parse repository reference: %s", val) + } + return RepositoryReference{ + OwnerName: split[0], + Name: split[1], + }, nil +} + +type repository struct { + url url.URL + name string + ownerName string + defaultBranch string +} + +func (r repository) URL(token string) string { + // Set the token as https://oauth2:TOKEN@url + r.url.User = url.UserPassword("oauth2", token) + return r.url.String() +} + +func (r repository) DefaultBranch() string { + return r.defaultBranch +} + +func (r repository) FullName() string { + return fmt.Sprintf("%s/%s", r.ownerName, r.name) +} + +type pullRequest struct { + ownerName string + repoName string + branchName string + index int64 // The id of the PR + webURL string + status domain.PullRequestStatus +} + +func (pr pullRequest) String() string { + return fmt.Sprintf("%s/%s #%d", pr.ownerName, pr.repoName, pr.index) +} + +func (pr pullRequest) Status() domain.PullRequestStatus { + return pr.status +} + +func (pr pullRequest) URL() string { + return pr.webURL +} + +// GetRepositories fetches repositories from all sources (groups/user/specific repo) +func (g *Gitea) GetRepositories(ctx context.Context) ([]domain.Repository, error) { + allRepos, err := g.getRepositories(ctx) + if err != nil { + return nil, err + } + + repos := make([]domain.Repository, 0, len(allRepos)) + for _, repo := range allRepos { + u, err := url.Parse(repo.CloneURL) + if err != nil { + return nil, err // TODO: better error + } + + repos = append(repos, repository{ + url: *u, + name: repo.Name, + ownerName: repo.Owner.UserName, + defaultBranch: repo.DefaultBranch, + }) + } + + return repos, nil +} + +func (g *Gitea) getRepositories(ctx context.Context) ([]*gitea.Repository, error) { + allRepos := []*gitea.Repository{} + + for _, group := range g.Organizations { + repos, err := g.getGroupRepositories(ctx, group) + if err != nil { + return nil, err + } + allRepos = append(allRepos, repos...) + } + + for _, user := range g.Users { + repos, err := g.getUserRepositories(ctx, user) + if err != nil { + return nil, err + } + allRepos = append(allRepos, repos...) + } + + for _, repo := range g.Repositories { + repo, err := g.getRepository(ctx, repo) + if err != nil { + return nil, err + } + allRepos = append(allRepos, repo) + } + + // Remove duplicate repos + repoMap := map[int64]*gitea.Repository{} + for _, repo := range allRepos { + repoMap[repo.ID] = repo + } + allRepos = make([]*gitea.Repository, 0, len(repoMap)) + for _, repo := range repoMap { + allRepos = append(allRepos, repo) + } + sort.Slice(allRepos, func(i, j int) bool { + return allRepos[i].ID < allRepos[j].ID + }) + + return allRepos, nil +} + +func (g *Gitea) getGroupRepositories(ctx context.Context, groupName string) ([]*gitea.Repository, error) { + var allRepos []*gitea.Repository + for i := 1; ; i++ { + repos, _, err := g.giteaClient(ctx).ListOrgRepos(groupName, gitea.ListOrgReposOptions{ + ListOptions: gitea.ListOptions{ + Page: i, + PageSize: 100, + }, + }) + if err != nil { + return nil, err + } + + allRepos = append(allRepos, repos...) + + if len(repos) < 100 { + break + } + } + return allRepos, nil +} + +func (g *Gitea) getRepository(ctx context.Context, repoRef RepositoryReference) (*gitea.Repository, error) { + repo, _, err := g.giteaClient(ctx).GetRepo(repoRef.OwnerName, repoRef.Name) + if err != nil { + return nil, err + } + return repo, err +} + +func (g *Gitea) getUserRepositories(ctx context.Context, username string) ([]*gitea.Repository, error) { + var allRepos []*gitea.Repository + for i := 1; ; i++ { + repos, _, err := g.giteaClient(ctx).ListUserRepos(username, gitea.ListReposOptions{ + ListOptions: gitea.ListOptions{ + Page: i, + PageSize: 100, + }, + }) + if err != nil { + return nil, err + } + + allRepos = append(allRepos, repos...) + + if len(repos) < 100 { + break + } + } + return allRepos, nil +} + +// CreatePullRequest creates a pull request +func (g *Gitea) CreatePullRequest(ctx context.Context, repo domain.Repository, newPR domain.NewPullRequest) (domain.PullRequest, error) { + r := repo.(repository) + + pr, _, err := g.giteaClient(ctx).CreatePullRequest(r.ownerName, r.name, gitea.CreatePullRequestOption{ + Head: newPR.Head, + Base: newPR.Base, + Title: newPR.Title, + Body: newPR.Body, + }) + if err != nil { + return nil, errors.Wrap(err, "could not create pull request") + } + + _, err = g.giteaClient(ctx).CreateReviewRequests(r.ownerName, r.name, pr.Index, gitea.PullReviewRequestOptions{ + Reviewers: newPR.Reviewers, + }) + if err != nil { + return nil, errors.Wrap(err, "could not add reviewer to pull request") + } + + return pullRequest{ + repoName: r.name, + ownerName: r.ownerName, + branchName: newPR.Head, + index: pr.Index, + webURL: pr.HTMLURL, + }, nil +} + +// GetPullRequests gets all pull requests of with a specific branch +func (g *Gitea) GetPullRequests(ctx context.Context, branchName string) ([]domain.PullRequest, error) { + repos, err := g.getRepositories(ctx) + if err != nil { + return nil, err + } + + prs := []domain.PullRequest{} + for _, repo := range repos { + pr, err := g.getPullRequest(ctx, branchName, repo) + if err != nil { + return nil, err + } + if pr == nil { + continue + } + + status, err := g.pullRequestStatus(ctx, repo, pr) + if err != nil { + return nil, err + } + + prs = append(prs, pullRequest{ + repoName: repo.Name, + ownerName: repo.Owner.UserName, + branchName: branchName, + status: status, + index: pr.Index, + webURL: pr.HTMLURL, + }) + } + + return prs, nil +} + +func (g *Gitea) getPullRequest(ctx context.Context, branchName string, repo *gitea.Repository) (*gitea.PullRequest, error) { + // We would like to be able to search for a pr with a specific head here, but current (2021-04-24), that option does not exist in the API + prs, _, err := g.giteaClient(ctx).ListRepoPullRequests(repo.Owner.UserName, repo.Name, gitea.ListPullRequestsOptions{ + State: "all", + Sort: "recentupdate", + }) + if err != nil { + return nil, err + } + + for _, pr := range prs { + if pr.Head.Name == branchName { + return pr, nil + } + } + return nil, nil +} + +func (g *Gitea) pullRequestStatus(ctx context.Context, repo *gitea.Repository, pr *gitea.PullRequest) (domain.PullRequestStatus, error) { + if pr.Merged != nil { + return domain.PullRequestStatusMerged, nil + } + + if pr.State == gitea.StateClosed { + return domain.PullRequestStatusClosed, nil + } + + status, _, err := g.giteaClient(ctx).GetCombinedStatus(repo.Owner.UserName, repo.Name, pr.Head.Sha) + if err != nil { + return domain.PullRequestStatusUnknown, err + } + + if len(status.Statuses) == 0 { + return domain.PullRequestStatusSuccess, nil + } + + switch status.State { + case gitea.StatusPending: + return domain.PullRequestStatusPending, nil + case gitea.StatusSuccess: + return domain.PullRequestStatusSuccess, nil + case gitea.StatusError, gitea.StatusFailure: + return domain.PullRequestStatusError, nil + } + + return domain.PullRequestStatusUnknown, nil +} + +// MergePullRequest merges a pull request +func (g *Gitea) MergePullRequest(ctx context.Context, pullReq domain.PullRequest) error { + pr := pullReq.(pullRequest) + + repo, _, err := g.giteaClient(ctx).GetRepo(pr.ownerName, pr.repoName) + if err != nil { + return errors.Wrapf(err, "could not fetch %s/%s repository", pr.ownerName, pr.repoName) + } + + // Filter out all merge types to only the allowed ones, but keep the order of the ones left + mergeTypes := domain.MergeTypeIntersection(g.MergeTypes, repoMergeTypes(repo)) + if len(mergeTypes) == 0 { + return errors.New("none of the configured merge types was permitted") + } + + merged, _, err := g.giteaClient(ctx).MergePullRequest(pr.ownerName, pr.repoName, pr.index, gitea.MergePullRequestOption{ + Style: mergeTypeGiteaName[mergeTypes[0]], + }) + if err != nil { + return errors.Wrapf(err, "could not merge %s/%s#%d", pr.ownerName, pr.repoName, pr.index) + } + + if !merged { + return errors.Errorf("could not merge %s/%s#%d", pr.ownerName, pr.repoName, pr.index) + } + + deleted, _, err := g.giteaClient(ctx).DeleteRepoBranch(pr.ownerName, pr.repoName, pr.branchName) + if err != nil { + return errors.Wrapf(err, "could not delete branch after merging %s/%s", pr.ownerName, pr.repoName) + } + + if !deleted { + return errors.Errorf("could not delete branch after merging %s/%s", pr.ownerName, pr.repoName) + } + + return nil +} + +// ClosePullRequest closes a pull request +func (g *Gitea) ClosePullRequest(ctx context.Context, pullReq domain.PullRequest) error { + pr := pullReq.(pullRequest) + + state := gitea.StateClosed + _, _, err := g.giteaClient(ctx).EditPullRequest(pr.ownerName, pr.repoName, pr.index, gitea.EditPullRequestOption{ + State: &state, + }) + if err != nil { + return errors.Wrapf(err, "could not close %s/%s#%d", pr.ownerName, pr.repoName, pr.index) + } + + deleted, _, err := g.giteaClient(ctx).DeleteRepoBranch(pr.ownerName, pr.repoName, pr.branchName) + if err != nil { + return errors.Wrapf(err, "could not delete branch after merging %s/%s", pr.ownerName, pr.repoName) + } + + if !deleted { + return errors.Errorf("could not delete branch after merging %s/%s", pr.ownerName, pr.repoName) + } + + return nil +} diff --git a/internal/scm/gitea/util.go b/internal/scm/gitea/util.go new file mode 100644 index 00000000..dd82a523 --- /dev/null +++ b/internal/scm/gitea/util.go @@ -0,0 +1,28 @@ +package gitea + +import ( + "code.gitea.io/sdk/gitea" + "github.com/lindell/multi-gitter/internal/domain" +) + +// maps merge types to what they are called in the gitea api +var mergeTypeGiteaName = map[domain.MergeType]gitea.MergeStyle{ + domain.MergeTypeMerge: gitea.MergeStyleMerge, + domain.MergeTypeRebase: gitea.MergeStyleRebase, + domain.MergeTypeSquash: gitea.MergeStyleSquash, +} + +// repoMergeTypes returns a list of all allowed merge types +func repoMergeTypes(repo *gitea.Repository) []domain.MergeType { + ret := []domain.MergeType{} + if repo.AllowMerge { + ret = append(ret, domain.MergeTypeMerge) + } + if repo.AllowMerge { + ret = append(ret, domain.MergeTypeRebase) + } + if repo.AllowSquash { + ret = append(ret, domain.MergeTypeSquash) + } + return ret +}