diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index a8d14cfe20..c003e7884b 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -4135,6 +4135,9 @@ const docTemplate = `{ "owner": { "type": "string" }, + "pr_enabled": { + "type": "boolean" + }, "private": { "type": "boolean" }, diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index eb8dd2083f..d9a92c3734 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -15,8 +15,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc-gen-go v1.32.0 +// protoc v4.25.1 // source: woodpecker.proto package proto @@ -1221,11 +1221,11 @@ var file_woodpecker_proto_rawDesc = []byte{ 0x68, 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x6f, 0x2e, 0x77, 0x6f, 0x6f, 0x64, 0x70, + 0x73, 0x65, 0x22, 0x00, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x6f, 0x2e, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x6f, 0x6f, - 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, - 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, + 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pipeline/rpc/proto/woodpecker_grpc.pb.go b/pipeline/rpc/proto/woodpecker_grpc.pb.go index 787f8f6e70..837ce74e09 100644 --- a/pipeline/rpc/proto/woodpecker_grpc.pb.go +++ b/pipeline/rpc/proto/woodpecker_grpc.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.25.1 // source: woodpecker.proto package proto diff --git a/server/forge/bitbucket/bitbucket.go b/server/forge/bitbucket/bitbucket.go index c1df1efad6..3415ae6cb3 100644 --- a/server/forge/bitbucket/bitbucket.go +++ b/server/forge/bitbucket/bitbucket.go @@ -367,7 +367,7 @@ func (c *config) PullRequests(ctx context.Context, u *model.User, r *model.Repo, if err != nil { return nil, err } - result := []*model.PullRequest{} + var result []*model.PullRequest for _, pullRequest := range pullRequests { result = append(result, &model.PullRequest{ Index: model.ForgeRemoteID(strconv.Itoa(int(pullRequest.ID))), diff --git a/server/forge/bitbucket/convert.go b/server/forge/bitbucket/convert.go index 286d3e0b84..7fb8cb6b55 100644 --- a/server/forge/bitbucket/convert.go +++ b/server/forge/bitbucket/convert.go @@ -62,6 +62,7 @@ func convertRepo(from *internal.Repo, perm *internal.RepoPerm) *model.Repo { SCMKind: model.SCMKind(from.Scm), Branch: from.Mainbranch.Name, Perm: convertPerm(perm), + PREnabled: true, } if repo.SCMKind == model.RepoHg { repo.Branch = "default" diff --git a/server/forge/gitea/helper.go b/server/forge/gitea/helper.go index ce4709a5f7..7d00d78a88 100644 --- a/server/forge/gitea/helper.go +++ b/server/forge/gitea/helper.go @@ -49,6 +49,7 @@ func toRepo(from *gitea.Repository) *model.Repo { CloneSSH: from.SSHURL, Branch: from.DefaultBranch, Perm: toPerm(from.Permissions), + PREnabled: from.HasPullRequests, } } diff --git a/server/forge/github/convert.go b/server/forge/github/convert.go index c8b968d690..917982ba60 100644 --- a/server/forge/github/convert.go +++ b/server/forge/github/convert.go @@ -95,6 +95,7 @@ func convertRepo(from *github.Repository) *model.Repo { Avatar: from.GetOwner().GetAvatarURL(), Perm: convertPerm(from.GetPermissions()), SCMKind: model.RepoGit, + PREnabled: true, } return repo } @@ -152,6 +153,7 @@ func convertRepoHook(eventRepo *github.PushEventRepository) *model.Repo { CloneSSH: eventRepo.GetSSHURL(), Branch: eventRepo.GetDefaultBranch(), SCMKind: model.RepoGit, + PREnabled: true, } if repo.FullName == "" { repo.FullName = repo.Owner + "/" + repo.Name diff --git a/server/forge/gitlab/convert.go b/server/forge/gitlab/convert.go index f93b3d6cd4..5ab015b63a 100644 --- a/server/forge/gitlab/convert.go +++ b/server/forge/gitlab/convert.go @@ -52,6 +52,7 @@ func (g *GitLab) convertGitLabRepo(_repo *gitlab.Project) (*model.Repo, error) { Push: isWrite(_repo), Admin: isAdmin(_repo), }, + PREnabled: _repo.MergeRequestsEnabled, } if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") { diff --git a/server/forge/mocks/forge.go b/server/forge/mocks/forge.go index 5f032c84a0..7237255b1e 100644 --- a/server/forge/mocks/forge.go +++ b/server/forge/mocks/forge.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.39.1. DO NOT EDIT. package mocks diff --git a/server/model/repo.go b/server/model/repo.go index 03f3dd7d33..34d50bb508 100644 --- a/server/model/repo.go +++ b/server/model/repo.go @@ -36,6 +36,7 @@ type Repo struct { CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'repo_clone_ssh'"` Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'repo_branch'"` SCMKind SCMKind `json:"scm,omitempty" xorm:"varchar(50) 'repo_scm'"` + PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'repo_pr_enabled'"` Timeout int64 `json:"timeout,omitempty" xorm:"repo_timeout"` Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'repo_visibility'"` IsSCMPrivate bool `json:"private" xorm:"repo_private"` @@ -66,7 +67,7 @@ func (r *Repo) ResetVisibility() { func ParseRepo(str string) (user, repo string, err error) { parts := strings.Split(str, "/") if len(parts) != 2 { - err = fmt.Errorf("Error: Invalid or missing repository. eg octocat/hello-world") + err = fmt.Errorf("error: Invalid or missing repository. eg octocat/hello-world") return } user = parts[0] @@ -85,6 +86,7 @@ func (r *Repo) Update(from *Repo) { r.Avatar = from.Avatar r.ForgeURL = from.ForgeURL r.SCMKind = from.SCMKind + r.PREnabled = from.PREnabled if len(from.Clone) > 0 { r.Clone = from.Clone } diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index 96dfa9193d..3022cb66c5 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.39.1. DO NOT EDIT. package mocks diff --git a/web/components.d.ts b/web/components.d.ts index 94980ba6a0..189d50f42c 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -62,14 +62,19 @@ declare module 'vue' { IMdiBitbucket: typeof import('~icons/mdi/bitbucket')['default'] IMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] IMdiClockTimeEightOutline: typeof import('~icons/mdi/clock-time-eight-outline')['default'] + IMdiCloseThick: typeof import('~icons/mdi/close-thick')['default'] IMdiErrorOutline: typeof import('~icons/mdi/error-outline')['default'] IMdiFormatListBulleted: typeof import('~icons/mdi/format-list-bulleted')['default'] IMdiGestureTap: typeof import('~icons/mdi/gesture-tap')['default'] IMdiGithub: typeof import('~icons/mdi/github')['default'] IMdiLoading: typeof import('~icons/mdi/loading')['default'] + IMdiPlay: typeof import('~icons/mdi/play')['default'] + IMdiRadioboxBlank: typeof import('~icons/mdi/radiobox-blank')['default'] + IMdiRadioboxIndeterminateVariant: typeof import('~icons/mdi/radiobox-indeterminate-variant')['default'] IMdiSourceBranch: typeof import('~icons/mdi/source-branch')['default'] IMdisourceCommit: typeof import('~icons/mdi/source-commit')['default'] IMdiSourcePull: typeof import('~icons/mdi/source-pull')['default'] + IMdiStop: typeof import('~icons/mdi/stop')['default'] IMdiSync: typeof import('~icons/mdi/sync')['default'] IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default'] InputField: typeof import('./src/components/form/InputField.vue')['default'] diff --git a/web/src/lib/api/types/repo.ts b/web/src/lib/api/types/repo.ts index ed6821cde4..1e1a75ee78 100644 --- a/web/src/lib/api/types/repo.ts +++ b/web/src/lib/api/types/repo.ts @@ -10,9 +10,12 @@ export type Repo = { forge_remote_id: string; // The source control management being used. - // Currently this is either 'git' or 'hg' (Mercurial). + // Currently, this is either 'git' or 'hg' (Mercurial). scm: string; + // Whether the forge repo has PRs enabled. + pr_enabled: boolean; + // The id of the organization that owns the repository. org_id: number; diff --git a/web/src/views/repo/RepoPullRequest.vue b/web/src/views/repo/RepoPullRequest.vue index 4ed27107fa..5aa014167a 100644 --- a/web/src/views/repo/RepoPullRequest.vue +++ b/web/src/views/repo/RepoPullRequest.vue @@ -20,6 +20,9 @@ const repoPermissions = inject>('repo-permissions'); if (!repo || !repoPermissions) { throw new Error('Unexpected: "repo" and "repoPermissions" should be provided at this place'); } +if (!repo.value.pr_enabled || !repo.value.allow_pr) { + throw new Error('Unexpected: pull requests are disabled for repo'); +} const allPipelines = inject>('pipelines'); const pipelines = computed( diff --git a/web/src/views/repo/RepoPullRequests.vue b/web/src/views/repo/RepoPullRequests.vue index 3a76cb9c15..14c9a5793b 100644 --- a/web/src/views/repo/RepoPullRequests.vue +++ b/web/src/views/repo/RepoPullRequests.vue @@ -29,6 +29,9 @@ const repo = inject>('repo'); if (!repo) { throw new Error('Unexpected: "repo" should be provided at this place'); } +if (!repo.value.pr_enabled || !repo.value.allow_pr) { + throw new Error('Unexpected: pull requests are disabled for repo'); +} async function loadPullRequests(page: number): Promise { if (!repo) { diff --git a/web/src/views/repo/RepoWrapper.vue b/web/src/views/repo/RepoWrapper.vue index a297092ae9..c392f9ac70 100644 --- a/web/src/views/repo/RepoWrapper.vue +++ b/web/src/views/repo/RepoWrapper.vue @@ -37,7 +37,7 @@ - +