alt
+ click/enter
t
issues.filter_label_no_select = All labels
issues.filter_milestone = Milestone
issues.filter_milestone_no_select = All milestones
+issues.filter_project = Project
+issues.filter_project_no_select = All projects
issues.filter_assignee = Assignee
issues.filter_assginee_no_select = All assignees
issues.filter_poster = Author
diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go
index 2e4309a2ebce6..f0457c55e19c3 100644
--- a/routers/api/packages/container/blob.go
+++ b/routers/api/packages/container/blob.go
@@ -26,14 +26,18 @@ var uploadVersionMutex sync.Mutex
// saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image
-func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_service.PackageInfo) (*packages_model.PackageBlob, error) {
+func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) {
+ if err := packages_service.CheckSizeQuotaExceeded(db.DefaultContext, pci.Creator, pci.Owner, packages_model.TypeContainer, hsr.Size()); err != nil {
+ return nil, err
+ }
+
pb := packages_service.NewPackageBlob(hsr)
exists := false
contentStore := packages_module.NewContentStore()
- uploadVersion, err := getOrCreateUploadVersion(pi)
+ uploadVersion, err := getOrCreateUploadVersion(&pci.PackageInfo)
if err != nil {
return nil, err
}
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 8b2c4e6bb2bc9..c22cfb500997b 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -227,8 +227,22 @@ func InitiateUploadBlob(ctx *context.Context) {
return
}
- if _, err := saveAsPackageBlob(buf, &packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}); err != nil {
- apiError(ctx, http.StatusInternalServerError, err)
+ if _, err := saveAsPackageBlob(
+ buf,
+ &packages_service.PackageCreationInfo{
+ PackageInfo: packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ Name: image,
+ },
+ Creator: ctx.Doer,
+ },
+ ); err != nil {
+ switch err {
+ case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
+ apiError(ctx, http.StatusForbidden, err)
+ default:
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
return
}
@@ -358,8 +372,22 @@ func EndUploadBlob(ctx *context.Context) {
return
}
- if _, err := saveAsPackageBlob(uploader, &packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}); err != nil {
- apiError(ctx, http.StatusInternalServerError, err)
+ if _, err := saveAsPackageBlob(
+ uploader,
+ &packages_service.PackageCreationInfo{
+ PackageInfo: packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ Name: image,
+ },
+ Creator: ctx.Doer,
+ },
+ ); err != nil {
+ switch err {
+ case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
+ apiError(ctx, http.StatusForbidden, err)
+ default:
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
return
}
@@ -526,7 +554,12 @@ func UploadManifest(ctx *context.Context) {
} else if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errBlobUnknown)
} else {
- apiError(ctx, http.StatusInternalServerError, err)
+ switch err {
+ case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
+ apiError(ctx, http.StatusForbidden, err)
+ default:
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
}
return
}
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index 350933f3d2f87..491fb70639435 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -327,6 +327,10 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
}
}
+ if err := packages_service.CheckCountQuotaExceeded(ctx, mci.Creator, mci.Owner); err != nil {
+ return nil, err
+ }
+
if mci.IsTagged {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
log.Error("Error setting package version property: %v", err)
diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go
new file mode 100644
index 0000000000000..2aed4139f3cb6
--- /dev/null
+++ b/routers/api/v1/admin/hooks.go
@@ -0,0 +1,174 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package admin
+
+import (
+ "net/http"
+
+ "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/utils"
+ webhook_service "code.gitea.io/gitea/services/webhook"
+)
+
+// ListHooks list system's webhooks
+func ListHooks(ctx *context.APIContext) {
+ // swagger:operation GET /admin/hooks admin adminListHooks
+ // ---
+ // summary: List system's webhooks
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/HookList"
+
+ sysHooks, err := webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err)
+ return
+ }
+ hooks := make([]*api.Hook, len(sysHooks))
+ for i, hook := range sysHooks {
+ h, err := webhook_service.ToHook(setting.AppURL+"/admin", hook)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ return
+ }
+ hooks[i] = h
+ }
+ ctx.JSON(http.StatusOK, hooks)
+}
+
+// GetHook get an organization's hook by id
+func GetHook(ctx *context.APIContext) {
+ // swagger:operation GET /admin/hooks/{id} admin adminGetHook
+ // ---
+ // summary: Get a hook
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: id
+ // in: path
+ // description: id of the hook to get
+ // type: integer
+ // format: int64
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/Hook"
+
+ hookID := ctx.ParamsInt64(":id")
+ hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ return
+ }
+ h, err := webhook_service.ToHook("/admin/", hook)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ return
+ }
+ ctx.JSON(http.StatusOK, h)
+}
+
+// CreateHook create a hook for an organization
+func CreateHook(ctx *context.APIContext) {
+ // swagger:operation POST /admin/hooks admin adminCreateHook
+ // ---
+ // summary: Create a hook
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: body
+ // in: body
+ // required: true
+ // schema:
+ // "$ref": "#/definitions/CreateHookOption"
+ // responses:
+ // "201":
+ // "$ref": "#/responses/Hook"
+
+ form := web.GetForm(ctx).(*api.CreateHookOption)
+ // TODO in body params
+ if !utils.CheckCreateHookOption(ctx, form) {
+ return
+ }
+ utils.AddSystemHook(ctx, form)
+}
+
+// EditHook modify a hook of a repository
+func EditHook(ctx *context.APIContext) {
+ // swagger:operation PATCH /admin/hooks/{id} admin adminEditHook
+ // ---
+ // summary: Update a hook
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: id
+ // in: path
+ // description: id of the hook to update
+ // type: integer
+ // format: int64
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/EditHookOption"
+ // responses:
+ // "200":
+ // "$ref": "#/responses/Hook"
+
+ form := web.GetForm(ctx).(*api.EditHookOption)
+
+ // TODO in body params
+ hookID := ctx.ParamsInt64(":id")
+ utils.EditSystemHook(ctx, form, hookID)
+}
+
+// DeleteHook delete a system hook
+func DeleteHook(ctx *context.APIContext) {
+ // swagger:operation DELETE /amdin/hooks/{id} admin adminDeleteHook
+ // ---
+ // summary: Delete a hook
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: id
+ // in: path
+ // description: id of the hook to delete
+ // type: integer
+ // format: int64
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+
+ hookID := ctx.ParamsInt64(":id")
+ if err := webhook.DeleteDefaultSystemWebhook(ctx, hookID); err != nil {
+ if webhook.IsErrWebhookNotExist(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "DeleteDefaultSystemWebhook", err)
+ }
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 21bc2e2de4d6d..eef2a64244083 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1222,6 +1222,13 @@ func Routes(ctx gocontext.Context) *web.Route {
m.Post("/{username}/{reponame}", admin.AdoptRepository)
m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository)
})
+ m.Group("/hooks", func() {
+ m.Combo("").Get(admin.ListHooks).
+ Post(bind(api.CreateHookOption{}), admin.CreateHook)
+ m.Combo("/{id}").Get(admin.GetHook).
+ Patch(bind(api.EditHookOption{}), admin.EditHook).
+ Delete(admin.DeleteHook)
+ })
}, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin())
m.Group("/topics", func() {
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index 44fba22b5afed..f6aaf74aff123 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
@@ -67,6 +68,19 @@ func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
return true
}
+// AddSystemHook add a system hook
+func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) {
+ hook, ok := addHook(ctx, form, 0, 0)
+ if ok {
+ h, err := webhook_service.ToHook(setting.AppSubURL+"/admin", hook)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ return
+ }
+ ctx.JSON(http.StatusCreated, h)
+ }
+}
+
// AddOrgHook add a hook to an organization. Writes to `ctx` accordingly
func AddOrgHook(ctx *context.APIContext, form *api.CreateHookOption) {
org := ctx.Org.Organization
@@ -196,6 +210,30 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
return w, true
}
+// EditSystemHook edit system webhook `w` according to `form`. Writes to `ctx` accordingly
+func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
+ hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ return
+ }
+ if !editHook(ctx, form, hook) {
+ ctx.Error(http.StatusInternalServerError, "editHook", err)
+ return
+ }
+ updated, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ return
+ }
+ h, err := webhook_service.ToHook(setting.AppURL+"/admin", updated)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ return
+ }
+ ctx.JSON(http.StatusOK, h)
+}
+
// EditOrgHook edit webhook `w` according to `form`. Writes to `ctx` accordingly
func EditOrgHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
org := ctx.Org.Organization
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index c62038899d71a..75de47bdc4b26 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -173,13 +173,6 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
return
}
- if !repo.AllowsPulls() {
- // We can stop there's no need to go any further
- ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
- RepoWasEmpty: wasEmpty,
- })
- return
- }
baseRepo = repo
if repo.IsFork {
@@ -191,7 +184,17 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
})
return
}
- baseRepo = repo.BaseRepo
+ if repo.BaseRepo.AllowsPulls() {
+ baseRepo = repo.BaseRepo
+ }
+ }
+
+ if !baseRepo.AllowsPulls() {
+ // We can stop there's no need to go any further
+ ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
+ RepoWasEmpty: wasEmpty,
+ })
+ return
}
}
@@ -217,14 +220,14 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
}
results = append(results, private.HookPostReceiveBranchResult{
- Message: setting.Git.PullRequestPushMessage && repo.AllowsPulls(),
+ Message: setting.Git.PullRequestPushMessage && baseRepo.AllowsPulls(),
Create: true,
Branch: branch,
URL: fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
})
} else {
results = append(results, private.HookPostReceiveBranchResult{
- Message: setting.Git.PullRequestPushMessage && repo.AllowsPulls(),
+ Message: setting.Git.PullRequestPushMessage && baseRepo.AllowsPulls(),
Create: false,
Branch: branch,
URL: fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go
index e8db9a3ded70a..57cf5f49e5b60 100644
--- a/routers/web/admin/hooks.go
+++ b/routers/web/admin/hooks.go
@@ -60,7 +60,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
// DeleteDefaultOrSystemWebhook handler to delete an admin-defined system or default webhook
func DeleteDefaultOrSystemWebhook(ctx *context.Context) {
- if err := webhook.DeleteDefaultSystemWebhook(ctx.FormInt64("id")); err != nil {
+ if err := webhook.DeleteDefaultSystemWebhook(ctx, ctx.FormInt64("id")); err != nil {
ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error())
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index edd9821ac783f..ed7461bbc669c 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -203,6 +203,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
RepoID: repo.ID,
Labels: selectLabels,
MilestoneID: milestoneID,
+ ProjectID: projectID,
AssigneeID: assigneeID,
MentionedID: mentionedID,
PosterID: posterID,
@@ -362,18 +363,16 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
return 0
}
- if ctx.Repo.CanWriteIssuesOrPulls(ctx.Params(":type") == "pulls") {
- projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{
- RepoID: repo.ID,
- Type: project_model.TypeRepository,
- IsClosed: util.OptionalBoolOf(isShowClosed),
- })
- if err != nil {
- ctx.ServerError("GetProjects", err)
- return
- }
- ctx.Data["Projects"] = projects
+ projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{
+ RepoID: repo.ID,
+ Type: project_model.TypeRepository,
+ IsClosed: util.OptionalBoolOf(isShowClosed),
+ })
+ if err != nil {
+ ctx.ServerError("FindProjects", err)
+ return
}
+ ctx.Data["Projects"] = projects
ctx.Data["IssueStats"] = issueStats
ctx.Data["SelLabelIDs"] = labelIDs
@@ -381,6 +380,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
ctx.Data["ViewType"] = viewType
ctx.Data["SortType"] = sortType
ctx.Data["MilestoneID"] = milestoneID
+ ctx.Data["ProjectID"] = projectID
ctx.Data["AssigneeID"] = assigneeID
ctx.Data["PosterID"] = posterID
ctx.Data["IsShowClosed"] = isShowClosed
@@ -397,6 +397,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
pager.AddParam(ctx, "state", "State")
pager.AddParam(ctx, "labels", "SelectLabels")
pager.AddParam(ctx, "milestone", "MilestoneID")
+ pager.AddParam(ctx, "project", "ProjectID")
pager.AddParam(ctx, "assignee", "AssigneeID")
pager.AddParam(ctx, "poster", "PosterID")
ctx.Data["Page"] = pager
@@ -2352,6 +2353,8 @@ func SearchIssues(ctx *context.Context) {
includedMilestones = strings.Split(milestones, ",")
}
+ projectID := ctx.FormInt64("project")
+
// this api is also used in UI,
// so the default limit is set to fit UI needs
limit := ctx.FormInt("limit")
@@ -2374,6 +2377,7 @@ func SearchIssues(ctx *context.Context) {
IssueIDs: issueIDs,
IncludedLabelNames: includedLabelNames,
IncludeMilestones: includedMilestones,
+ ProjectID: projectID,
SortType: "priorityrepo",
PriorityRepoID: ctx.FormInt64("priority_repo_id"),
IsPull: isPull,
@@ -2511,6 +2515,8 @@ func ListIssues(ctx *context.Context) {
}
}
+ projectID := ctx.FormInt64("project")
+
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
@@ -2550,6 +2556,7 @@ func ListIssues(ctx *context.Context) {
IssueIDs: issueIDs,
LabelIDs: labelIDs,
MilestoneIDs: mileIDs,
+ ProjectID: projectID,
IsPull: isPull,
UpdatedBeforeUnix: before,
UpdatedAfterUnix: since,
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 96261af674461..d3826c3f3de31 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -591,7 +591,7 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
} else if orCtx.OrgID > 0 {
w, err = webhook.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
} else if orCtx.IsAdmin {
- w, err = webhook.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id"))
+ w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id"))
}
if err != nil || w == nil {
if webhook.IsErrWebhookNotExist(err) {
diff --git a/services/packages/packages.go b/services/packages/packages.go
index 410e73c048516..754dfa7110235 100644
--- a/services/packages/packages.go
+++ b/services/packages/packages.go
@@ -173,7 +173,7 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
if versionCreated {
- if err := checkCountQuotaExceeded(ctx, pvci.Creator, pvci.Owner); err != nil {
+ if err := CheckCountQuotaExceeded(ctx, pvci.Creator, pvci.Owner); err != nil {
return nil, false, err
}
@@ -240,7 +240,7 @@ func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.Packag
func addFileToPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pvi *PackageInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
log.Trace("Adding package file: %v, %s", pv.ID, pfci.Filename)
- if err := checkSizeQuotaExceeded(ctx, pfci.Creator, pvi.Owner, pvi.PackageType, pfci.Data.Size()); err != nil {
+ if err := CheckSizeQuotaExceeded(ctx, pfci.Creator, pvi.Owner, pvi.PackageType, pfci.Data.Size()); err != nil {
return nil, nil, false, err
}
@@ -302,7 +302,9 @@ func addFileToPackageVersion(ctx context.Context, pv *packages_model.PackageVers
return pf, pb, !exists, nil
}
-func checkCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User) error {
+// CheckCountQuotaExceeded checks if the owner has more than the allowed packages
+// The check is skipped if the doer is an admin.
+func CheckCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User) error {
if doer.IsAdmin {
return nil
}
@@ -324,7 +326,9 @@ func checkCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User)
return nil
}
-func checkSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, packageType packages_model.Type, uploadSize int64) error {
+// CheckSizeQuotaExceeded checks if the upload size is bigger than the allowed size
+// The check is skipped if the doer is an admin.
+func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, packageType packages_model.Type, uploadSize int64) error {
if doer.IsAdmin {
return nil
}
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 7f81def6d66cc..c983c4f3e78ec 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -4,14 +4,12 @@
package pull
import (
- "bufio"
- "bytes"
"context"
"fmt"
"io"
+ "os"
"regexp"
"strings"
- "time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
@@ -29,6 +27,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
+ "code.gitea.io/gitea/modules/util"
issue_service "code.gitea.io/gitea/services/issue"
)
@@ -351,69 +350,56 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
// checkIfPRContentChanged checks if diff to target branch has changed by push
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
- if err = pr.LoadHeadRepo(ctx); err != nil {
- return false, fmt.Errorf("LoadHeadRepo: %w", err)
- } else if pr.HeadRepo == nil {
- // corrupt data assumed changed
- return true, nil
- }
-
- if err = pr.LoadBaseRepo(ctx); err != nil {
- return false, fmt.Errorf("LoadBaseRepo: %w", err)
- }
-
- headGitRepo, err := git.OpenRepository(ctx, pr.HeadRepo.RepoPath())
+ tmpBasePath, err := createTemporaryRepo(ctx, pr)
if err != nil {
- return false, fmt.Errorf("OpenRepository: %w", err)
- }
- defer headGitRepo.Close()
-
- // Add a temporary remote.
- tmpRemote := "checkIfPRContentChanged-" + fmt.Sprint(time.Now().UnixNano())
- if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath(), true); err != nil {
- return false, fmt.Errorf("AddRemote: %s/%s-%s: %w", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err)
+ log.Error("CreateTemporaryRepo: %v", err)
+ return false, err
}
defer func() {
- if err := headGitRepo.RemoveRemote(tmpRemote); err != nil {
- log.Error("checkIfPRContentChanged: RemoveRemote: %s/%s-%s: %v", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err)
+ if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil {
+ log.Error("checkIfPRContentChanged: RemoveTemporaryPath: %s", err)
}
}()
- // To synchronize repo and get a base ref
- _, base, err := headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch)
+
+ tmpRepo, err := git.OpenRepository(ctx, tmpBasePath)
if err != nil {
- return false, fmt.Errorf("GetMergeBase: %w", err)
+ return false, fmt.Errorf("OpenRepository: %w", err)
}
+ defer tmpRepo.Close()
- diffBefore := &bytes.Buffer{}
- diffAfter := &bytes.Buffer{}
- if err := headGitRepo.GetDiffFromMergeBase(base, oldCommitID, diffBefore); err != nil {
- // If old commit not found, assume changed.
- log.Debug("GetDiffFromMergeBase: %v", err)
- return true, nil
- }
- if err := headGitRepo.GetDiffFromMergeBase(base, newCommitID, diffAfter); err != nil {
- // New commit should be found
- return false, fmt.Errorf("GetDiffFromMergeBase: %w", err)
+ // Find the merge-base
+ _, base, err := tmpRepo.GetMergeBase("", "base", "tracking")
+ if err != nil {
+ return false, fmt.Errorf("GetMergeBase: %w", err)
}
- diffBeforeLines := bufio.NewScanner(diffBefore)
- diffAfterLines := bufio.NewScanner(diffAfter)
-
- for diffBeforeLines.Scan() && diffAfterLines.Scan() {
- if strings.HasPrefix(diffBeforeLines.Text(), "index") && strings.HasPrefix(diffAfterLines.Text(), "index") {
- // file hashes can change without the diff changing
- continue
- } else if strings.HasPrefix(diffBeforeLines.Text(), "@@") && strings.HasPrefix(diffAfterLines.Text(), "@@") {
- // the location of the difference may change
- continue
- } else if !bytes.Equal(diffBeforeLines.Bytes(), diffAfterLines.Bytes()) {
+ cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
+ stdoutReader, stdoutWriter, err := os.Pipe()
+ if err != nil {
+ return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
+ }
+
+ if err := cmd.Run(&git.RunOpts{
+ Dir: tmpBasePath,
+ Stdout: stdoutWriter,
+ PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
+ _ = stdoutWriter.Close()
+ defer func() {
+ _ = stdoutReader.Close()
+ }()
+ return util.IsEmptyReader(stdoutReader)
+ },
+ }); err != nil {
+ if err == util.ErrNotEmpty {
return true, nil
}
- }
- if diffBeforeLines.Scan() || diffAfterLines.Scan() {
- // Diffs not of equal length
- return true, nil
+ log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v",
+ newCommitID, oldCommitID, base,
+ pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch,
+ err)
+
+ return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
}
return false, nil
diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl
index 89be609225895..c3b96a0245a9e 100644
--- a/templates/base/footer_content.tmpl
+++ b/templates/base/footer_content.tmpl
@@ -1,6 +1,6 @@
-