. For example, refs/heads/feature-branch-1.
- "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
- "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
- "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
- "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
- "repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
- "repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
- "repositoryUrl": t.Job.Run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git.
- "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept.
- "run_id": fmt.Sprint(t.Job.RunID), // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run.
- "run_number": fmt.Sprint(t.Job.Run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
- "run_attempt": fmt.Sprint(t.Job.Attempt), // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.
- "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces.
- "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com.
- "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53.
- "token": t.Token, // string, A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the GITHUB_TOKEN secret. For more information, see "Automatic token authentication."
- "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
- "workflow": t.Job.Run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
- "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action.
-
- // additional contexts
- "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(),
- "gitea_runtime_token": giteaRuntimeToken,
- })
- if err != nil {
- log.Error("structpb.NewStruct failed: %v", err)
- }
-
- return taskContext
-}
-
-func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[string]*runnerv1.TaskNeed, error) {
- if err := task.LoadAttributes(ctx); err != nil {
- return nil, fmt.Errorf("LoadAttributes: %w", err)
- }
- if len(task.Job.Needs) == 0 {
- return nil, nil
- }
- needs := container.SetOf(task.Job.Needs...)
-
- jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID})
- if err != nil {
- return nil, fmt.Errorf("FindRunJobs: %w", err)
- }
-
- jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
- for _, job := range jobs {
- jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
- }
-
- ret := make(map[string]*runnerv1.TaskNeed, len(needs))
- for jobID, jobsWithSameID := range jobIDJobs {
- if !needs.Contains(jobID) {
- continue
- }
- var jobOutputs map[string]string
- for _, job := range jobsWithSameID {
- if job.TaskID == 0 || !job.Status.IsDone() {
- // it shouldn't happen, or the job has been rerun
- continue
- }
- got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
- if err != nil {
- return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
- }
- outputs := make(map[string]string, len(got))
- for _, v := range got {
- outputs[v.OutputKey] = v.OutputValue
- }
- if len(jobOutputs) == 0 {
- jobOutputs = outputs
- } else {
- jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
- }
- }
- ret[jobID] = &runnerv1.TaskNeed{
- Outputs: jobOutputs,
- Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)),
- }
- }
-
- return ret, nil
-}
-
-// mergeTwoOutputs merges two outputs from two different ActionRunJobs
-// Values with the same output name may be overridden. The user should ensure the output names are unique.
-// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
-func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
- ret := make(map[string]string, len(o1))
- for k1, v1 := range o1 {
- if len(v1) > 0 {
- ret[k1] = v1
- } else {
- ret[k1] = o2[k1]
- }
- }
- return ret
-}
diff --git a/routers/api/forgejo/v1/api.go b/routers/api/forgejo/v1/api.go
index 88c7502e66..dfc5a29d05 100644
--- a/routers/api/forgejo/v1/api.go
+++ b/routers/api/forgejo/v1/api.go
@@ -4,8 +4,8 @@
package v1
import (
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/shared"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/shared"
)
func Routes() *web.Route {
diff --git a/routers/api/forgejo/v1/forgejo.go b/routers/api/forgejo/v1/forgejo.go
index 0f1f4f1932..b63db6fb9a 100644
--- a/routers/api/forgejo/v1/forgejo.go
+++ b/routers/api/forgejo/v1/forgejo.go
@@ -5,8 +5,8 @@ package v1
import (
"net/http"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
)
type Forgejo struct{}
diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go
index 831a910e36..dc992ebb5a 100644
--- a/routers/api/packages/alpine/alpine.go
+++ b/routers/api/packages/alpine/alpine.go
@@ -13,16 +13,16 @@ import (
"net/http"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- alpine_model "code.gitea.io/gitea/models/packages/alpine"
- "code.gitea.io/gitea/modules/json"
- packages_module "code.gitea.io/gitea/modules/packages"
- alpine_module "code.gitea.io/gitea/modules/packages/alpine"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
- alpine_service "code.gitea.io/gitea/services/packages/alpine"
+ packages_model "forgejo.org/models/packages"
+ alpine_model "forgejo.org/models/packages/alpine"
+ "forgejo.org/modules/json"
+ packages_module "forgejo.org/modules/packages"
+ alpine_module "forgejo.org/modules/packages/alpine"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
+ alpine_service "forgejo.org/services/packages/alpine"
)
func apiError(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/alt/alt.go b/routers/api/packages/alt/alt.go
new file mode 100644
index 0000000000..a118459ce3
--- /dev/null
+++ b/routers/api/packages/alt/alt.go
@@ -0,0 +1,260 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package alt
+
+import (
+ stdctx "context"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ packages_module "forgejo.org/modules/packages"
+ rpm_module "forgejo.org/modules/packages/rpm"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ notify_service "forgejo.org/services/notify"
+ packages_service "forgejo.org/services/packages"
+ alt_service "forgejo.org/services/packages/alt"
+)
+
+func apiError(ctx *context.Context, status int, obj any) {
+ helper.LogAndProcessError(ctx, status, obj, func(message string) {
+ ctx.PlainText(status, message)
+ })
+}
+
+func GetRepositoryConfig(ctx *context.Context) {
+ group := ctx.Params("group")
+
+ var groupParts []string
+ if group != "" {
+ groupParts = strings.Split(group, "/")
+ }
+
+ url := fmt.Sprintf("%sapi/packages/%s/alt", setting.AppURL, ctx.Package.Owner.Name)
+
+ ctx.PlainText(http.StatusOK, `[gitea-`+strings.Join(append([]string{ctx.Package.Owner.LowerName}, groupParts...), "-")+`]
+name=`+strings.Join(append([]string{ctx.Package.Owner.Name, setting.AppName}, groupParts...), " - ")+`
+baseurl=`+strings.Join(append([]string{url}, groupParts...), "/")+`
+enabled=1`)
+}
+
+// Gets a pre-generated repository metadata file
+func GetRepositoryFile(ctx *context.Context, arch string) {
+ pv, err := alt_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ ctx,
+ pv,
+ &packages_service.PackageFileInfo{
+ Filename: ctx.Params("filename"),
+ CompositeKey: arch + "__" + ctx.Params("group"),
+ },
+ )
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ apiError(ctx, http.StatusNotFound, err)
+ } else {
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ helper.ServePackageFile(ctx, s, u, pf)
+}
+
+func UploadPackageFile(ctx *context.Context) {
+ upload, needToClose, err := ctx.UploadStream()
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ if needToClose {
+ defer upload.Close()
+ }
+
+ buf, err := packages_module.CreateHashedBufferFromReader(upload)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ defer buf.Close()
+
+ pck, err := rpm_module.ParsePackage(buf, "alt")
+ if err != nil {
+ if errors.Is(err, util.ErrInvalidArgument) {
+ apiError(ctx, http.StatusBadRequest, err)
+ } else {
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+ if _, err := buf.Seek(0, io.SeekStart); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ fileMetadataRaw, err := json.Marshal(pck.FileMetadata)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ group := ctx.Params("group")
+ _, _, err = packages_service.CreatePackageOrAddFileToExisting(
+ ctx,
+ &packages_service.PackageCreationInfo{
+ PackageInfo: packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypeAlt,
+ Name: pck.Name,
+ Version: pck.Version,
+ },
+ Creator: ctx.Doer,
+ Metadata: pck.VersionMetadata,
+ },
+ &packages_service.PackageFileCreationInfo{
+ PackageFileInfo: packages_service.PackageFileInfo{
+ Filename: fmt.Sprintf("%s-%s.%s.rpm", pck.Name, pck.Version, pck.FileMetadata.Architecture),
+ CompositeKey: group,
+ },
+ Creator: ctx.Doer,
+ Data: buf,
+ IsLead: true,
+ Properties: map[string]string{
+ rpm_module.PropertyGroup: group,
+ rpm_module.PropertyArchitecture: pck.FileMetadata.Architecture,
+ rpm_module.PropertyMetadata: string(fileMetadataRaw),
+ },
+ },
+ )
+ if err != nil {
+ switch err {
+ case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile:
+ apiError(ctx, http.StatusConflict, err)
+ case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
+ apiError(ctx, http.StatusForbidden, err)
+ default:
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ if err := alt_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ ctx.Status(http.StatusCreated)
+}
+
+func DownloadPackageFile(ctx *context.Context) {
+ name := ctx.Params("name")
+ version := ctx.Params("version")
+
+ s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ ctx,
+ &packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypeAlt,
+ Name: name,
+ Version: version,
+ },
+ &packages_service.PackageFileInfo{
+ Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")),
+ CompositeKey: ctx.Params("group"),
+ },
+ )
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ apiError(ctx, http.StatusNotFound, err)
+ } else {
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ helper.ServePackageFile(ctx, s, u, pf)
+}
+
+func DeletePackageFile(webctx *context.Context) {
+ group := webctx.Params("group")
+ name := webctx.Params("name")
+ version := webctx.Params("version")
+ architecture := webctx.Params("architecture")
+
+ var pd *packages_model.PackageDescriptor
+
+ err := db.WithTx(webctx, func(ctx stdctx.Context) error {
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx,
+ webctx.Package.Owner.ID,
+ packages_model.TypeAlt,
+ name,
+ version,
+ )
+ if err != nil {
+ return err
+ }
+
+ pf, err := packages_model.GetFileForVersionByName(
+ ctx,
+ pv.ID,
+ fmt.Sprintf("%s-%s.%s.rpm", name, version, architecture),
+ group,
+ )
+ if err != nil {
+ return err
+ }
+
+ if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
+ return err
+ }
+
+ has, err := packages_model.HasVersionFileReferences(ctx, pv.ID)
+ if err != nil {
+ return err
+ }
+ if !has {
+ pd, err = packages_model.GetPackageDescriptor(ctx, pv)
+ if err != nil {
+ return err
+ }
+
+ if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ })
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ apiError(webctx, http.StatusNotFound, err)
+ } else {
+ apiError(webctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ if pd != nil {
+ notify_service.PackageDelete(webctx, webctx.Doer, pd)
+ }
+
+ if err := alt_service.BuildSpecificRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil {
+ apiError(webctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ webctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index e216a0c02b..79e61cf352 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -8,36 +8,37 @@ import (
"regexp"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/perm"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/packages/alpine"
- "code.gitea.io/gitea/routers/api/packages/arch"
- "code.gitea.io/gitea/routers/api/packages/cargo"
- "code.gitea.io/gitea/routers/api/packages/chef"
- "code.gitea.io/gitea/routers/api/packages/composer"
- "code.gitea.io/gitea/routers/api/packages/conan"
- "code.gitea.io/gitea/routers/api/packages/conda"
- "code.gitea.io/gitea/routers/api/packages/container"
- "code.gitea.io/gitea/routers/api/packages/cran"
- "code.gitea.io/gitea/routers/api/packages/debian"
- "code.gitea.io/gitea/routers/api/packages/generic"
- "code.gitea.io/gitea/routers/api/packages/goproxy"
- "code.gitea.io/gitea/routers/api/packages/helm"
- "code.gitea.io/gitea/routers/api/packages/maven"
- "code.gitea.io/gitea/routers/api/packages/npm"
- "code.gitea.io/gitea/routers/api/packages/nuget"
- "code.gitea.io/gitea/routers/api/packages/pub"
- "code.gitea.io/gitea/routers/api/packages/pypi"
- "code.gitea.io/gitea/routers/api/packages/rpm"
- "code.gitea.io/gitea/routers/api/packages/rubygems"
- "code.gitea.io/gitea/routers/api/packages/swift"
- "code.gitea.io/gitea/routers/api/packages/vagrant"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/perm"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/packages/alpine"
+ "forgejo.org/routers/api/packages/alt"
+ "forgejo.org/routers/api/packages/arch"
+ "forgejo.org/routers/api/packages/cargo"
+ "forgejo.org/routers/api/packages/chef"
+ "forgejo.org/routers/api/packages/composer"
+ "forgejo.org/routers/api/packages/conan"
+ "forgejo.org/routers/api/packages/conda"
+ "forgejo.org/routers/api/packages/container"
+ "forgejo.org/routers/api/packages/cran"
+ "forgejo.org/routers/api/packages/debian"
+ "forgejo.org/routers/api/packages/generic"
+ "forgejo.org/routers/api/packages/goproxy"
+ "forgejo.org/routers/api/packages/helm"
+ "forgejo.org/routers/api/packages/maven"
+ "forgejo.org/routers/api/packages/npm"
+ "forgejo.org/routers/api/packages/nuget"
+ "forgejo.org/routers/api/packages/pub"
+ "forgejo.org/routers/api/packages/pypi"
+ "forgejo.org/routers/api/packages/rpm"
+ "forgejo.org/routers/api/packages/rubygems"
+ "forgejo.org/routers/api/packages/swift"
+ "forgejo.org/routers/api/packages/vagrant"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/context"
)
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
@@ -47,13 +48,14 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
if ok { // it's a personal access token but not oauth2 token
scopeMatched := false
var err error
- if accessMode == perm.AccessModeRead {
+ switch accessMode {
+ case perm.AccessModeRead:
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeReadPackage)
if err != nil {
ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
return
}
- } else if accessMode == perm.AccessModeWrite {
+ case perm.AccessModeWrite:
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeWritePackage)
if err != nil {
ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
@@ -624,6 +626,73 @@ func CommonRoutes() *web.Route {
ctx.Status(http.StatusNotFound)
})
}, reqPackageAccess(perm.AccessModeRead))
+ r.Group("/alt", func() {
+ var (
+ baseURLPattern = regexp.MustCompile(`\A(.*?)\.repo\z`)
+ uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`)
+ baseRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\/base/(\S+)`)
+ rpmsRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_-]+)\.(\S+)\.rpm`)
+ )
+
+ r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
+ path := ctx.Params("*")
+ isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
+ isPut := ctx.Req.Method == "PUT"
+ isDelete := ctx.Req.Method == "DELETE"
+
+ m := baseURLPattern.FindStringSubmatch(path)
+ if len(m) == 2 && isGetHead {
+ ctx.SetParams("group", strings.Trim(m[1], "/"))
+ alt.GetRepositoryConfig(ctx)
+ return
+ }
+
+ m = baseRepoPattern.FindStringSubmatch(path)
+ if len(m) == 4 {
+ if strings.Trim(m[1], "/") != "alt" {
+ ctx.SetParams("group", strings.Trim(m[1], "/"))
+ }
+ ctx.SetParams("filename", m[3])
+ if isGetHead {
+ alt.GetRepositoryFile(ctx, m[2])
+ }
+ return
+ }
+
+ m = uploadPattern.FindStringSubmatch(path)
+ if len(m) == 2 && isPut {
+ reqPackageAccess(perm.AccessModeWrite)(ctx)
+ if ctx.Written() {
+ return
+ }
+ ctx.SetParams("group", strings.Trim(m[1], "/"))
+ alt.UploadPackageFile(ctx)
+ return
+ }
+
+ m = rpmsRepoPattern.FindStringSubmatch(path)
+ if len(m) == 7 && (isGetHead || isDelete) {
+ if strings.Trim(m[1], "/") != "alt" {
+ ctx.SetParams("group", strings.Trim(m[1], "/"))
+ }
+ ctx.SetParams("name", m[4])
+ ctx.SetParams("version", m[5])
+ ctx.SetParams("architecture", m[6])
+ if isGetHead {
+ alt.DownloadPackageFile(ctx)
+ } else {
+ reqPackageAccess(perm.AccessModeWrite)(ctx)
+ if ctx.Written() {
+ return
+ }
+ alt.DeletePackageFile(ctx)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNotFound)
+ })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
diff --git a/routers/api/packages/arch/arch.go b/routers/api/packages/arch/arch.go
index eedce5cdbc..a45f38dd08 100644
--- a/routers/api/packages/arch/arch.go
+++ b/routers/api/packages/arch/arch.go
@@ -13,15 +13,15 @@ import (
"regexp"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- packages_module "code.gitea.io/gitea/modules/packages"
- arch_module "code.gitea.io/gitea/modules/packages/arch"
- "code.gitea.io/gitea/modules/sync"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
- arch_service "code.gitea.io/gitea/services/packages/arch"
+ packages_model "forgejo.org/models/packages"
+ packages_module "forgejo.org/modules/packages"
+ arch_module "forgejo.org/modules/packages/arch"
+ "forgejo.org/modules/sync"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
+ arch_service "forgejo.org/services/packages/arch"
)
var (
diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go
index 140e532efd..50dc8d1c3d 100644
--- a/routers/api/packages/cargo/cargo.go
+++ b/routers/api/packages/cargo/cargo.go
@@ -10,20 +10,20 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- cargo_module "code.gitea.io/gitea/modules/packages/cargo"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- packages_service "code.gitea.io/gitea/services/packages"
- cargo_service "code.gitea.io/gitea/services/packages/cargo"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ cargo_module "forgejo.org/modules/packages/cargo"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ packages_service "forgejo.org/services/packages"
+ cargo_service "forgejo.org/services/packages/cargo"
)
// https://doc.rust-lang.org/cargo/reference/registries.html#web-api
diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go
index a790e9a363..7263cf13bb 100644
--- a/routers/api/packages/chef/auth.go
+++ b/routers/api/packages/chef/auth.go
@@ -12,6 +12,7 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
+ "errors"
"fmt"
"hash"
"math/big"
@@ -23,10 +24,10 @@ import (
"strings"
"time"
- user_model "code.gitea.io/gitea/models/user"
- chef_module "code.gitea.io/gitea/modules/packages/chef"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/auth"
+ user_model "forgejo.org/models/user"
+ chef_module "forgejo.org/modules/packages/chef"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/auth"
)
const (
@@ -121,7 +122,7 @@ func verifyTimestamp(req *http.Request) error {
}
if diff > maxTimeDifference {
- return fmt.Errorf("time difference")
+ return errors.New("time difference")
}
return nil
@@ -147,7 +148,7 @@ func getSignVersion(req *http.Request) (string, error) {
version := m[1]
m = algorithmPattern.FindStringSubmatch(hdr)
- if len(m) == 2 && m[1] != "sha1" && !(m[1] == "sha256" && version == "1.3") {
+ if len(m) == 2 && m[1] != "sha1" && (m[1] != "sha256" || version != "1.3") {
return "", util.NewInvalidArgumentErrorf("unsupported algorithm")
}
@@ -190,7 +191,7 @@ func getAuthorizationData(req *http.Request) ([]byte, error) {
tmp := make([]string, len(valueList))
for k, v := range valueList {
if k > len(tmp) {
- return nil, fmt.Errorf("invalid X-Ops-Authorization headers")
+ return nil, errors.New("invalid X-Ops-Authorization headers")
}
tmp[k-1] = v
}
@@ -267,7 +268,7 @@ func verifyDataOld(signature, data []byte, pub *rsa.PublicKey) error {
}
if !slices.Equal(out[skip:], data) {
- return fmt.Errorf("could not verify signature")
+ return errors.New("could not verify signature")
}
return nil
diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go
index b49f4e9d0a..13419b9a95 100644
--- a/routers/api/packages/chef/chef.go
+++ b/routers/api/packages/chef/chef.go
@@ -13,16 +13,16 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- chef_module "code.gitea.io/gitea/modules/packages/chef"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ chef_module "forgejo.org/modules/packages/chef"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
func apiError(ctx *context.Context, status int, obj any) {
@@ -139,7 +139,7 @@ func EnumeratePackages(ctx *context.Context) {
})
}
- skip, _ := opts.Paginator.GetSkipTake()
+ skip, _ := opts.GetSkipTake()
ctx.JSON(http.StatusOK, &Result{
Start: skip,
diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go
index a3bcf80417..dc491ea8a8 100644
--- a/routers/api/packages/composer/api.go
+++ b/routers/api/packages/composer/api.go
@@ -8,8 +8,8 @@ import (
"net/url"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- composer_module "code.gitea.io/gitea/modules/packages/composer"
+ packages_model "forgejo.org/models/packages"
+ composer_module "forgejo.org/modules/packages/composer"
)
// ServiceIndexResponse contains registry endpoints
@@ -66,6 +66,7 @@ type PackageMetadataResponse struct {
}
// PackageVersionMetadata contains package metadata
+// https://getcomposer.org/doc/05-repositories.md#package
type PackageVersionMetadata struct {
*composer_module.Metadata
Name string `json:"name"`
@@ -73,6 +74,7 @@ type PackageVersionMetadata struct {
Type string `json:"type"`
Created time.Time `json:"time"`
Dist Dist `json:"dist"`
+ Source Source `json:"source"`
}
// Dist contains package download information
@@ -82,6 +84,13 @@ type Dist struct {
Checksum string `json:"shasum"`
}
+// Source contains package source information
+type Source struct {
+ URL string `json:"url"`
+ Type string `json:"type"`
+ Reference string `json:"reference"`
+}
+
func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse {
versions := make([]*PackageVersionMetadata, 0, len(pds))
@@ -94,7 +103,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
}
}
- versions = append(versions, &PackageVersionMetadata{
+ pkg := PackageVersionMetadata{
Name: pd.Package.Name,
Version: pd.Version.Version,
Type: packageType,
@@ -105,7 +114,16 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
URL: fmt.Sprintf("%s/files/%s/%s/%s", registryURL, url.PathEscape(pd.Package.LowerName), url.PathEscape(pd.Version.LowerVersion), url.PathEscape(pd.Files[0].File.LowerName)),
Checksum: pd.Files[0].Blob.HashSHA1,
},
- })
+ }
+ if pd.Repository != nil {
+ pkg.Source = Source{
+ URL: pd.Repository.HTMLURL(),
+ Type: "git",
+ Reference: pd.Version.Version,
+ }
+ }
+
+ versions = append(versions, &pkg)
}
return &PackageMetadataResponse{
diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go
index a045da40de..9e67d419ec 100644
--- a/routers/api/packages/composer/composer.go
+++ b/routers/api/packages/composer/composer.go
@@ -12,17 +12,17 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- composer_module "code.gitea.io/gitea/modules/packages/composer"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ composer_module "forgejo.org/modules/packages/composer"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ packages_service "forgejo.org/services/packages"
"github.com/hashicorp/go-version"
)
diff --git a/routers/api/packages/conan/auth.go b/routers/api/packages/conan/auth.go
index e2e1901b08..1f5af77304 100644
--- a/routers/api/packages/conan/auth.go
+++ b/routers/api/packages/conan/auth.go
@@ -6,10 +6,10 @@ package conan
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/packages"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/packages"
)
var _ auth.Method = &Auth{}
diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go
index e07907a8b1..927d131309 100644
--- a/routers/api/packages/conan/conan.go
+++ b/routers/api/packages/conan/conan.go
@@ -11,20 +11,20 @@ import (
"strings"
"time"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- conan_model "code.gitea.io/gitea/models/packages/conan"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- conan_module "code.gitea.io/gitea/modules/packages/conan"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- notify_service "code.gitea.io/gitea/services/notify"
- packages_service "code.gitea.io/gitea/services/packages"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ conan_model "forgejo.org/models/packages/conan"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ conan_module "forgejo.org/modules/packages/conan"
+ "forgejo.org/modules/setting"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ notify_service "forgejo.org/services/notify"
+ packages_service "forgejo.org/services/packages"
)
const (
diff --git a/routers/api/packages/conan/search.go b/routers/api/packages/conan/search.go
index 7370c702cd..afc94afef5 100644
--- a/routers/api/packages/conan/search.go
+++ b/routers/api/packages/conan/search.go
@@ -7,11 +7,11 @@ import (
"net/http"
"strings"
- conan_model "code.gitea.io/gitea/models/packages/conan"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- conan_module "code.gitea.io/gitea/modules/packages/conan"
- "code.gitea.io/gitea/services/context"
+ conan_model "forgejo.org/models/packages/conan"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ conan_module "forgejo.org/modules/packages/conan"
+ "forgejo.org/services/context"
)
// SearchResult contains the found recipe names
diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go
index c7e4544d52..52872d2543 100644
--- a/routers/api/packages/conda/conda.go
+++ b/routers/api/packages/conda/conda.go
@@ -10,16 +10,16 @@ import (
"net/http"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- conda_model "code.gitea.io/gitea/models/packages/conda"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- conda_module "code.gitea.io/gitea/modules/packages/conda"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ conda_model "forgejo.org/models/packages/conda"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ conda_module "forgejo.org/modules/packages/conda"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
"github.com/dsnet/compress/bzip2"
)
diff --git a/routers/api/packages/container/auth.go b/routers/api/packages/container/auth.go
index a8b3ec117a..71c237326e 100644
--- a/routers/api/packages/container/auth.go
+++ b/routers/api/packages/container/auth.go
@@ -6,10 +6,10 @@ package container
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/packages"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/packages"
)
var _ auth.Method = &Auth{}
diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go
index 9e3a47076c..0e07b03c0c 100644
--- a/routers/api/packages/container/blob.go
+++ b/routers/api/packages/container/blob.go
@@ -12,14 +12,14 @@ import (
"strings"
"sync"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- container_module "code.gitea.io/gitea/modules/packages/container"
- "code.gitea.io/gitea/modules/util"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ container_model "forgejo.org/models/packages/container"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ container_module "forgejo.org/modules/packages/container"
+ "forgejo.org/modules/util"
+ packages_service "forgejo.org/services/packages"
)
var uploadVersionMutex sync.Mutex
@@ -193,7 +193,7 @@ func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error
}
func digestFromHashSummer(h packages_module.HashSummer) string {
- _, _, hashSHA256, _ := h.Sums()
+ _, _, hashSHA256, _, _ := h.Sums()
return "sha256:" + hex.EncodeToString(hashSHA256)
}
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 9c9da38424..191a4aa455 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -4,6 +4,7 @@
package container
import (
+ "bytes"
"errors"
"fmt"
"io"
@@ -14,20 +15,20 @@ import (
"strconv"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- container_module "code.gitea.io/gitea/modules/packages/container"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
- container_service "code.gitea.io/gitea/services/packages/container"
+ auth_model "forgejo.org/models/auth"
+ packages_model "forgejo.org/models/packages"
+ container_model "forgejo.org/models/packages/container"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ container_module "forgejo.org/modules/packages/container"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
+ container_service "forgejo.org/services/packages/container"
digest "github.com/opencontainers/go-digest"
)
@@ -62,9 +63,6 @@ func setResponseHeaders(resp http.ResponseWriter, h *containerHeaders) {
if h.ContentType != "" {
resp.Header().Set("Content-Type", h.ContentType)
}
- if h.ContentLength != 0 {
- resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength, 10))
- }
if h.UploadUUID != "" {
resp.Header().Set("Docker-Upload-Uuid", h.UploadUUID)
}
@@ -72,17 +70,29 @@ func setResponseHeaders(resp http.ResponseWriter, h *containerHeaders) {
resp.Header().Set("Docker-Content-Digest", h.ContentDigest)
resp.Header().Set("ETag", fmt.Sprintf(`"%s"`, h.ContentDigest))
}
+ if h.ContentLength >= 0 {
+ resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength, 10))
+ }
resp.Header().Set("Docker-Distribution-Api-Version", "registry/2.0")
resp.WriteHeader(h.Status)
}
func jsonResponse(ctx *context.Context, status int, obj any) {
- setResponseHeaders(ctx.Resp, &containerHeaders{
- Status: status,
- ContentType: "application/json",
- })
- if err := json.NewEncoder(ctx.Resp).Encode(obj); err != nil {
+ // Buffer the JSON content first to calculate correct Content-Length
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(obj); err != nil {
log.Error("JSON encode: %v", err)
+ return
+ }
+
+ setResponseHeaders(ctx.Resp, &containerHeaders{
+ Status: status,
+ ContentType: "application/json",
+ ContentLength: int64(buf.Len()),
+ })
+
+ if _, err := buf.WriteTo(ctx.Resp); err != nil {
+ log.Error("JSON write: %v", err)
}
}
@@ -691,33 +701,30 @@ func DeleteManifest(ctx *context.Context) {
func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
serveDirectReqParams := make(url.Values)
serveDirectReqParams.Set("response-content-type", pfd.Properties.GetByName(container_module.PropertyMediaType))
- s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
+ s, u, pf, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
if err != nil {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
apiError(ctx, http.StatusInternalServerError, err)
return
}
- headers := &containerHeaders{
- ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
- ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
- ContentLength: pfd.Blob.Size,
- Status: http.StatusOK,
+ opts := &context.ServeHeaderOptions{
+ ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
+ RedirectStatusCode: http.StatusTemporaryRedirect,
+ AdditionalHeaders: map[string][]string{
+ "Docker-Distribution-Api-Version": {"registry/2.0"},
+ },
}
- if u != nil {
- headers.Status = http.StatusTemporaryRedirect
- headers.Location = u.String()
-
- setResponseHeaders(ctx.Resp, headers)
- return
+ if d := pfd.Properties.GetByName(container_module.PropertyDigest); d != "" {
+ opts.AdditionalHeaders["Docker-Content-Digest"] = []string{d}
+ opts.AdditionalHeaders["ETag"] = []string{fmt.Sprintf(`"%s"`, d)}
}
- defer s.Close()
-
- setResponseHeaders(ctx.Resp, headers)
- if _, err := io.Copy(ctx.Resp, s); err != nil {
- log.Error("Error whilst copying content to response: %v", err)
- }
+ helper.ServePackageFile(ctx, s, u, pf, opts)
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
@@ -725,7 +732,7 @@ func GetTagList(ctx *context.Context) {
image := ctx.Params("image")
if _, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.TypeContainer, image); err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiErrorDefined(ctx, errNameUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
diff --git a/routers/api/packages/container/container_test.go b/routers/api/packages/container/container_test.go
new file mode 100644
index 0000000000..2ed38d846d
--- /dev/null
+++ b/routers/api/packages/container/container_test.go
@@ -0,0 +1,124 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSetResponseHeaders(t *testing.T) {
+ t.Run("Content-Length for empty content", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusOK,
+ ContentLength: 0, // Empty blob
+ ContentDigest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ })
+
+ assert.Equal(t, "0", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", recorder.Header().Get("Docker-Content-Digest"))
+ assert.Equal(t, "registry/2.0", recorder.Header().Get("Docker-Distribution-Api-Version"))
+ assert.Equal(t, http.StatusOK, recorder.Code)
+ })
+
+ t.Run("Content-Length for non-empty content", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusOK,
+ ContentLength: 1024,
+ ContentDigest: "sha256:abcd1234",
+ })
+
+ assert.Equal(t, "1024", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:abcd1234", recorder.Header().Get("Docker-Content-Digest"))
+ })
+
+ t.Run("All headers set correctly", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusAccepted,
+ ContentLength: 512,
+ ContentDigest: "sha256:test123",
+ ContentType: "application/vnd.oci.image.manifest.v1+json",
+ Location: "/v2/test/repo/blobs/uploads/uuid123",
+ Range: "0-511",
+ UploadUUID: "uuid123",
+ })
+
+ assert.Equal(t, "512", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:test123", recorder.Header().Get("Docker-Content-Digest"))
+ assert.Equal(t, "application/vnd.oci.image.manifest.v1+json", recorder.Header().Get("Content-Type"))
+ assert.Equal(t, "/v2/test/repo/blobs/uploads/uuid123", recorder.Header().Get("Location"))
+ assert.Equal(t, "0-511", recorder.Header().Get("Range"))
+ assert.Equal(t, "uuid123", recorder.Header().Get("Docker-Upload-Uuid"))
+ assert.Equal(t, "registry/2.0", recorder.Header().Get("Docker-Distribution-Api-Version"))
+ assert.Equal(t, `"sha256:test123"`, recorder.Header().Get("ETag"))
+ assert.Equal(t, http.StatusAccepted, recorder.Code)
+ })
+}
+
+// TestResponseHeadersForEmptyBlobs tests the core fix for ORAS empty blob support
+func TestResponseHeadersForEmptyBlobs(t *testing.T) {
+ t.Run("Content-Length set for empty blob", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ // This tests the main fix: empty blobs should have Content-Length: 0
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusOK,
+ ContentLength: 0, // Empty blob (like empty config in ORAS artifacts)
+ ContentDigest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ })
+
+ // The key fix: Content-Length should be set even for 0-byte blobs
+ assert.Equal(t, "0", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", recorder.Header().Get("Docker-Content-Digest"))
+ assert.Equal(t, "registry/2.0", recorder.Header().Get("Docker-Distribution-Api-Version"))
+ assert.Equal(t, `"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`, recorder.Header().Get("ETag"))
+ assert.Equal(t, http.StatusOK, recorder.Code)
+ })
+
+ t.Run("Content-Length set for regular blob", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusOK,
+ ContentLength: 1024,
+ ContentDigest: "sha256:abcd1234",
+ })
+
+ assert.Equal(t, "1024", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:abcd1234", recorder.Header().Get("Docker-Content-Digest"))
+ })
+
+ t.Run("All headers set correctly", func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+
+ setResponseHeaders(recorder, &containerHeaders{
+ Status: http.StatusAccepted,
+ ContentLength: 512,
+ ContentDigest: "sha256:test123",
+ ContentType: "application/vnd.oci.image.manifest.v1+json",
+ Location: "/v2/test/repo/blobs/uploads/uuid123",
+ Range: "0-511",
+ UploadUUID: "uuid123",
+ })
+
+ assert.Equal(t, "512", recorder.Header().Get("Content-Length"))
+ assert.Equal(t, "sha256:test123", recorder.Header().Get("Docker-Content-Digest"))
+ assert.Equal(t, "application/vnd.oci.image.manifest.v1+json", recorder.Header().Get("Content-Type"))
+ assert.Equal(t, "/v2/test/repo/blobs/uploads/uuid123", recorder.Header().Get("Location"))
+ assert.Equal(t, "0-511", recorder.Header().Get("Range"))
+ assert.Equal(t, "uuid123", recorder.Header().Get("Docker-Upload-Uuid"))
+ assert.Equal(t, "registry/2.0", recorder.Header().Get("Docker-Distribution-Api-Version"))
+ assert.Equal(t, `"sha256:test123"`, recorder.Header().Get("ETag"))
+ assert.Equal(t, http.StatusAccepted, recorder.Code)
+ })
+}
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index 4a79a58f51..428e7605a6 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -11,17 +11,17 @@ import (
"os"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- container_module "code.gitea.io/gitea/modules/packages/container"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ container_model "forgejo.org/models/packages/container"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ container_module "forgejo.org/modules/packages/container"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
+ packages_service "forgejo.org/services/packages"
digest "github.com/opencontainers/go-digest"
oci "github.com/opencontainers/image-spec/specs-go/v1"
diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go
index f1d616724a..f73111278f 100644
--- a/routers/api/packages/cran/cran.go
+++ b/routers/api/packages/cran/cran.go
@@ -11,14 +11,14 @@ import (
"net/http"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- cran_model "code.gitea.io/gitea/models/packages/cran"
- packages_module "code.gitea.io/gitea/modules/packages"
- cran_module "code.gitea.io/gitea/modules/packages/cran"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ cran_model "forgejo.org/models/packages/cran"
+ packages_module "forgejo.org/modules/packages"
+ cran_module "forgejo.org/modules/packages/cran"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
func apiError(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go
index 8c05476cbc..fd64e35657 100644
--- a/routers/api/packages/debian/debian.go
+++ b/routers/api/packages/debian/debian.go
@@ -11,16 +11,16 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- packages_module "code.gitea.io/gitea/modules/packages"
- debian_module "code.gitea.io/gitea/modules/packages/debian"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- notify_service "code.gitea.io/gitea/services/notify"
- packages_service "code.gitea.io/gitea/services/packages"
- debian_service "code.gitea.io/gitea/services/packages/debian"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ packages_module "forgejo.org/modules/packages"
+ debian_module "forgejo.org/modules/packages/debian"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ notify_service "forgejo.org/services/notify"
+ packages_service "forgejo.org/services/packages"
+ debian_service "forgejo.org/services/packages/debian"
)
func apiError(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go
index e66f3ee676..b84b902d2b 100644
--- a/routers/api/packages/generic/generic.go
+++ b/routers/api/packages/generic/generic.go
@@ -10,12 +10,12 @@ import (
"strings"
"unicode"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
var (
@@ -155,7 +155,7 @@ func DeletePackage(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -182,7 +182,7 @@ func DeletePackageFile(ctx *context.Context) {
return pv, pf, nil
}()
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go
index 56a07dbd43..488850ecbf 100644
--- a/routers/api/packages/goproxy/goproxy.go
+++ b/routers/api/packages/goproxy/goproxy.go
@@ -11,14 +11,14 @@ import (
"sort"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- goproxy_module "code.gitea.io/gitea/modules/packages/goproxy"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ goproxy_module "forgejo.org/modules/packages/goproxy"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
func apiError(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go
index efdb83ec0e..1d8efb8d68 100644
--- a/routers/api/packages/helm/helm.go
+++ b/routers/api/packages/helm/helm.go
@@ -12,17 +12,17 @@ import (
"strings"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- helm_module "code.gitea.io/gitea/modules/packages/helm"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ helm_module "forgejo.org/modules/packages/helm"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
"gopkg.in/yaml.v3"
)
diff --git a/routers/api/packages/helper/helper.go b/routers/api/packages/helper/helper.go
index cdb64109ad..f9b91d9a09 100644
--- a/routers/api/packages/helper/helper.go
+++ b/routers/api/packages/helper/helper.go
@@ -9,10 +9,10 @@ import (
"net/http"
"net/url"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// LogAndProcessError logs an error and calls a custom callback with the processed error message.
@@ -39,16 +39,9 @@ func LogAndProcessError(ctx *context.Context, status int, obj any, cb func(strin
}
}
-// Serves the content of the package file
+// ServePackageFile Serves the content of the package file
// If the url is set it will redirect the request, otherwise the content is copied to the response.
func ServePackageFile(ctx *context.Context, s io.ReadSeekCloser, u *url.URL, pf *packages_model.PackageFile, forceOpts ...*context.ServeHeaderOptions) {
- if u != nil {
- ctx.Redirect(u.String())
- return
- }
-
- defer s.Close()
-
var opts *context.ServeHeaderOptions
if len(forceOpts) > 0 {
opts = forceOpts[0]
@@ -59,5 +52,12 @@ func ServePackageFile(ctx *context.Context, s io.ReadSeekCloser, u *url.URL, pf
}
}
+ if u != nil {
+ ctx.Redirect(u.String(), opts.RedirectStatusCode)
+ return
+ }
+
+ defer s.Close()
+
ctx.ServeContent(s, opts)
}
diff --git a/routers/api/packages/maven/api.go b/routers/api/packages/maven/api.go
index 167fe42b56..21d2aaa100 100644
--- a/routers/api/packages/maven/api.go
+++ b/routers/api/packages/maven/api.go
@@ -7,8 +7,8 @@ import (
"encoding/xml"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- maven_module "code.gitea.io/gitea/modules/packages/maven"
+ packages_model "forgejo.org/models/packages"
+ maven_module "forgejo.org/modules/packages/maven"
)
// MetadataResponse https://maven.apache.org/ref/3.2.5/maven-repository-metadata/repository-metadata.html
diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index 92f20255e1..30737f91dd 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -11,6 +11,7 @@ import (
"encoding/hex"
"encoding/xml"
"errors"
+ "fmt"
"io"
"net/http"
"path/filepath"
@@ -19,15 +20,15 @@ import (
"strconv"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- maven_module "code.gitea.io/gitea/modules/packages/maven"
- "code.gitea.io/gitea/modules/sync"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ maven_module "forgejo.org/modules/packages/maven"
+ "forgejo.org/modules/sync"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
const (
@@ -61,6 +62,12 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
+// buildPackageID creates a package ID from group and artifact ID
+// Refer to https://maven.apache.org/pom.html#Maven_Coordinates
+func buildPackageID(groupID, artifactID string) string {
+ return fmt.Sprintf("%s:%s", groupID, artifactID)
+}
+
// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
handlePackageFile(ctx, true)
@@ -88,7 +95,7 @@ func handlePackageFile(ctx *context.Context, serveContent bool) {
func serveMavenMetadata(ctx *context.Context, params parameters) {
// /com/foo/project/maven-metadata.xml[.md5/.sha1/.sha256/.sha512]
- packageName := params.GroupID + "-" + params.ArtifactID
+ packageName := buildPackageID(params.GroupID, params.ArtifactID)
pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, packageName)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -119,8 +126,8 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
latest := pds[len(pds)-1]
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
- lastModifed := latest.Version.CreatedUnix.AsTime().UTC().Format(http.TimeFormat)
- ctx.Resp.Header().Set("Last-Modified", lastModifed)
+ lastModified := latest.Version.CreatedUnix.AsTime().UTC().Format(http.TimeFormat)
+ ctx.Resp.Header().Set("Last-Modified", lastModified)
ext := strings.ToLower(filepath.Ext(params.Filename))
if isChecksumExtension(ext) {
@@ -150,7 +157,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
}
func servePackageFile(ctx *context.Context, params parameters, serveContent bool) {
- packageName := params.GroupID + "-" + params.ArtifactID
+ packageName := buildPackageID(params.GroupID, params.ArtifactID)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, packageName, params.Version)
if err != nil {
@@ -169,9 +176,9 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool
filename = filename[:len(filename)-len(ext)]
}
- pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, filename, packages_model.EmptyFileKey)
+ pf, err := packages_model.GetFileForVersionByNameMatchCase(ctx, pv.ID, filename, packages_model.EmptyFileKey)
if err != nil {
- if err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -247,7 +254,7 @@ func UploadPackageFile(ctx *context.Context) {
return
}
- packageName := params.GroupID + "-" + params.ArtifactID
+ packageName := buildPackageID(params.GroupID, params.ArtifactID)
mavenUploadLock.CheckIn(packageName)
defer mavenUploadLock.CheckOut(packageName)
@@ -283,9 +290,9 @@ func UploadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err)
return
}
- pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, params.Filename[:len(params.Filename)-len(ext)], packages_model.EmptyFileKey)
+ pf, err := packages_model.GetFileForVersionByNameMatchCase(ctx, pv.ID, params.Filename[:len(params.Filename)-len(ext)], packages_model.EmptyFileKey)
if err != nil {
- if err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -339,7 +346,7 @@ func UploadPackageFile(ctx *context.Context) {
if pvci.Metadata != nil {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version)
- if err != nil && err != packages_model.ErrPackageNotExist {
+ if err != nil && !errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusInternalServerError, err)
return
}
diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go
index b4379f3f49..e610c29b4d 100644
--- a/routers/api/packages/npm/api.go
+++ b/routers/api/packages/npm/api.go
@@ -10,9 +10,9 @@ import (
"net/url"
"sort"
- packages_model "code.gitea.io/gitea/models/packages"
- npm_module "code.gitea.io/gitea/modules/packages/npm"
- "code.gitea.io/gitea/modules/setting"
+ packages_model "forgejo.org/models/packages"
+ npm_module "forgejo.org/modules/packages/npm"
+ "forgejo.org/modules/setting"
)
func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *npm_module.PackageMetadata {
diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go
index 84acfffae2..bf9d247b30 100644
--- a/routers/api/packages/npm/npm.go
+++ b/routers/api/packages/npm/npm.go
@@ -12,19 +12,19 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- npm_module "code.gitea.io/gitea/modules/packages/npm"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ npm_module "forgejo.org/modules/packages/npm"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
"github.com/hashicorp/go-version"
)
diff --git a/routers/api/packages/nuget/api_v2.go b/routers/api/packages/nuget/api_v2.go
index a726065ad0..13c93316d5 100644
--- a/routers/api/packages/nuget/api_v2.go
+++ b/routers/api/packages/nuget/api_v2.go
@@ -8,8 +8,8 @@ import (
"strings"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- nuget_module "code.gitea.io/gitea/modules/packages/nuget"
+ packages_model "forgejo.org/models/packages"
+ nuget_module "forgejo.org/modules/packages/nuget"
)
type AtomTitle struct {
@@ -249,6 +249,9 @@ type FeedEntryProperties struct {
Version string `xml:"d:Version"`
NormalizedVersion string `xml:"d:NormalizedVersion"`
Authors string `xml:"d:Authors"`
+ Owners string `xml:"d:Owners,omitempty"`
+ Copyright string `xml:"d:Copyright,omitempty"`
+ Language string `xml:"d:Language,omitempty"`
Dependencies string `xml:"d:Dependencies"`
Description string `xml:"d:Description"`
VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
@@ -258,9 +261,15 @@ type FeedEntryProperties struct {
LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"`
Published TypedValue[time.Time] `xml:"d:Published"`
ProjectURL string `xml:"d:ProjectUrl,omitempty"`
+ LicenseURL string `xml:"d:LicenseUrl,omitempty"`
+ IconURL string `xml:"d:IconUrl,omitempty"`
ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"`
RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"`
- Title string `xml:"d:Title"`
+ DevelopmentDependency TypedValue[bool] `xml:"d:DevelopmentDependency"`
+ Title string `xml:"d:Title,omitempty"`
+ MinClientVersion string `xml:"d:MinClientVersion,omitempty"`
+ Tags string `xml:"d:Tags,omitempty"`
+ ID string `xml:"d:Id,omitempty"`
}
type FeedEntry struct {
@@ -356,6 +365,9 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames
Version: pd.Version.Version,
NormalizedVersion: pd.Version.Version,
Authors: metadata.Authors,
+ Owners: metadata.Owners,
+ Copyright: metadata.Copyright,
+ Language: metadata.Language,
Dependencies: buildDependencyString(metadata),
Description: metadata.Description,
VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
@@ -365,9 +377,15 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames
LastUpdated: createdValue,
Published: createdValue,
ProjectURL: metadata.ProjectURL,
+ LicenseURL: metadata.LicenseURL,
+ IconURL: metadata.IconURL,
ReleaseNotes: metadata.ReleaseNotes,
RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance},
- Title: pd.Package.Name,
+ DevelopmentDependency: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.DevelopmentDependency},
+ Title: metadata.Title,
+ MinClientVersion: metadata.MinClientVersion,
+ Tags: metadata.Tags,
+ ID: pd.Package.Name,
},
}
diff --git a/routers/api/packages/nuget/api_v3.go b/routers/api/packages/nuget/api_v3.go
index 2fe25dc0f8..f1f5300523 100644
--- a/routers/api/packages/nuget/api_v3.go
+++ b/routers/api/packages/nuget/api_v3.go
@@ -7,8 +7,8 @@ import (
"sort"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- nuget_module "code.gitea.io/gitea/modules/packages/nuget"
+ packages_model "forgejo.org/models/packages"
+ nuget_module "forgejo.org/modules/packages/nuget"
"golang.org/x/text/collate"
"golang.org/x/text/language"
diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go
index 1bb68d059b..92868bdef5 100644
--- a/routers/api/packages/nuget/auth.go
+++ b/routers/api/packages/nuget/auth.go
@@ -6,11 +6,11 @@ package nuget
import (
"net/http"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/services/auth"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/services/auth"
)
var _ auth.Method = &Auth{}
@@ -25,7 +25,7 @@ func (a *Auth) Name() string {
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
token, err := auth_model.GetAccessTokenBySHA(req.Context(), req.Header.Get("X-NuGet-ApiKey"))
if err != nil {
- if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) {
+ if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
log.Error("GetAccessTokenBySHA: %v", err)
return nil, err
}
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index 0d7212d7f7..254f4311c1 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -14,18 +14,18 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- nuget_model "code.gitea.io/gitea/models/packages/nuget"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- nuget_module "code.gitea.io/gitea/modules/packages/nuget"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ nuget_model "forgejo.org/models/packages/nuget"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ nuget_module "forgejo.org/modules/packages/nuget"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
func apiError(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go
index f87df52a29..1a1343083f 100644
--- a/routers/api/packages/pub/pub.go
+++ b/routers/api/packages/pub/pub.go
@@ -13,16 +13,16 @@ import (
"strings"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- pub_module "code.gitea.io/gitea/modules/packages/pub"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ pub_module "forgejo.org/modules/packages/pub"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
func jsonResponse(ctx *context.Context, status int, obj any) {
diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go
index 7824db1823..360632570e 100644
--- a/routers/api/packages/pypi/pypi.go
+++ b/routers/api/packages/pypi/pypi.go
@@ -10,15 +10,16 @@ import (
"regexp"
"sort"
"strings"
+ "unicode"
- packages_model "code.gitea.io/gitea/models/packages"
- packages_module "code.gitea.io/gitea/modules/packages"
- pypi_module "code.gitea.io/gitea/modules/packages/pypi"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ packages_module "forgejo.org/modules/packages"
+ pypi_module "forgejo.org/modules/packages/pypi"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
// https://peps.python.org/pep-0426/#name
@@ -120,7 +121,7 @@ func UploadPackageFile(ctx *context.Context) {
}
defer buf.Close()
- _, _, hashSHA256, _ := buf.Sums()
+ _, _, hashSHA256, _, _ := buf.Sums()
if !strings.EqualFold(ctx.Req.FormValue("sha256_digest"), hex.EncodeToString(hashSHA256)) {
apiError(ctx, http.StatusBadRequest, "hash mismatch")
@@ -139,9 +140,30 @@ func UploadPackageFile(ctx *context.Context) {
return
}
- projectURL := ctx.Req.FormValue("home_page")
- if !validation.IsValidURL(projectURL) {
- projectURL = ""
+ // Ensure ctx.Req.Form exists.
+ _ = ctx.Req.ParseForm()
+
+ var homepageURL string
+ projectURLs := ctx.Req.Form["project_urls"]
+ for _, purl := range projectURLs {
+ label, url, found := strings.Cut(purl, ",")
+ if !found {
+ continue
+ }
+ if normalizeLabel(label) != "homepage" {
+ continue
+ }
+ homepageURL = strings.TrimSpace(url)
+ break
+ }
+
+ if len(homepageURL) == 0 {
+ // TODO: Home-page is a deprecated metadata field. Remove this branch once it's no longer apart of the spec.
+ homepageURL = ctx.Req.FormValue("home_page")
+ }
+
+ if !validation.IsValidURL(homepageURL) {
+ homepageURL = ""
}
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
@@ -160,7 +182,7 @@ func UploadPackageFile(ctx *context.Context) {
Description: ctx.Req.FormValue("description"),
LongDescription: ctx.Req.FormValue("long_description"),
Summary: ctx.Req.FormValue("summary"),
- ProjectURL: projectURL,
+ ProjectURL: homepageURL,
License: ctx.Req.FormValue("license"),
RequiresPython: ctx.Req.FormValue("requires_python"),
},
@@ -189,6 +211,23 @@ func UploadPackageFile(ctx *context.Context) {
ctx.Status(http.StatusCreated)
}
+// Normalizes a Project-URL label.
+// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
+func normalizeLabel(label string) string {
+ var builder strings.Builder
+
+ // "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
+ // to lowercase."
+ for _, r := range label {
+ if unicode.IsPunct(r) || unicode.IsSpace(r) {
+ continue
+ }
+ builder.WriteRune(unicode.ToLower(r))
+ }
+
+ return builder.String()
+}
+
func isValidNameAndVersion(packageName, packageVersion string) bool {
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
}
diff --git a/routers/api/packages/pypi/pypi_test.go b/routers/api/packages/pypi/pypi_test.go
index 3023692177..786105693f 100644
--- a/routers/api/packages/pypi/pypi_test.go
+++ b/routers/api/packages/pypi/pypi_test.go
@@ -36,3 +36,13 @@ func TestIsValidNameAndVersion(t *testing.T) {
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
}
+
+func TestNormalizeLabel(t *testing.T) {
+ // Cases fetched from https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
+ assert.Equal(t, "homepage", normalizeLabel("Homepage"))
+ assert.Equal(t, "homepage", normalizeLabel("Home-page"))
+ assert.Equal(t, "homepage", normalizeLabel("Home page"))
+ assert.Equal(t, "changelog", normalizeLabel("Change_Log"))
+ assert.Equal(t, "whatsnew", normalizeLabel("What's New?"))
+ assert.Equal(t, "github", normalizeLabel("github"))
+}
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index 54fb01c854..cdbf893183 100644
--- a/routers/api/packages/rpm/rpm.go
+++ b/routers/api/packages/rpm/rpm.go
@@ -11,18 +11,18 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- packages_module "code.gitea.io/gitea/modules/packages"
- rpm_module "code.gitea.io/gitea/modules/packages/rpm"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- notify_service "code.gitea.io/gitea/services/notify"
- packages_service "code.gitea.io/gitea/services/packages"
- rpm_service "code.gitea.io/gitea/services/packages/rpm"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ packages_module "forgejo.org/modules/packages"
+ rpm_module "forgejo.org/modules/packages/rpm"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ notify_service "forgejo.org/services/notify"
+ packages_service "forgejo.org/services/packages"
+ rpm_service "forgejo.org/services/packages/rpm"
)
func apiError(ctx *context.Context, status int, obj any) {
@@ -149,7 +149,7 @@ func UploadPackageFile(ctx *context.Context) {
buf = signedBuf
}
- pck, err := rpm_module.ParsePackage(buf)
+ pck, err := rpm_module.ParsePackage(buf, "rpm")
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
apiError(ctx, http.StatusBadRequest, err)
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index dfefe2c4fb..eed19467ff 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -13,14 +13,14 @@ import (
"net/http"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ rubygems_module "forgejo.org/modules/packages/rubygems"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
)
const (
@@ -105,9 +105,11 @@ func ServePackageInfo(ctx *context.Context) {
ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, packageName)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
+ return
}
if len(versions) == 0 {
apiError(ctx, http.StatusNotFound, fmt.Sprintf("Could not find package %s", packageName))
+ return
}
result, err := buildInfoFileForPackage(ctx, versions)
@@ -135,6 +137,7 @@ func ServeVersionsFile(ctx *context.Context) {
ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, pack.Name)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
+ return
}
if len(versions) == 0 {
// No versions left for this package, we should continue.
@@ -144,6 +147,20 @@ func ServeVersionsFile(ctx *context.Context) {
fmt.Fprintf(result, "%s ", pack.Name)
for i, v := range versions {
result.WriteString(v.Version)
+
+ pd, err := packages_model.GetPackageDescriptor(ctx, v)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ metadata := pd.Metadata.(*rubygems_module.Metadata)
+
+ if metadata.Platform != "ruby" {
+ result.WriteString("_")
+ result.WriteString(metadata.Platform)
+ }
+
if i != len(versions)-1 {
result.WriteString(",")
}
@@ -152,6 +169,7 @@ func ServeVersionsFile(ctx *context.Context) {
info, err := buildInfoFileForPackage(ctx, versions)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
+ return
}
checksum := md5.Sum([]byte(*info))
@@ -413,6 +431,11 @@ func buildRequirementStringFromVersion(ctx *context.Context, version *packages_m
additionalRequirements.WriteString(",rubygems:")
writeRequirements(metadata.RequiredRubygemsVersion, additionalRequirements)
}
+
+ if metadata.Platform != "ruby" {
+ return fmt.Sprintf("%s-%s %s|%s", version.Version, metadata.Platform, dependencyRequirements, additionalRequirements), nil
+ }
+
return fmt.Sprintf("%s %s|%s", version.Version, dependencyRequirements, additionalRequirements), nil
}
diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go
index fce2a36dd6..a65bd31cd9 100644
--- a/routers/api/packages/swift/swift.go
+++ b/routers/api/packages/swift/swift.go
@@ -12,17 +12,17 @@ import (
"sort"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- swift_module "code.gitea.io/gitea/modules/packages/swift"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ swift_module "forgejo.org/modules/packages/swift"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
"github.com/hashicorp/go-version"
)
@@ -290,7 +290,24 @@ func DownloadManifest(ctx *context.Context) {
})
}
-// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
+// formFileOptionalReadCloser returns (nil, nil) if the formKey is not present.
+func formFileOptionalReadCloser(ctx *context.Context, formKey string) (io.ReadCloser, error) {
+ multipartFile, _, err := ctx.Req.FormFile(formKey)
+ if err != nil && !errors.Is(err, http.ErrMissingFile) {
+ return nil, err
+ }
+ if multipartFile != nil {
+ return multipartFile, nil
+ }
+
+ content := ctx.Req.FormValue(formKey)
+ if content == "" {
+ return nil, nil
+ }
+ return io.NopCloser(strings.NewReader(content)), nil
+}
+
+// UploadPackageFile refers to https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
func UploadPackageFile(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
@@ -304,9 +321,9 @@ func UploadPackageFile(ctx *context.Context) {
packageVersion := v.Core().String()
- file, _, err := ctx.Req.FormFile("source-archive")
- if err != nil {
- apiError(ctx, http.StatusBadRequest, err)
+ file, err := formFileOptionalReadCloser(ctx, "source-archive")
+ if file == nil || err != nil {
+ apiError(ctx, http.StatusBadRequest, "unable to read source-archive file")
return
}
defer file.Close()
@@ -318,10 +335,13 @@ func UploadPackageFile(ctx *context.Context) {
}
defer buf.Close()
- var mr io.Reader
- metadata := ctx.Req.FormValue("metadata")
- if metadata != "" {
- mr = strings.NewReader(metadata)
+ mr, err := formFileOptionalReadCloser(ctx, "metadata")
+ if err != nil {
+ apiError(ctx, http.StatusBadRequest, "unable to read metadata file")
+ return
+ }
+ if mr != nil {
+ defer mr.Close()
}
pck, err := swift_module.ParsePackage(buf, buf.Size(), mr)
diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go
index 98a81da368..26131c2cf2 100644
--- a/routers/api/packages/vagrant/vagrant.go
+++ b/routers/api/packages/vagrant/vagrant.go
@@ -11,13 +11,13 @@ import (
"sort"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- packages_module "code.gitea.io/gitea/modules/packages"
- vagrant_module "code.gitea.io/gitea/modules/packages/vagrant"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/api/packages/helper"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ packages_module "forgejo.org/modules/packages"
+ vagrant_module "forgejo.org/modules/packages/vagrant"
+ "forgejo.org/modules/setting"
+ "forgejo.org/routers/api/packages/helper"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
"github.com/hashicorp/go-version"
)
diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go
index e2ff004024..7d537f1ef9 100644
--- a/routers/api/shared/middleware.go
+++ b/routers/api/shared/middleware.go
@@ -6,13 +6,11 @@ package shared
import (
"net/http"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/context"
"github.com/go-chi/cors"
)
@@ -32,7 +30,6 @@ func Middlewares() (stack []any) {
return append(stack,
context.APIContexter(),
- checkDeprecatedAuthMethods,
// Get user from session if logged in.
apiAuth(buildAuthGroup()),
verifyAuthWithOptions(&common.VerifyOptions{
@@ -51,10 +48,6 @@ func buildAuthGroup() *auth.Group {
group.Add(&auth.ReverseProxy{})
}
- if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) {
- group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI
- }
-
return group
}
@@ -133,13 +126,6 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC
}
}
-// check for and warn against deprecated authentication options
-func checkDeprecatedAuthMethods(ctx *context.APIContext) {
- if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" {
- ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.")
- }
-}
-
func securityHeaders() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
diff --git a/routers/api/v1/activitypub/actor.go b/routers/api/v1/activitypub/actor.go
index 4f128e74c4..e49f277842 100644
--- a/routers/api/v1/activitypub/actor.go
+++ b/routers/api/v1/activitypub/actor.go
@@ -7,11 +7,11 @@ package activitypub
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
@@ -28,7 +28,7 @@ func Actor(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/ActivityPub"
- link := user_model.APActorUserAPActorID()
+ link := user_model.APServerActorID()
actor := ap.ActorNew(ap.IRI(link), ap.ApplicationType)
actor.PreferredUsername = ap.NaturalLanguageValuesNew()
@@ -46,7 +46,7 @@ func Actor(ctx *context.APIContext) {
actor.PublicKey.ID = ap.IRI(link + "#main-key")
actor.PublicKey.Owner = ap.IRI(link)
- publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPActorUser())
+ publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPServerActor())
if err != nil {
ctx.ServerError("GetPublicKey", err)
return
diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go
index 995a148f0b..1da7933418 100644
--- a/routers/api/v1/activitypub/person.go
+++ b/routers/api/v1/activitypub/person.go
@@ -8,10 +8,10 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go
index bc6e7905a6..c506840f1c 100644
--- a/routers/api/v1/activitypub/repository.go
+++ b/routers/api/v1/activitypub/repository.go
@@ -8,12 +8,12 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/modules/forgefed"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/federation"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/federation"
ap "github.com/go-ap/activitypub"
)
@@ -70,8 +70,8 @@ func RepositoryInbox(ctx *context.APIContext) {
repository := ctx.Repo.Repository
log.Info("RepositoryInbox: repo: %v", repository)
-
form := web.GetForm(ctx)
+ // TODO: Decide between like/undo{like} activity
httpStatus, title, err := federation.ProcessLikeActivity(ctx, form, repository.ID)
if err != nil {
ctx.Error(httpStatus, title, err)
diff --git a/routers/api/v1/activitypub/repository_test.go b/routers/api/v1/activitypub/repository_test.go
index 1e5af6acac..8d448a4356 100644
--- a/routers/api/v1/activitypub/repository_test.go
+++ b/routers/api/v1/activitypub/repository_test.go
@@ -6,7 +6,7 @@ package activitypub
import (
"testing"
- "code.gitea.io/gitea/modules/validation"
+ "forgejo.org/modules/validation"
)
func Test_UserEmailValidate(t *testing.T) {
diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go
index 6003f664a0..b84fbe05fa 100644
--- a/routers/api/v1/activitypub/reqsignature.go
+++ b/routers/api/v1/activitypub/reqsignature.go
@@ -6,59 +6,134 @@ package activitypub
import (
"crypto"
"crypto/x509"
+ "database/sql"
"encoding/pem"
+ "errors"
"fmt"
- "io"
"net/http"
"net/url"
- "code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- gitea_context "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/models/forgefed"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ fm "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ gitea_context "forgejo.org/services/context"
+ "forgejo.org/services/federation"
+ "github.com/42wim/httpsig"
ap "github.com/go-ap/activitypub"
- "github.com/go-fed/httpsig"
)
-func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) {
- person := ap.PersonNew(ap.IRI(keyID.String()))
- err = person.UnmarshalJSON(b)
- if err != nil {
- return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
- }
- pubKey := person.PublicKey
- if pubKey.ID.String() != keyID.String() {
- return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
- }
- pubKeyPem := pubKey.PublicKeyPem
+func decodePublicKeyPem(pubKeyPem string) ([]byte, error) {
block, _ := pem.Decode([]byte(pubKeyPem))
if block == nil || block.Type != "PUBLIC KEY" {
- return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
+ return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
}
- p, err = x509.ParsePKIXPublicKey(block.Bytes)
- return p, err
+
+ return block.Bytes, nil
}
-func fetch(iri *url.URL) (b []byte, err error) {
- req := httplib.NewRequest(iri.String(), http.MethodGet)
- req.Header("Accept", activitypub.ActivityStreamsContentType)
- req.Header("User-Agent", "Gitea/"+setting.AppVer)
- resp, err := req.Response()
+func getFederatedUser(ctx *gitea_context.APIContext, person *ap.Person, federationHost *forgefed.FederationHost) (*user.FederatedUser, error) {
+ personID, err := fm.NewPersonID(person.ID.String(), string(federationHost.NodeInfo.SoftwareName))
if err != nil {
return nil, err
}
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
+ _, federatedUser, err := user.FindFederatedUser(ctx, personID.ID, federationHost.ID)
+ if err != nil {
+ return nil, err
}
- b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
- return b, err
+
+ if federatedUser != nil {
+ return federatedUser, nil
+ }
+
+ _, newFederatedUser, err := federation.CreateUserFromAP(ctx, personID, federationHost.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ return newFederatedUser, nil
+}
+
+func storePublicKey(ctx *gitea_context.APIContext, person *ap.Person, pubKeyBytes []byte) error {
+ federationHost, err := federation.GetFederationHostForURI(ctx, person.ID.String())
+ if err != nil {
+ return err
+ }
+
+ if person.Type == ap.ActivityVocabularyType("Application") {
+ federationHost.KeyID = sql.NullString{
+ String: person.PublicKey.ID.String(),
+ Valid: true,
+ }
+
+ federationHost.PublicKey = sql.Null[sql.RawBytes]{
+ V: pubKeyBytes,
+ Valid: true,
+ }
+
+ _, err = db.GetEngine(ctx).ID(federationHost.ID).Update(federationHost)
+ if err != nil {
+ return err
+ }
+ } else if person.Type == ap.ActivityVocabularyType("Person") {
+ federatedUser, err := getFederatedUser(ctx, person, federationHost)
+ if err != nil {
+ return err
+ }
+
+ federatedUser.KeyID = sql.NullString{
+ String: person.PublicKey.ID.String(),
+ Valid: true,
+ }
+
+ federatedUser.PublicKey = sql.Null[sql.RawBytes]{
+ V: pubKeyBytes,
+ Valid: true,
+ }
+
+ _, err = db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func getPublicKeyFromResponse(b []byte, keyID *url.URL) (person *ap.Person, pubKeyBytes []byte, p crypto.PublicKey, err error) {
+ person = ap.PersonNew(ap.IRI(keyID.String()))
+ err = person.UnmarshalJSON(b)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
+ }
+
+ pubKey := person.PublicKey
+ if pubKey.ID.String() != keyID.String() {
+ return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
+ }
+
+ pubKeyBytes, err = decodePublicKeyPem(pubKey.PublicKeyPem)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ p, err = x509.ParsePKIXPublicKey(pubKeyBytes)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return person, pubKeyBytes, p, err
}
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
+ if !setting.Federation.SignatureEnforced {
+ return true, nil
+ }
+
r := ctx.Req
// 1. Figure out what key we need to verify
@@ -66,23 +141,78 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
if err != nil {
return false, err
}
+
ID := v.KeyId()
idIRI, err := url.Parse(ID)
if err != nil {
return false, err
}
+
+ signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0])
+
// 2. Fetch the public key of the other actor
- b, err := fetch(idIRI)
+ // Try if the signing actor is an already known federated user
+ _, federationUser, err := user.FindFederatedUserByKeyID(ctx, idIRI.String())
if err != nil {
return false, err
}
- pubKey, err := getPublicKeyFromResponse(b, idIRI)
+
+ if federationUser != nil && federationUser.PublicKey.Valid {
+ pubKey, err := x509.ParsePKIXPublicKey(federationUser.PublicKey.V)
+ if err != nil {
+ return false, err
+ }
+
+ authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
+ return authenticated, err
+ }
+
+ // Try if the signing actor is an already known federation host
+ federationHost, err := forgefed.FindFederationHostByKeyID(ctx, idIRI.String())
if err != nil {
return false, err
}
- // 3. Verify the other actor's key
- algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
- authenticated = v.Verify(pubKey, algo) == nil
+
+ if federationHost != nil && federationHost.PublicKey.Valid {
+ pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V)
+ if err != nil {
+ return false, err
+ }
+
+ authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
+ return authenticated, err
+ }
+
+ // Fetch missing public key
+ actionsUser := user.NewAPServerActor()
+ clientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return false, err
+ }
+
+ apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
+ if err != nil {
+ return false, err
+ }
+
+ b, err := apClient.GetBody(idIRI.String())
+ if err != nil {
+ return false, err
+ }
+
+ person, pubKeyBytes, pubKey, err := getPublicKeyFromResponse(b, idIRI)
+ if err != nil {
+ return false, err
+ }
+
+ authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
+ if authenticated {
+ err = storePublicKey(ctx, person, pubKeyBytes)
+ if err != nil {
+ return false, err
+ }
+ }
+
return authenticated, err
}
diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go
index 42ef375f12..a97f363cc2 100644
--- a/routers/api/v1/activitypub/response.go
+++ b/routers/api/v1/activitypub/response.go
@@ -6,10 +6,10 @@ package activitypub
import (
"net/http"
- "code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/forgefed"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go
index a4708fe032..082411f2bc 100644
--- a/routers/api/v1/admin/adopt.go
+++ b/routers/api/v1/admin/adopt.go
@@ -6,12 +6,12 @@ package admin
import (
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
// ListUnadoptedRepositories lists the unadopted repositories that match the provided names
diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go
index e1ca6048c9..5d68ab2dce 100644
--- a/routers/api/v1/admin/cron.go
+++ b/routers/api/v1/admin/cron.go
@@ -6,12 +6,12 @@ package admin
import (
"net/http"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/cron"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/cron"
)
// ListCronTasks api for getting cron tasks
diff --git a/routers/api/v1/admin/email.go b/routers/api/v1/admin/email.go
index ba963e9f69..906780a44b 100644
--- a/routers/api/v1/admin/email.go
+++ b/routers/api/v1/admin/email.go
@@ -6,11 +6,11 @@ package admin
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetAllEmails
diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go
index b246cb61b1..b3db2eb5e3 100644
--- a/routers/api/v1/admin/hooks.go
+++ b/routers/api/v1/admin/hooks.go
@@ -7,14 +7,14 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/webhook"
- "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"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
// ListHooks list system's webhooks
diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go
index a5c299bbf0..d3a5cea056 100644
--- a/routers/api/v1/admin/org.go
+++ b/routers/api/v1/admin/org.go
@@ -7,14 +7,14 @@ package admin
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// CreateOrg api for create organization
diff --git a/routers/api/v1/admin/quota.go b/routers/api/v1/admin/quota.go
index 1e7c11e007..c7da0e6398 100644
--- a/routers/api/v1/admin/quota.go
+++ b/routers/api/v1/admin/quota.go
@@ -6,9 +6,9 @@ package admin
import (
"net/http"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetUserQuota return information about a user's quota
diff --git a/routers/api/v1/admin/quota_group.go b/routers/api/v1/admin/quota_group.go
index e20b361eb5..afe33b639c 100644
--- a/routers/api/v1/admin/quota_group.go
+++ b/routers/api/v1/admin/quota_group.go
@@ -7,12 +7,12 @@ import (
go_context "context"
"net/http"
- "code.gitea.io/gitea/models/db"
- quota_model "code.gitea.io/gitea/models/quota"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ quota_model "forgejo.org/models/quota"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListQuotaGroups returns all the quota groups
diff --git a/routers/api/v1/admin/quota_rule.go b/routers/api/v1/admin/quota_rule.go
index 85c05e1e9b..c2bc6843e4 100644
--- a/routers/api/v1/admin/quota_rule.go
+++ b/routers/api/v1/admin/quota_rule.go
@@ -4,14 +4,14 @@
package admin
import (
- "fmt"
+ "errors"
"net/http"
- quota_model "code.gitea.io/gitea/models/quota"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ quota_model "forgejo.org/models/quota"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func toLimitSubjects(subjStrings []string) (*quota_model.LimitSubjects, error) {
@@ -83,7 +83,7 @@ func CreateQuotaRule(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateQuotaRuleOptions)
if form.Limit == nil {
- ctx.Error(http.StatusUnprocessableEntity, "quota_model.ParseLimitSubject", fmt.Errorf("[Limit]: Required"))
+ ctx.Error(http.StatusUnprocessableEntity, "quota_model.ParseLimitSubject", errors.New("[Limit]: Required"))
return
}
diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go
index c119d5390a..9f9118e0ab 100644
--- a/routers/api/v1/admin/repo.go
+++ b/routers/api/v1/admin/repo.go
@@ -4,10 +4,10 @@
package admin
import (
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/repo"
- "code.gitea.io/gitea/services/context"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/repo"
+ "forgejo.org/services/context"
)
// CreateRepo api for creating a repository
diff --git a/routers/api/v1/admin/runners.go b/routers/api/v1/admin/runners.go
index 329242d9f6..e459b947ff 100644
--- a/routers/api/v1/admin/runners.go
+++ b/routers/api/v1/admin/runners.go
@@ -4,8 +4,8 @@
package admin
import (
- "code.gitea.io/gitea/routers/api/v1/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/services/context"
)
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
@@ -24,3 +24,23 @@ func GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, 0, 0)
}
+
+// SearchActionRunJobs return a list of actions jobs filtered by the provided parameters
+func SearchActionRunJobs(ctx *context.APIContext) {
+ // swagger:operation GET /admin/runners/jobs admin adminSearchRunJobs
+ // ---
+ // summary: Search action jobs according filter conditions
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: labels
+ // in: query
+ // description: a comma separated list of run job labels to search for
+ // type: string
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RunJobList"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ shared.GetActionRunJobs(ctx, 0, 0)
+}
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index acab883107..8aa67b3b0a 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -10,26 +10,26 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/user"
- "code.gitea.io/gitea/routers/api/v1/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/mailer"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/user"
+ "forgejo.org/routers/api/v1/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/mailer"
+ user_service "forgejo.org/services/user"
)
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) {
@@ -140,7 +140,6 @@ func CreateUser(ctx *context.APIContext) {
user_model.IsErrEmailAlreadyUsed(err) ||
db.IsErrNameReserved(err) ||
db.IsErrNameCharsNotAllowed(err) ||
- validation.IsErrEmailCharIsNotSupported(err) ||
validation.IsErrEmailInvalid(err) ||
db.IsErrNamePatternNotAllowed(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
@@ -197,7 +196,7 @@ func EditUser(ctx *context.APIContext) {
// If either LoginSource or LoginName is given, the other must be present too.
if form.SourceID != nil || form.LoginName != nil {
if form.SourceID == nil || form.LoginName == nil {
- ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", fmt.Errorf("source_id and login_name must be specified together"))
+ ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", errors.New("source_id and login_name must be specified together"))
return
}
}
@@ -226,7 +225,7 @@ func EditUser(ctx *context.APIContext) {
if form.Email != nil {
if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, ctx.ContextUser, *form.Email); err != nil {
switch {
- case validation.IsErrEmailCharIsNotSupported(err), validation.IsErrEmailInvalid(err):
+ case validation.IsErrEmailInvalid(err):
ctx.Error(http.StatusBadRequest, "EmailInvalid", err)
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Error(http.StatusBadRequest, "EmailUsed", err)
@@ -305,7 +304,7 @@ func DeleteUser(ctx *context.APIContext) {
// admin should not delete themself
if ctx.ContextUser.ID == ctx.Doer.ID {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("you cannot delete yourself"))
+ ctx.Error(http.StatusUnprocessableEntity, "", errors.New("you cannot delete yourself"))
return
}
@@ -523,7 +522,7 @@ func RenameUser(ctx *context.APIContext) {
newName := web.GetForm(ctx).(*api.RenameUserOption).NewName
// Check if user name has been changed
- if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
+ if err := user_service.AdminRenameUser(ctx, ctx.ContextUser, newName); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken"))
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 18ab6ce287..bf08bdd249 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -22,8 +22,6 @@
//
// Security:
// - BasicAuth :
-// - Token :
-// - AccessToken :
// - AuthorizationHeaderToken :
// - SudoParam :
// - SudoHeader :
@@ -32,16 +30,6 @@
// SecurityDefinitions:
// BasicAuth:
// type: basic
-// Token:
-// type: apiKey
-// name: token
-// in: query
-// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
-// AccessToken:
-// type: apiKey
-// name: access_token
-// in: query
-// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
// AuthorizationHeaderToken:
// type: apiKey
// name: Authorization
@@ -71,37 +59,37 @@ import (
"net/http"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/forgefed"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/shared"
- "code.gitea.io/gitea/routers/api/v1/activitypub"
- "code.gitea.io/gitea/routers/api/v1/admin"
- "code.gitea.io/gitea/routers/api/v1/misc"
- "code.gitea.io/gitea/routers/api/v1/notify"
- "code.gitea.io/gitea/routers/api/v1/org"
- "code.gitea.io/gitea/routers/api/v1/packages"
- "code.gitea.io/gitea/routers/api/v1/repo"
- "code.gitea.io/gitea/routers/api/v1/settings"
- "code.gitea.io/gitea/routers/api/v1/user"
- "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/shared"
+ "forgejo.org/routers/api/v1/activitypub"
+ "forgejo.org/routers/api/v1/admin"
+ "forgejo.org/routers/api/v1/misc"
+ "forgejo.org/routers/api/v1/notify"
+ "forgejo.org/routers/api/v1/org"
+ "forgejo.org/routers/api/v1/packages"
+ "forgejo.org/routers/api/v1/repo"
+ "forgejo.org/routers/api/v1/settings"
+ "forgejo.org/routers/api/v1/user"
+ "forgejo.org/services/actions"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
- _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
+ _ "forgejo.org/routers/api/v1/swagger" // for swagger generation
"code.forgejo.org/go-chi/binding"
)
@@ -203,19 +191,19 @@ func repoAssignment() func(ctx *context.APIContext) {
}
if task.IsForkPullRequest {
- ctx.Repo.Permission.AccessMode = perm.AccessModeRead
+ ctx.Repo.AccessMode = perm.AccessModeRead
} else {
- ctx.Repo.Permission.AccessMode = perm.AccessModeWrite
+ ctx.Repo.AccessMode = perm.AccessModeWrite
}
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadUnits", err)
return
}
- ctx.Repo.Permission.Units = ctx.Repo.Repository.Units
- ctx.Repo.Permission.UnitsMode = make(map[unit.Type]perm.AccessMode)
+ ctx.Repo.Units = ctx.Repo.Repository.Units
+ ctx.Repo.UnitsMode = make(map[unit.Type]perm.AccessMode)
for _, u := range ctx.Repo.Repository.Units {
- ctx.Repo.Permission.UnitsMode[u.Type] = ctx.Repo.Permission.AccessMode
+ ctx.Repo.UnitsMode[u.Type] = ctx.Repo.AccessMode
}
} else {
ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
@@ -692,7 +680,7 @@ func mustEnableIssues(ctx *context.APIContext) {
}
func mustAllowPulls(ctx *context.APIContext) {
- if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) {
+ if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() {
if ctx.IsSigned {
log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
@@ -716,7 +704,7 @@ func mustAllowPulls(ctx *context.APIContext) {
func mustEnableIssuesOrPulls(ctx *context.APIContext) {
if !ctx.Repo.CanRead(unit.TypeIssues) &&
- !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) {
+ (!ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests)) {
if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() {
if ctx.IsSigned {
log.Trace("Permission Denied: User %-v cannot read %-v and %-v in Repo %-v\n"+
@@ -777,13 +765,13 @@ func bind[T any](_ T) any {
func individualPermsChecker(ctx *context.APIContext) {
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
if ctx.ContextUser.IsIndividual() {
- switch {
- case ctx.ContextUser.Visibility == api.VisibleTypePrivate:
+ switch ctx.ContextUser.Visibility {
+ case api.VisibleTypePrivate:
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
ctx.NotFound("Visit Project", nil)
return
}
- case ctx.ContextUser.Visibility == api.VisibleTypeLimited:
+ case api.VisibleTypeLimited:
if ctx.Doer == nil {
ctx.NotFound("Visit Project", nil)
return
@@ -822,6 +810,7 @@ func Routes() *web.Route {
m.Group("/runners", func() {
m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
+ m.Get("/jobs", reqToken(), reqChecker, act.SearchActionRunJobs)
})
})
}
@@ -839,22 +828,22 @@ func Routes() *web.Route {
m.Group("/activitypub", func() {
// deprecated, remove in 1.20, use /user-id/{user-id} instead
m.Group("/user/{username}", func() {
- m.Get("", activitypub.Person)
+ m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
m.Group("/user-id/{user-id}", func() {
- m.Get("", activitypub.Person)
+ m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
}, context.UserIDAssignmentAPI(), checkTokenPublicOnly())
m.Group("/actor", func() {
m.Get("", activitypub.Actor)
- m.Post("/inbox", activitypub.ActorInbox)
+ m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.ActorInbox)
})
m.Group("/repository-id/{repository-id}", func() {
- m.Get("", activitypub.Repository)
+ m.Get("", activitypub.ReqHTTPSignature(), activitypub.Repository)
m.Post("/inbox",
bind(forgefed.ForgeLike{}),
- // TODO: activitypub.ReqHTTPSignature(),
+ activitypub.ReqHTTPSignature(),
activitypub.RepositoryInbox)
}, context.RepositoryIDAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub))
@@ -864,6 +853,7 @@ func Routes() *web.Route {
m.Group("", func() {
m.Get("/version", misc.Version)
m.Get("/signing-key.gpg", misc.SigningKey)
+ m.Get("/signing-key.ssh", misc.SSHSigningKey)
m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup)
m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown)
m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw)
@@ -975,6 +965,7 @@ func Routes() *web.Route {
m.Group("/runners", func() {
m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
+ m.Get("/jobs", reqToken(), user.SearchActionRunJobs)
})
})
@@ -1181,6 +1172,10 @@ func Routes() *web.Route {
}, reqToken(), reqAdmin())
m.Group("/actions", func() {
m.Get("/tasks", repo.ListActionTasks)
+ m.Group("/runs", func() {
+ m.Get("", repo.ListActionRuns)
+ m.Get("/{run_id}", repo.GetActionRun)
+ })
m.Group("/workflows", func() {
m.Group("/{workflowname}", func() {
@@ -1315,6 +1310,7 @@ func Routes() *web.Route {
m.Get("/refs", repo.GetGitAllRefs)
m.Get("/refs/*", repo.GetGitRefs)
m.Get("/trees/{sha}", repo.GetTree)
+ m.Get("/blobs", repo.GetBlobs)
m.Get("/blobs/{sha}", repo.GetBlob)
m.Get("/tags/{sha}", repo.GetAnnotatedTag)
m.Group("/notes/{sha}", func() {
@@ -1353,6 +1349,12 @@ func Routes() *web.Route {
m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar)
m.Delete("", repo.DeleteAvatar)
}, reqAdmin(), reqToken())
+ m.Group("/sync_fork", func() {
+ m.Get("", reqRepoReader(unit.TypeCode), repo.SyncForkDefaultInfo)
+ m.Post("", mustNotBeArchived, reqRepoWriter(unit.TypeCode), repo.SyncForkDefault)
+ m.Get("/{branch}", reqRepoReader(unit.TypeCode), repo.SyncForkBranchInfo)
+ m.Post("/{branch}", mustNotBeArchived, reqRepoWriter(unit.TypeCode), repo.SyncForkBranch)
+ })
m.Get("/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive)
}, repoAssignment(), checkTokenPublicOnly())
@@ -1483,12 +1485,18 @@ func Routes() *web.Route {
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
m.Group("/packages/{username}", func() {
- m.Group("/{type}/{name}/{version}", func() {
- m.Get("", reqToken(), packages.GetPackage)
- m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
- m.Get("/files", reqToken(), packages.ListPackageFiles)
+ m.Group("/{type}/{name}", func() {
+ m.Group("/{version}", func() {
+ m.Get("", packages.GetPackage)
+ m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
+ m.Get("/files", packages.ListPackageFiles)
+ })
+
+ m.Post("/-/link/{repo_name}", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage)
+ m.Post("/-/unlink", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage)
})
- m.Get("/", reqToken(), packages.ListPackages)
+
+ m.Get("/", packages.ListPackages)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
// Organizations
@@ -1503,6 +1511,7 @@ func Routes() *web.Route {
m.Combo("").Get(org.Get).
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
Delete(reqToken(), reqOrgOwnership(), org.Delete)
+ m.Post("/rename", reqToken(), reqOrgOwnership(), bind(api.RenameOrgOption{}), org.Rename)
m.Combo("/repos").Get(user.ListOrgRepos).
Post(reqToken(), bind(api.CreateRepoOption{}), context.EnforceQuotaAPI(quota_model.LimitSubjectSizeReposAll, context.QuotaTargetOrg), repo.CreateOrgRepo)
m.Group("/members", func() {
@@ -1631,6 +1640,7 @@ func Routes() *web.Route {
})
m.Group("/runners", func() {
m.Get("/registration-token", admin.GetRegistrationToken)
+ m.Get("/jobs", admin.SearchActionRunJobs)
})
if setting.Quota.Enabled {
m.Group("/quota", func() {
diff --git a/routers/api/v1/misc/gitignore.go b/routers/api/v1/misc/gitignore.go
index dffd771752..ec57038a9b 100644
--- a/routers/api/v1/misc/gitignore.go
+++ b/routers/api/v1/misc/gitignore.go
@@ -6,11 +6,11 @@ package misc
import (
"net/http"
- "code.gitea.io/gitea/modules/options"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/options"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
// Shows a list of all Gitignore templates
diff --git a/routers/api/v1/misc/label_templates.go b/routers/api/v1/misc/label_templates.go
index cc11f37626..dad5ee34c1 100644
--- a/routers/api/v1/misc/label_templates.go
+++ b/routers/api/v1/misc/label_templates.go
@@ -6,10 +6,10 @@ package misc
import (
"net/http"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// Shows a list of all Label templates
diff --git a/routers/api/v1/misc/licenses.go b/routers/api/v1/misc/licenses.go
index 2a980f5084..c9b657a890 100644
--- a/routers/api/v1/misc/licenses.go
+++ b/routers/api/v1/misc/licenses.go
@@ -8,12 +8,12 @@ import (
"net/http"
"net/url"
- "code.gitea.io/gitea/modules/options"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/options"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
// Returns a list of all License templates
diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go
index 155ad15712..e9f03fdd5c 100644
--- a/routers/api/v1/misc/markup.go
+++ b/routers/api/v1/misc/markup.go
@@ -6,12 +6,12 @@ package misc
import (
"net/http"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
)
// Markup render markup document to HTML
diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go
index df70ee49ef..3335199e12 100644
--- a/routers/api/v1/misc/markup_test.go
+++ b/routers/api/v1/misc/markup_test.go
@@ -10,11 +10,11 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
@@ -112,7 +112,7 @@ Here are some links to the most important topics. You can find the full list of
Quick Links
Here are some links to the most important topics. You can find the full list of pages at the sidebar.
Configuration
-
+
`,
}
diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go
index 9c2a0db8d2..9631de7edd 100644
--- a/routers/api/v1/misc/nodeinfo.go
+++ b/routers/api/v1/misc/nodeinfo.go
@@ -7,20 +7,20 @@ import (
"net/http"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
)
const cacheKeyNodeInfoUsage = "API_NodeInfoUsage"
-// NodeInfo returns the NodeInfo for the Gitea instance to allow for federation
+// NodeInfo returns the NodeInfo for the Forgejo instance to allow for federation
func NodeInfo(ctx *context.APIContext) {
// swagger:operation GET /nodeinfo miscellaneous getNodeInfo
// ---
- // summary: Returns the nodeinfo of the Gitea application
+ // summary: Returns the nodeinfo of the Forgejo application
// produces:
// - application/json
// responses:
diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go
index 24a46c1e70..9f829b8443 100644
--- a/routers/api/v1/misc/signing.go
+++ b/routers/api/v1/misc/signing.go
@@ -7,8 +7,11 @@ import (
"fmt"
"net/http"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+
+ "golang.org/x/crypto/ssh"
)
// SigningKey returns the public key of the default signing key if it exists
@@ -61,3 +64,29 @@ func SigningKey(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "gpg export", fmt.Errorf("Error writing key content %w", err))
}
}
+
+// SSHSigningKey returns the public SSH key of the default signing key if it exists
+func SSHSigningKey(ctx *context.APIContext) {
+ // swagger:operation GET /signing-key.ssh miscellaneous getSSHSigningKey
+ // ---
+ // summary: Get default signing-key.ssh
+ // produces:
+ // - text/plain
+ // responses:
+ // "200":
+ // description: "SSH public key in OpenSSH authorized key format"
+ // schema:
+ // type: string
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ if setting.SSHInstanceKey == nil {
+ ctx.NotFound()
+ return
+ }
+
+ _, err := ctx.Write(ssh.MarshalAuthorizedKey(setting.SSHInstanceKey))
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "ssh export", err)
+ }
+}
diff --git a/routers/api/v1/misc/version.go b/routers/api/v1/misc/version.go
index e3b43a0e6b..5802c12462 100644
--- a/routers/api/v1/misc/version.go
+++ b/routers/api/v1/misc/version.go
@@ -6,16 +6,16 @@ package misc
import (
"net/http"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
)
// Version shows the version of the Gitea server
func Version(ctx *context.APIContext) {
// swagger:operation GET /version miscellaneous getVersion
// ---
- // summary: Returns the version of the Gitea application
+ // summary: Returns the version of the running application
// produces:
// - application/json
// responses:
diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go
index 46b3c7f5e7..2e19fa0b9c 100644
--- a/routers/api/v1/notify/notifications.go
+++ b/routers/api/v1/notify/notifications.go
@@ -7,11 +7,11 @@ import (
"net/http"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
)
// NewAvailable check if unread notifications exist
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index 1744426ee8..64a9654d48 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -8,11 +8,11 @@ import (
"strings"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func statusStringToNotificationStatus(status string) activities_model.NotificationStatus {
diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go
index 8e12d359cb..57c78f5f15 100644
--- a/routers/api/v1/notify/threads.go
+++ b/routers/api/v1/notify/threads.go
@@ -7,11 +7,11 @@ import (
"fmt"
"net/http"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetThread get notification by ID
diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go
index 879f484cce..2b8cc8c112 100644
--- a/routers/api/v1/notify/user.go
+++ b/routers/api/v1/notify/user.go
@@ -7,11 +7,11 @@ import (
"net/http"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListNotifications list users's notification threads
diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go
index 390d074ad7..0d8550a019 100644
--- a/routers/api/v1/org/action.go
+++ b/routers/api/v1/org/action.go
@@ -7,17 +7,17 @@ import (
"errors"
"net/http"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
- 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/shared"
- "code.gitea.io/gitea/routers/api/v1/utils"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
- secret_service "code.gitea.io/gitea/services/secrets"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ secret_model "forgejo.org/models/secret"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/routers/api/v1/utils"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
+ secret_service "forgejo.org/services/secrets"
)
// ListActionsSecrets list an organization's actions secrets
@@ -189,6 +189,31 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
}
+// SearchActionRunJobs return a list of actions jobs filtered by the provided parameters
+func (Action) SearchActionRunJobs(ctx *context.APIContext) {
+ // swagger:operation GET /orgs/{org}/actions/runners/jobs organization orgSearchRunJobs
+ // ---
+ // summary: Search for organization's action jobs according filter conditions
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // - name: labels
+ // in: query
+ // description: a comma separated list of run job labels to search for
+ // type: string
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RunJobList"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ shared.GetActionRunJobs(ctx, ctx.Org.Organization.ID, 0)
+}
+
// ListVariables list org-level variables
func (Action) ListVariables(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go
index f11eb6c1cd..824a9f3495 100644
--- a/routers/api/v1/org/avatar.go
+++ b/routers/api/v1/org/avatar.go
@@ -7,10 +7,10 @@ import (
"encoding/base64"
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
// UpdateAvatarupdates the Avatar of an Organisation
diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go
index c1dc0519ea..2877acd3c7 100644
--- a/routers/api/v1/org/hook.go
+++ b/routers/api/v1/org/hook.go
@@ -6,11 +6,11 @@ package org
import (
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
// ListHooks list an organziation's webhooks
diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go
index b5ec54ccf4..172d531229 100644
--- a/routers/api/v1/org/label.go
+++ b/routers/api/v1/org/label.go
@@ -8,13 +8,13 @@ import (
"strconv"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/label"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/label"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListLabels list all the labels of an organization
diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go
index 0895c53328..c6e67b2ab9 100644
--- a/routers/api/v1/org/member.go
+++ b/routers/api/v1/org/member.go
@@ -7,14 +7,14 @@ import (
"net/http"
"net/url"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/user"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models"
+ "forgejo.org/models/organization"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/user"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// listMembers list an organization's members
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index 6759360def..87bc27be63 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -8,21 +8,21 @@ import (
"fmt"
"net/http"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/user"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/org"
- user_service "code.gitea.io/gitea/services/user"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/user"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/org"
+ user_service "forgejo.org/services/user"
)
func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
@@ -320,6 +320,44 @@ func Get(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, org)
}
+func Rename(ctx *context.APIContext) {
+ // swagger:operation POST /orgs/{org}/rename organization renameOrg
+ // ---
+ // summary: Rename an organization
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: existing org name
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // required: true
+ // schema:
+ // "$ref": "#/definitions/RenameOrgOption"
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ form := web.GetForm(ctx).(*api.RenameOrgOption)
+ orgUser := ctx.Org.Organization.AsUser()
+ if err := user_service.RenameUser(ctx, orgUser, form.NewName); err != nil {
+ if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
+ ctx.Error(http.StatusUnprocessableEntity, "RenameOrg", err)
+ } else {
+ ctx.ServerError("RenameOrg", err)
+ }
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
+
// Edit change an organization's information
func Edit(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org} organization orgEdit
@@ -360,7 +398,7 @@ func Edit(ctx *context.APIContext) {
ctx.Org.Organization.Email = ""
} else {
if err := user_service.ReplacePrimaryEmailAddress(ctx, ctx.Org.Organization.AsUser(), *form.Email); err != nil {
- if validation.IsErrEmailInvalid(err) || validation.IsErrEmailCharIsNotSupported(err) {
+ if validation.IsErrEmailInvalid(err) {
ctx.Error(http.StatusUnprocessableEntity, "ReplacePrimaryEmailAddress", err)
} else {
ctx.Error(http.StatusInternalServerError, "ReplacePrimaryEmailAddress", err)
diff --git a/routers/api/v1/org/quota.go b/routers/api/v1/org/quota.go
index 57c41f5ce3..f4f89b0aaf 100644
--- a/routers/api/v1/org/quota.go
+++ b/routers/api/v1/org/quota.go
@@ -4,8 +4,8 @@
package org
import (
- "code.gitea.io/gitea/routers/api/v1/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/services/context"
)
// GetQuota returns the quota information for a given organization
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index bf28d54c42..680cc19ce8 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -8,22 +8,22 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/user"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- org_service "code.gitea.io/gitea/services/org"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/user"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ org_service "forgejo.org/services/org"
+ repo_service "forgejo.org/services/repository"
)
// ListTeams list all the teams of an organization
diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go
index b38aa13167..03057c4feb 100644
--- a/routers/api/v1/packages/package.go
+++ b/routers/api/v1/packages/package.go
@@ -4,15 +4,18 @@
package packages
import (
+ "errors"
"net/http"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/optional"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ packages_service "forgejo.org/services/packages"
)
// ListPackages gets all packages of an owner
@@ -213,3 +216,122 @@ func ListPackageFiles(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, apiPackageFiles)
}
+
+// LinkPackage sets a repository link for a package
+func LinkPackage(ctx *context.APIContext) {
+ // swagger:operation POST /packages/{owner}/{type}/{name}/-/link/{repo_name} package linkPackage
+ // ---
+ // summary: Link a package to a repository
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // - name: repo_name
+ // in: path
+ // description: name of the repository to link.
+ // type: string
+ // required: true
+ // responses:
+ // "201":
+ // "$ref": "#/responses/empty"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParamRaw("type")), ctx.PathParamRaw("name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.Error(http.StatusNotFound, "GetPackageByName", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetPackageByName", err)
+ }
+ return
+ }
+
+ repo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ctx.PathParamRaw("repo_name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.Error(http.StatusNotFound, "GetRepositoryByName", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err)
+ }
+ return
+ }
+
+ err = packages_service.LinkToRepository(ctx, pkg, repo, ctx.Doer)
+ if err != nil {
+ switch {
+ case errors.Is(err, util.ErrInvalidArgument):
+ ctx.Error(http.StatusBadRequest, "LinkToRepository", err)
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.Error(http.StatusForbidden, "LinkToRepository", err)
+ default:
+ ctx.Error(http.StatusInternalServerError, "LinkToRepository", err)
+ }
+ return
+ }
+ ctx.Status(http.StatusCreated)
+}
+
+// UnlinkPackage sets a repository link for a package
+func UnlinkPackage(ctx *context.APIContext) {
+ // swagger:operation POST /packages/{owner}/{type}/{name}/-/unlink package unlinkPackage
+ // ---
+ // summary: Unlink a package from a repository
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // responses:
+ // "201":
+ // "$ref": "#/responses/empty"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParamRaw("type")), ctx.PathParamRaw("name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.Error(http.StatusNotFound, "GetPackageByName", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetPackageByName", err)
+ }
+ return
+ }
+
+ err = packages_service.UnlinkFromRepository(ctx, pkg, ctx.Doer)
+ if err != nil {
+ switch {
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.Error(http.StatusForbidden, "UnlinkFromRepository", err)
+ case errors.Is(err, util.ErrInvalidArgument):
+ ctx.Error(http.StatusBadRequest, "UnlinkFromRepository", err)
+ default:
+ ctx.Error(http.StatusInternalServerError, "UnlinkFromRepository", err)
+ }
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go
index b109497d19..dbc4933de6 100644
--- a/routers/api/v1/repo/action.go
+++ b/routers/api/v1/repo/action.go
@@ -5,20 +5,21 @@ package repo
import (
"errors"
+ "fmt"
"net/http"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
- 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/shared"
- "code.gitea.io/gitea/routers/api/v1/utils"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- secret_service "code.gitea.io/gitea/services/secrets"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ secret_model "forgejo.org/models/secret"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/routers/api/v1/utils"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ secret_service "forgejo.org/services/secrets"
)
// ListActionsSecrets list an repo's actions secrets
@@ -507,6 +508,36 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
}
+// SearchActionRunJobs return a list of actions jobs filtered by the provided parameters
+func (Action) SearchActionRunJobs(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runners/jobs repository repoSearchRunJobs
+ // ---
+ // summary: Search for repository's action jobs according filter conditions
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: labels
+ // in: query
+ // description: a comma separated list of run job labels to search for
+ // type: string
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RunJobList"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ shared.GetActionRunJobs(ctx, 0, ctx.Repo.Repository.ID)
+}
+
var _ actions_service.API = new(Action)
// Action implements actions_service.API
@@ -610,6 +641,8 @@ func DispatchWorkflow(ctx *context.APIContext) {
// schema:
// "$ref": "#/definitions/DispatchWorkflowOption"
// responses:
+ // "201":
+ // "$ref": "#/responses/DispatchWorkflowRun"
// "204":
// "$ref": "#/responses/empty"
// "404":
@@ -640,7 +673,8 @@ func DispatchWorkflow(ctx *context.APIContext) {
return opt.Inputs[key]
}
- if err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer); err != nil {
+ run, jobs, err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer)
+ if err != nil {
if actions_service.IsInputRequiredErr(err) {
ctx.Error(http.StatusBadRequest, "workflow.Dispatch", err)
} else {
@@ -649,5 +683,173 @@ func DispatchWorkflow(ctx *context.APIContext) {
return
}
- ctx.JSON(http.StatusNoContent, nil)
+ workflowRun := &api.DispatchWorkflowRun{
+ ID: run.ID,
+ RunNumber: run.Index,
+ Jobs: jobs,
+ }
+
+ if opt.ReturnRunInfo {
+ ctx.JSON(http.StatusCreated, workflowRun)
+ } else {
+ ctx.JSON(http.StatusNoContent, nil)
+ }
+}
+
+// ListActionRuns return a filtered list of ActionRun
+func ListActionRuns(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs repository ListActionRuns
+ // ---
+ // summary: List a repository's action runs
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results, default maximum page size is 50
+ // type: integer
+ // - name: event
+ // in: query
+ // description: Returns workflow run triggered by the specified events. For example, `push`, `pull_request` or `workflow_dispatch`.
+ // type: array
+ // items:
+ // type: string
+ // - name: status
+ // in: query
+ // description: |
+ // Returns workflow runs with the check run status or conclusion that is specified. For example, a conclusion can be success or a status can be in_progress. Only Forgejo Actions can set a status of waiting, pending, or requested.
+ // type: array
+ // items:
+ // type: string
+ // enum: [unknown, waiting, running, success, failure, cancelled, skipped, blocked]
+ // - name: run_number
+ // in: query
+ // description: |
+ // Returns the workflow run associated with the run number.
+ // type: integer
+ // format: int64
+ // - name: head_sha
+ // in: query
+ // description: Only returns workflow runs that are associated with the specified head_sha.
+ // type: string
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActionRunList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ statusStrs := ctx.FormStrings("status")
+ statuses := make([]actions_model.Status, len(statusStrs))
+ for i, s := range statusStrs {
+ if status, exists := actions_model.StatusFromString(s); exists {
+ statuses[i] = status
+ } else {
+ ctx.Error(http.StatusBadRequest, "StatusFromString", fmt.Sprintf("unknown status: %s", s))
+ return
+ }
+ }
+
+ runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, &actions_model.FindRunJobOptions{
+ ListOptions: utils.GetListOptions(ctx),
+ OwnerID: ctx.Repo.Owner.ID,
+ RepoID: ctx.Repo.Repository.ID,
+ Events: ctx.FormStrings("event"),
+ Statuses: statuses,
+ RunNumber: ctx.FormInt64("run_number"),
+ CommitSHA: ctx.FormString("head_sha"),
+ })
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "ListActionRuns", err)
+ return
+ }
+
+ res := new(api.ListActionRunResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionRun, len(runs))
+ for i, r := range runs {
+ if err := r.LoadAttributes(ctx); err != nil {
+ ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ return
+ }
+ cr := convert.ToActionRun(ctx, r, ctx.Doer)
+ res.Entries[i] = cr
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
+
+// GetActionRun get one action instance
+func GetActionRun(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run_id} repository ActionRun
+ // ---
+ // summary: Get an action run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: run_id
+ // in: path
+ // description: id of the action run
+ // type: integer
+ // format: int64
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActionRun"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ run, err := actions_model.GetRunByID(ctx, ctx.ParamsInt64(":run_id"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.Error(http.StatusNotFound, "GetRunById", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetRunByID", err)
+ }
+ return
+ }
+
+ // Action runs lives in its own table, therefore we check that the
+ // run with the requested ID is owned by the repository
+ if ctx.Repo.Repository.ID != run.RepoID {
+ ctx.Error(http.StatusNotFound, "GetRunById", util.ErrNotExist)
+ return
+ }
+
+ if err := run.LoadAttributes(ctx); err != nil {
+ ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, convert.ToActionRun(ctx, run, ctx.Doer))
}
diff --git a/routers/api/v1/repo/avatar.go b/routers/api/v1/repo/avatar.go
index 698337ffd2..84aafe764d 100644
--- a/routers/api/v1/repo/avatar.go
+++ b/routers/api/v1/repo/avatar.go
@@ -7,10 +7,10 @@ import (
"encoding/base64"
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
// UpdateVatar updates the Avatar of an Repo
diff --git a/routers/api/v1/repo/blob.go b/routers/api/v1/repo/blob.go
index 3b116666ea..63baec2025 100644
--- a/routers/api/v1/repo/blob.go
+++ b/routers/api/v1/repo/blob.go
@@ -5,11 +5,54 @@ package repo
import (
"net/http"
+ "strings"
- "code.gitea.io/gitea/services/context"
- files_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/services/context"
+ files_service "forgejo.org/services/repository/files"
)
+// GetBlobs gets multiple blobs of a repository.
+func GetBlobs(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/git/blobs repository GetBlobs
+ // ---
+ // summary: Gets multiplbe blobs of a repository.
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: shas
+ // in: query
+ // description: a comma separated list of blob-sha (mind the overall URL-length limit of ~2,083 chars)
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/GitBlobList"
+ // "400":
+ // "$ref": "#/responses/error"
+
+ shas := ctx.FormString("shas")
+ if len(shas) == 0 {
+ ctx.Error(http.StatusBadRequest, "", "shas not provided")
+ return
+ }
+
+ if blobs, err := files_service.GetBlobsBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, strings.Split(shas, ",")); err != nil {
+ ctx.Error(http.StatusBadRequest, "", err)
+ } else {
+ ctx.JSON(http.StatusOK, blobs)
+ }
+}
+
// GetBlob get the blob of a repository file.
func GetBlob(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/git/blobs/{sha} repository GetBlob
@@ -30,12 +73,12 @@ func GetBlob(ctx *context.APIContext) {
// required: true
// - name: sha
// in: path
- // description: sha of the commit
+ // description: sha of the blob to retrieve
// type: string
// required: true
// responses:
// "200":
- // "$ref": "#/responses/GitBlobResponse"
+ // "$ref": "#/responses/GitBlob"
// "400":
// "$ref": "#/responses/error"
// "404":
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 3ca97f7770..7c9593d625 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -6,25 +6,24 @@ package repo
import (
"errors"
- "fmt"
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ pull_service "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
)
// GetBranch get a branch of a repository
@@ -151,7 +150,7 @@ func DeleteBranch(ctx *context.APIContext) {
}
if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository"))
+ ctx.Error(http.StatusForbidden, "IsMirrored", errors.New("can not delete branch of an mirror repository"))
return
}
@@ -160,9 +159,9 @@ func DeleteBranch(ctx *context.APIContext) {
case git.IsErrBranchNotExist(err):
ctx.NotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
- ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
+ ctx.Error(http.StatusForbidden, "DefaultBranch", errors.New("can not delete default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
- ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
+ ctx.Error(http.StatusForbidden, "IsProtectedBranch", errors.New("branch protected"))
default:
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
}
@@ -231,9 +230,9 @@ func CreateBranch(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
}
- } else if len(opt.OldBranchName) > 0 { //nolint
- if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint
- oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint
+ } else if len(opt.OldBranchName) > 0 { //nolint:staticcheck
+ if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint:staticcheck
+ oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint:staticcheck
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
return
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index a43a21a88e..3ef7721ef5 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -8,18 +8,18 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- repo_module "code.gitea.io/gitea/modules/repository"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ repo_module "forgejo.org/modules/repository"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ repo_service "forgejo.org/services/repository"
)
// ListCollaborators list a repository's collaborators
@@ -82,6 +82,7 @@ func IsCollaborator(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/collaborators/{collaborator} repository repoCheckCollaborator
// ---
// summary: Check if a user is a collaborator of a repository
+ // description: If the user is a collaborator, return 204. If the user is not a collaborator, return 404.
// produces:
// - application/json
// parameters:
@@ -281,11 +282,6 @@ func GetRepoPermissions(ctx *context.APIContext) {
// "403":
// "$ref": "#/responses/forbidden"
- if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.Params(":collaborator") && !ctx.IsUserRepoAdmin() {
- ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
- return
- }
-
collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
@@ -296,6 +292,15 @@ func GetRepoPermissions(ctx *context.APIContext) {
return
}
+ // Only allow the request in any of the following situations:
+ // - The user is the instance admin.
+ // - The user is the repository admin.
+ // - The user is querying the permissions of themselves.
+ if !ctx.IsUserSiteAdmin() && ctx.Doer.ID != collaborator.ID && !ctx.IsUserRepoAdmin() {
+ ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
+ return
+ }
+
permission, err := access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, collaborator)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index c5e8cf99eb..4221e59f17 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -10,14 +10,14 @@ import (
"net/http"
"strconv"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetSingleCommit get a commit via sha
diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go
index 429145c714..7fc59ea171 100644
--- a/routers/api/v1/repo/compare.go
+++ b/routers/api/v1/repo/compare.go
@@ -7,11 +7,11 @@ import (
"net/http"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/gitrepo"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/gitrepo"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// CompareDiff compare two branches or commits
@@ -64,7 +64,7 @@ func CompareDiff(ctx *context.APIContext) {
}
}
- _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
+ headRepository, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
Base: infos[0],
Head: infos[1],
})
@@ -77,9 +77,10 @@ func CompareDiff(ctx *context.APIContext) {
files := ctx.FormString("files") == "" || ctx.FormBool("files")
apiCommits := make([]*api.Commit, 0, len(ci.Commits))
+ apiFiles := []*api.CommitAffectedFiles{}
userCache := make(map[string]*user_model.User)
for i := 0; i < len(ci.Commits); i++ {
- apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache,
+ apiCommit, err := convert.ToCommit(ctx, headRepository, headGitRepo, ci.Commits[i], userCache,
convert.ToCommitOptions{
Stat: true,
Verification: verification,
@@ -90,10 +91,12 @@ func CompareDiff(ctx *context.APIContext) {
return
}
apiCommits = append(apiCommits, apiCommit)
+ apiFiles = append(apiFiles, apiCommit.Files...)
}
ctx.JSON(http.StatusOK, &api.Compare{
TotalCommits: len(ci.Commits),
Commits: apiCommits,
+ Files: apiFiles,
})
}
diff --git a/routers/api/v1/repo/download.go b/routers/api/v1/repo/download.go
index 3a0401a5b0..86910123e6 100644
--- a/routers/api/v1/repo/download.go
+++ b/routers/api/v1/repo/download.go
@@ -7,10 +7,10 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/services/context"
- archiver_service "code.gitea.io/gitea/services/repository/archiver"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/services/context"
+ archiver_service "forgejo.org/services/repository/archiver"
)
func DownloadArchive(ctx *context.APIContext) {
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index 2cea538b72..549fe9fae0 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -11,27 +11,26 @@ import (
"fmt"
"io"
"net/http"
- "path"
"strings"
"time"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
- archiver_service "code.gitea.io/gitea/services/repository/archiver"
- files_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
+ archiver_service "forgejo.org/services/repository/archiver"
+ files_service "forgejo.org/services/repository/files"
)
const (
@@ -247,19 +246,14 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return nil, nil, nil
}
- info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
+ latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err)
+ ctx.Error(http.StatusInternalServerError, "GetTreePathLatestCommit", err)
return nil, nil, nil
}
+ when := &latestCommit.Committer.When
- if len(info) == 1 {
- // Not Modified
- lastModified = &info[0].Commit.Committer.When
- }
- blob = entry.Blob()
-
- return blob, entry, lastModified
+ return entry.Blob(), entry, when
}
// GetArchive get archive of a repository
@@ -443,7 +437,7 @@ func canWriteFiles(ctx *context.APIContext, branch string) bool {
// canReadFiles returns true if repository is readable and user has proper access level.
func canReadFiles(r *context.Repository) bool {
- return r.Permission.CanRead(unit.TypeCode)
+ return r.CanRead(unit.TypeCode)
}
func base64Reader(s string) (io.ReadSeeker, error) {
@@ -486,6 +480,8 @@ func ChangeFiles(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -590,6 +586,8 @@ func CreateFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -690,6 +688,8 @@ func UpdateFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -698,7 +698,7 @@ func UpdateFile(ctx *context.APIContext) {
// "$ref": "#/responses/repoArchivedError"
apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions)
if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
+ ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", errors.New("repo is empty"))
return
}
@@ -763,11 +763,19 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
ctx.Error(http.StatusForbidden, "Access", err)
return
}
- if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
- models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
+ if git_model.IsErrBranchAlreadyExists(err) ||
+ models.IsErrFilenameInvalid(err) ||
+ models.IsErrSHAOrCommitIDNotProvided(err) ||
+ models.IsErrFilePathInvalid(err) ||
+ models.IsErrRepoFileAlreadyExists(err) {
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
return
}
+ if models.IsErrCommitIDDoesNotMatch(err) ||
+ models.IsErrSHADoesNotMatch(err) {
+ ctx.Error(http.StatusConflict, "Conflict", err)
+ return
+ }
if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
return
diff --git a/routers/api/v1/repo/flags.go b/routers/api/v1/repo/flags.go
index ac5cb2e6d6..46af528f0f 100644
--- a/routers/api/v1/repo/flags.go
+++ b/routers/api/v1/repo/flags.go
@@ -6,9 +6,9 @@ package repo
import (
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
)
func ListFlags(ctx *context.APIContext) {
diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go
index c9dc9681c9..edb85cf54f 100644
--- a/routers/api/v1/repo/fork.go
+++ b/routers/api/v1/repo/fork.go
@@ -9,19 +9,19 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- 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"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ repo_service "forgejo.org/services/repository"
)
// ListForks list a repository's forks
diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go
index 26ae84d08d..31957c8b4d 100644
--- a/routers/api/v1/repo/git_hook.go
+++ b/routers/api/v1/repo/git_hook.go
@@ -6,11 +6,11 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListGitHooks list all Git hooks of a repository
diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go
index 54da5eeaa7..b2e52ad95d 100644
--- a/routers/api/v1/repo/git_ref.go
+++ b/routers/api/v1/repo/git_ref.go
@@ -7,10 +7,10 @@ import (
"net/http"
"net/url"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
)
// GetGitAllRefs get ref or an list all the refs of a repository
diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go
index ffd2313591..5d277604b8 100644
--- a/routers/api/v1/repo/hook.go
+++ b/routers/api/v1/repo/hook.go
@@ -7,19 +7,19 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ webhook_service "forgejo.org/services/webhook"
)
// ListHooks list all hooks of a repository
diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go
index a8065e4a60..52d2245f03 100644
--- a/routers/api/v1/repo/hook_test.go
+++ b/routers/api/v1/repo/hook_test.go
@@ -7,9 +7,9 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/unittest"
+ "forgejo.org/models/webhook"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
@@ -25,7 +25,7 @@ func TestTestHook(t *testing.T) {
defer ctx.Repo.GitRepo.Close()
contexttest.LoadRepoCommit(t, ctx)
TestHook(ctx)
- assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
+ assert.Equal(t, http.StatusNoContent, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
HookID: 1,
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 6221681875..442e109843 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -12,23 +12,23 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// SearchIssues searches for issues across the repositories that the user has access to
@@ -121,6 +121,12 @@ func SearchIssues(ctx *context.APIContext) {
// description: Number of items per page
// type: integer
// minimum: 0
+ // - name: sort
+ // in: query
+ // description: Type of sort
+ // type: string
+ // enum: [relevance, latest, oldest, recentupdate, leastupdate, mostcomment, leastcomment, nearduedate, farduedate]
+ // default: latest
// responses:
// "200":
// "$ref": "#/responses/IssueList"
@@ -276,7 +282,7 @@ func SearchIssues(ctx *context.APIContext) {
IsClosed: isClosed,
IncludedAnyLabelIDs: includedAnyLabels,
MilestoneIDs: includedMilestones,
- SortBy: issue_indexer.SortByCreatedDesc,
+ SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc),
}
if since != 0 {
@@ -305,9 +311,10 @@ func SearchIssues(ctx *context.APIContext) {
}
}
- // FIXME: It's unsupported to sort by priority repo when searching by indexer,
- // it's indeed an regression, but I think it is worth to support filtering by indexer first.
- _ = ctx.FormInt64("priority_repo_id")
+ priorityRepoID := ctx.FormInt64("priority_repo_id")
+ if priorityRepoID > 0 {
+ searchOpt.PriorityRepoID = optional.Some(priorityRepoID)
+ }
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
@@ -397,6 +404,12 @@ func ListIssues(ctx *context.APIContext) {
// in: query
// description: page size of results
// type: integer
+ // - name: sort
+ // in: query
+ // description: Type of sort
+ // type: string
+ // enum: [relevance, latest, oldest, recentupdate, leastupdate, mostcomment, leastcomment, nearduedate, farduedate]
+ // default: latest
// responses:
// "200":
// "$ref": "#/responses/IssueList"
@@ -510,7 +523,7 @@ func ListIssues(ctx *context.APIContext) {
RepoIDs: []int64{ctx.Repo.Repository.ID},
IsPull: isPull,
IsClosed: isClosed,
- SortBy: issue_indexer.SortByCreatedDesc,
+ SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc),
}
if since != 0 {
searchOpt.UpdatedAfterUnix = optional.Some(since)
diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index a972ab0374..0cb1875af1 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -7,17 +7,17 @@ import (
"net/http"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/attachment"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/attachment"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// GetIssueAttachment gets a single attachment of the issue
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index 1ff755c058..1b98c154c8 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -9,18 +9,18 @@ import (
"errors"
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// ListIssueComments list all the comments of an issue
@@ -62,6 +62,10 @@ func ListIssueComments(ctx *context.APIContext) {
// "$ref": "#/responses/CommentList"
// "404":
// "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
@@ -70,6 +74,10 @@ func ListIssueComments(ctx *context.APIContext) {
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
+ if issues_model.IsErrIssueNotExist(err) {
+ ctx.NotFound("IsErrIssueNotExist", err)
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
return
}
@@ -166,6 +174,10 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
// "$ref": "#/responses/TimelineList"
// "404":
// "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
@@ -174,6 +186,10 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
+ if issues_model.IsErrIssueNotExist(err) {
+ ctx.NotFound("IsErrIssueNotExist", err)
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
return
}
@@ -271,6 +287,10 @@ func ListRepoIssueComments(ctx *context.APIContext) {
// "$ref": "#/responses/CommentList"
// "404":
// "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
@@ -378,9 +398,16 @@ func CreateIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "423":
// "$ref": "#/responses/repoArchivedError"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
+
form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
+ if issues_model.IsErrIssueNotExist(err) {
+ ctx.NotFound("IsErrIssueNotExist", err)
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
return
}
@@ -449,6 +476,8 @@ func GetIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
comment := ctx.Comment
@@ -511,6 +540,9 @@ func EditIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "423":
// "$ref": "#/responses/repoArchivedError"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
+
form := web.GetForm(ctx).(*api.EditIssueCommentOption)
editIssueComment(ctx, *form)
}
@@ -560,6 +592,8 @@ func EditIssueCommentDeprecated(ctx *context.APIContext) {
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
form := web.GetForm(ctx).(*api.EditIssueCommentOption)
editIssueComment(ctx, *form)
@@ -626,8 +660,8 @@ func DeleteIssueComment(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
- // "404":
- // "$ref": "#/responses/notFound"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
deleteIssueComment(ctx, issues_model.CommentTypeComment)
}
@@ -665,8 +699,8 @@ func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
- // "404":
- // "$ref": "#/responses/notFound"
+ // "500":
+ // "$ref": "#/responses/internalServerError"
deleteIssueComment(ctx, issues_model.CommentTypeComment)
}
diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go
index c45e2ebe89..9edc9a3cb1 100644
--- a/routers/api/v1/repo/issue_comment_attachment.go
+++ b/routers/api/v1/repo/issue_comment_attachment.go
@@ -7,17 +7,17 @@ import (
"net/http"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/attachment"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/attachment"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// GetIssueCommentAttachment gets a single attachment of the comment
diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go
index c40e92c01b..7bf1d3c67c 100644
--- a/routers/api/v1/repo/issue_dependency.go
+++ b/routers/api/v1/repo/issue_dependency.go
@@ -7,15 +7,15 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetIssueDependencies list an issue's dependencies
@@ -72,7 +72,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
}
// 1. We must be able to read this issue
- if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
+ if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
ctx.NotFound()
return
}
@@ -88,7 +88,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
limit = setting.API.MaxResponseItems
}
- canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)
+ canWrite := ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
blockerIssues := make([]*issues_model.Issue, 0, limit)
@@ -123,7 +123,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
}
// check permission
- if !perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) {
+ if !perm.CanReadIssuesOrPulls(blocker.IsPull) {
if !canWrite {
hiddenBlocker := &issues_model.DependencyInfo{
Issue: issues_model.Issue{
@@ -134,19 +134,19 @@ func GetIssueDependencies(ctx *context.APIContext) {
} else {
confidentialBlocker := &issues_model.DependencyInfo{
Issue: issues_model.Issue{
- RepoID: blocker.Issue.RepoID,
+ RepoID: blocker.RepoID,
Index: blocker.Index,
Title: blocker.Title,
IsClosed: blocker.IsClosed,
IsPull: blocker.IsPull,
},
Repository: repo_model.Repository{
- ID: blocker.Issue.Repo.ID,
- Name: blocker.Issue.Repo.Name,
- OwnerName: blocker.Issue.Repo.OwnerName,
+ ID: blocker.Repo.ID,
+ Name: blocker.Repo.Name,
+ OwnerName: blocker.Repo.OwnerName,
},
}
- confidentialBlocker.Issue.Repo = &confidentialBlocker.Repository
+ confidentialBlocker.Repo = &confidentialBlocker.Repository
blocker = confidentialBlocker
}
}
@@ -323,7 +323,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
return
}
- if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
+ if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
ctx.NotFound()
return
}
@@ -373,11 +373,11 @@ func GetIssueBlocks(ctx *context.APIContext) {
repoPerms[depMeta.RepoID] = perm
}
- if !perm.CanReadIssuesOrPulls(depMeta.Issue.IsPull) {
+ if !perm.CanReadIssuesOrPulls(depMeta.IsPull) {
continue
}
- depMeta.Issue.Repo = &depMeta.Repository
+ depMeta.Repo = &depMeta.Repository
issues = append(issues, &depMeta.Issue)
}
diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go
index 6458fbf514..3b2935305c 100644
--- a/routers/api/v1/repo/issue_label.go
+++ b/routers/api/v1/repo/issue_label.go
@@ -5,16 +5,16 @@
package repo
import (
- "fmt"
+ "errors"
"net/http"
"reflect"
- issues_model "code.gitea.io/gitea/models/issues"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ issues_model "forgejo.org/models/issues"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// ListIssueLabels list all the labels of an issue
@@ -352,12 +352,12 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
labelNames = append(labelNames, rv.String())
default:
ctx.Error(http.StatusBadRequest, "InvalidLabel", "a label must be an integer or a string")
- return nil, nil, fmt.Errorf("invalid label")
+ return nil, nil, errors.New("invalid label")
}
}
if len(labelIDs) > 0 && len(labelNames) > 0 {
ctx.Error(http.StatusBadRequest, "InvalidLabels", "labels should be an array of strings or integers")
- return nil, nil, fmt.Errorf("invalid labels")
+ return nil, nil, errors.New("invalid labels")
}
if len(labelNames) > 0 {
repoLabelIDs, err := issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, labelNames)
diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go
index af3e06332a..84079ed452 100644
--- a/routers/api/v1/repo/issue_pin.go
+++ b/routers/api/v1/repo/issue_pin.go
@@ -6,10 +6,10 @@ package repo
import (
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// PinIssue pins a issue
diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go
index c395255c13..2d6218bf46 100644
--- a/routers/api/v1/repo/issue_reaction.go
+++ b/routers/api/v1/repo/issue_reaction.go
@@ -7,14 +7,14 @@ import (
"errors"
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
)
// GetIssueCommentReactions list reactions of a comment from an issue
diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go
index dd61967ed0..83bcabbe15 100644
--- a/routers/api/v1/repo/issue_stopwatch.go
+++ b/routers/api/v1/repo/issue_stopwatch.go
@@ -7,10 +7,10 @@ import (
"errors"
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// StartIssueStopwatch creates a stopwatch for the given issue.
diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go
index 6b29218575..33654dc136 100644
--- a/routers/api/v1/repo/issue_subscription.go
+++ b/routers/api/v1/repo/issue_subscription.go
@@ -7,12 +7,12 @@ import (
"fmt"
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// AddIssueSubscription Subscribe user to issue
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index 3d8abfa5f3..61875b577c 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -4,19 +4,20 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListTrackedTimes list all the tracked times of an issue
@@ -116,7 +117,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
if opts.UserID == 0 {
opts.UserID = ctx.Doer.ID
} else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights"))
return
}
}
@@ -437,7 +438,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
}
if !ctx.IsUserRepoAdmin() && !ctx.Doer.IsAdmin && ctx.Doer.ID != user.ID {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights"))
return
}
@@ -545,7 +546,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
if opts.UserID == 0 {
opts.UserID = ctx.Doer.ID
} else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights"))
return
}
}
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 88444a2625..2abf95a189 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -10,18 +10,18 @@ import (
"net/http"
"net/url"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// appendPrivateInformation appends the owner and key type information to api.PublicKey
diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go
index b6eb51fd20..bf722ace34 100644
--- a/routers/api/v1/repo/label.go
+++ b/routers/api/v1/repo/label.go
@@ -8,13 +8,13 @@ import (
"net/http"
"strconv"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/label"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/label"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListLabels list all the labels of a repository
diff --git a/routers/api/v1/repo/language.go b/routers/api/v1/repo/language.go
index f1d5bbe45f..498aac3447 100644
--- a/routers/api/v1/repo/language.go
+++ b/routers/api/v1/repo/language.go
@@ -8,9 +8,9 @@ import (
"net/http"
"strconv"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
type languageResponse []*repo_model.LanguageStat
diff --git a/routers/api/v1/repo/main_test.go b/routers/api/v1/repo/main_test.go
index 451f34d72f..a3655fb76b 100644
--- a/routers/api/v1/repo/main_test.go
+++ b/routers/api/v1/repo/main_test.go
@@ -6,9 +6,9 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ webhook_service "forgejo.org/services/webhook"
)
func TestMain(m *testing.M) {
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 0991723d47..a848a950db 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -10,28 +10,28 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "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/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/migrations"
- notify_service "code.gitea.io/gitea/services/notify"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/migrations"
+ notify_service "forgejo.org/services/notify"
+ repo_service "forgejo.org/services/repository"
)
// Migrate migrate remote git repository to gitea
@@ -123,12 +123,12 @@ func Migrate(ctx *context.APIContext) {
gitServiceType := convert.ToGitServiceType(form.Service)
if form.Mirror && setting.Mirror.DisableNewPull {
- ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", fmt.Errorf("the site administrator has disabled the creation of new pull mirrors"))
+ ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", errors.New("the site administrator has disabled the creation of new pull mirrors"))
return
}
if setting.Repository.DisableMigrations {
- ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", fmt.Errorf("the site administrator has disabled migrations"))
+ ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", errors.New("the site administrator has disabled migrations"))
return
}
@@ -218,6 +218,17 @@ func Migrate(ctx *context.APIContext) {
return
}
+ if opts.Releases || opts.Wiki {
+ repoOpt := api.EditRepoOption{
+ HasReleases: &opts.Releases,
+ HasWiki: &opts.Wiki,
+ }
+
+ if err = updateRepoUnits(ctx, repoOwner.Name, repo, repoOpt); err != nil {
+ log.Error("Failed to update units on %s/%s repo. %w", repoOwner.Name, form.RepoName, err)
+ }
+ }
+
log.Trace("Repository migrated: %s/%s", repoOwner.Name, form.RepoName)
ctx.JSON(http.StatusCreated, convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeAdmin}))
}
diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go
index b9534016e4..7aa9881bc4 100644
--- a/routers/api/v1/repo/milestone.go
+++ b/routers/api/v1/repo/milestone.go
@@ -9,15 +9,15 @@ import (
"strconv"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/optional"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListMilestones list milestones for a repository
diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go
index 11b026abb7..bc48c6acb7 100644
--- a/routers/api/v1/repo/mirror.go
+++ b/routers/api/v1/repo/mirror.go
@@ -9,21 +9,21 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/git"
- "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"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/migrations"
- mirror_service "code.gitea.io/gitea/services/mirror"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/migrations"
+ mirror_service "forgejo.org/services/mirror"
)
// MirrorSync adds a mirrored repository to the sync queue
diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go
index 9ed78ce80f..f3ceeaeacf 100644
--- a/routers/api/v1/repo/notes.go
+++ b/routers/api/v1/repo/notes.go
@@ -4,14 +4,15 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetNote Get a note corresponding to a single commit from a repository
@@ -63,7 +64,7 @@ func GetNote(ctx *context.APIContext) {
func getNote(ctx *context.APIContext, identifier string) {
if ctx.Repo.GitRepo == nil {
- ctx.InternalServerError(fmt.Errorf("no open git repo"))
+ ctx.InternalServerError(errors.New("no open git repo"))
return
}
diff --git a/routers/api/v1/repo/patch.go b/routers/api/v1/repo/patch.go
index 27c5c17dce..6f35891627 100644
--- a/routers/api/v1/repo/patch.go
+++ b/routers/api/v1/repo/patch.go
@@ -7,14 +7,14 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/repository/files"
)
// ApplyDiffPatch handles API call for applying a patch
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 1a791e8dd5..e7ff533d6a 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -12,42 +12,42 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- activities_model "code.gitea.io/gitea/models/activities"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/automerge"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/gitdiff"
- issue_service "code.gitea.io/gitea/services/issue"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ activities_model "forgejo.org/models/activities"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/automerge"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/gitdiff"
+ issue_service "forgejo.org/services/issue"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
)
// ListPullRequests returns a list of all PRs
func ListPullRequests(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/pulls repository repoListPullRequests
// ---
- // summary: List a repo's pull requests
+ // summary: List a repo's pull requests. If a pull request is selected but fails to be retrieved for any reason, it will be a null value in the list of results.
// produces:
// - application/json
// parameters:
@@ -71,7 +71,7 @@ func ListPullRequests(ctx *context.APIContext) {
// in: query
// description: Type of sort
// type: string
- // enum: [oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority]
+ // enum: [oldest, recentupdate, recentclose, leastupdate, mostcomment, leastcomment, priority]
// - name: milestone
// in: query
// description: ID of the milestone
@@ -1050,11 +1050,11 @@ func MergePullRequest(ctx *context.APIContext) {
if err := repo_service.DeleteBranchAfterMerge(ctx, ctx.Doer, pr, headRepo); err != nil {
switch {
case errors.Is(err, repo_service.ErrBranchIsDefault):
- ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("the head branch is the default branch"))
+ ctx.Error(http.StatusForbidden, "DefaultBranch", errors.New("the head branch is the default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
- ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("the head branch is protected"))
+ ctx.Error(http.StatusForbidden, "IsProtectedBranch", errors.New("the head branch is protected"))
case errors.Is(err, util.ErrPermissionDenied):
- ctx.Error(http.StatusForbidden, "HeadBranch", fmt.Errorf("insufficient permission to delete head branch"))
+ ctx.Error(http.StatusForbidden, "HeadBranch", errors.New("insufficient permission to delete head branch"))
default:
ctx.Error(http.StatusInternalServerError, "DeleteBranchAfterMerge", err)
}
@@ -1084,7 +1084,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
err error
)
- // If there is no head repository, it means pull request between same repository.
headInfos := strings.Split(form.Head, ":")
if len(headInfos) == 1 {
isSameRepo = true
@@ -1094,7 +1093,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
headUser, err = user_model.GetUserByName(ctx, headInfos[0])
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName")
+ ctx.NotFound(fmt.Errorf("the owner %s does not exist", headInfos[0]))
} else {
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
}
@@ -1104,7 +1103,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
// The head repository can also point to the same repo
isSameRepo = ctx.Repo.Owner.ID == headUser.ID
} else {
- ctx.NotFound()
+ ctx.NotFound(fmt.Errorf("the head part of {basehead} %s must contain zero or one colon (:) but contains %d", form.Head, len(headInfos)-1))
return nil, nil, nil, "", ""
}
@@ -1116,16 +1115,10 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch)
baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch)
if !baseIsCommit && !baseIsBranch && !baseIsTag {
- // Check for short SHA usage
- if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil {
- baseBranch = baseCommit.ID.String()
- } else {
- ctx.NotFound("BaseNotExist")
- return nil, nil, nil, "", ""
- }
+ ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the base repository %s/%s", baseBranch, baseRepo.Owner.Name, baseRepo.Name))
+ return nil, nil, nil, "", ""
}
- // Check if current user has fork of repository or in the same repository.
headRepo := repo_model.GetForkedRepo(ctx, headUser.ID, baseRepo.ID)
if headRepo == nil && !isSameRepo {
err := baseRepo.GetBaseRepo(ctx)
@@ -1134,13 +1127,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
return nil, nil, nil, "", ""
}
- // Check if baseRepo's base repository is the same as headUser's repository.
if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID {
log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
- ctx.NotFound("GetBaseRepo")
+ ctx.NotFound(fmt.Errorf("%[1]s does not have a fork of %[2]s/%[3]s and %[2]s/%[3]s is not a fork of a repository from %[1]s", headUser.Name, baseRepo.Owner.Name, baseRepo.Name))
return nil, nil, nil, "", ""
}
- // Assign headRepo so it can be used below.
headRepo = baseRepo.BaseRepo
}
@@ -1194,32 +1185,27 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
return nil, nil, nil, "", ""
}
- // Check if head branch is valid.
- headIsCommit := headGitRepo.IsBranchExist(headBranch)
- headIsBranch := headGitRepo.IsTagExist(headBranch)
- headIsTag := headGitRepo.IsCommitExist(baseBranch)
- if !headIsCommit && !headIsBranch && !headIsTag {
- // Check if headBranch is short sha commit hash
- if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil {
- headBranch = headCommit.ID.String()
- } else {
- headGitRepo.Close()
- ctx.NotFound("IsRefExist", nil)
- return nil, nil, nil, "", ""
- }
- }
-
baseBranchRef := baseBranch
if baseIsBranch {
baseBranchRef = git.BranchPrefix + baseBranch
} else if baseIsTag {
baseBranchRef = git.TagPrefix + baseBranch
}
+
+ // Check if head branch is valid.
+ headIsCommit := headGitRepo.IsCommitExist(headBranch)
+ headIsBranch := headGitRepo.IsBranchExist(headBranch)
+ headIsTag := headGitRepo.IsTagExist(headBranch)
+ if !headIsCommit && !headIsBranch && !headIsTag {
+ ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the head repository %s/%s", headBranch, headRepo.Owner.Name, headRepo.Name))
+ return nil, nil, nil, "", ""
+ }
+
headBranchRef := headBranch
if headIsBranch {
- headBranchRef = headBranch
+ headBranchRef = git.BranchPrefix + headBranch
} else if headIsTag {
- headBranchRef = headBranch
+ headBranchRef = git.TagPrefix + headBranch
}
compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false)
@@ -1628,7 +1614,7 @@ func GetPullRequestFiles(ctx *context.APIContext) {
maxLines := setting.Git.MaxGitDiffLines
// FIXME: If there are too many files in the repo, may cause some unpredictable issues.
- diff, err := gitdiff.GetDiff(ctx, baseGitRepo,
+ diff, _, err := gitdiff.GetDiffSimple(ctx, baseGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
AfterCommitID: endCommitID,
diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index 8fba085ff7..830a62bf54 100644
--- a/routers/api/v1/repo/pull_review.go
+++ b/routers/api/v1/repo/pull_review.go
@@ -4,22 +4,23 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/gitrepo"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- issue_service "code.gitea.io/gitea/services/issue"
- pull_service "code.gitea.io/gitea/services/pull"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/gitrepo"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ issue_service "forgejo.org/services/issue"
+ pull_service "forgejo.org/services/pull"
)
// ListPullReviews lists all reviews of a pull request
@@ -581,7 +582,7 @@ func SubmitPullReview(ctx *context.APIContext) {
}
if review.Type != issues_model.ReviewTypePending {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("only a pending review can be submitted"))
+ ctx.Error(http.StatusUnprocessableEntity, "", errors.New("only a pending review can be submitted"))
return
}
@@ -593,7 +594,7 @@ func SubmitPullReview(ctx *context.APIContext) {
// if review stay pending return
if reviewType == issues_model.ReviewTypePending {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review stay pending"))
+ ctx.Error(http.StatusUnprocessableEntity, "", errors.New("review stay pending"))
return
}
@@ -634,7 +635,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
case api.ReviewStateApproved:
// can not approve your own PR
if pr.Issue.IsPoster(ctx.Doer.ID) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("approve your own pull is not allowed"))
+ ctx.Error(http.StatusUnprocessableEntity, "", errors.New("approve your own pull is not allowed"))
return -1, true
}
reviewType = issues_model.ReviewTypeApprove
@@ -643,7 +644,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
case api.ReviewStateRequestChanges:
// can not reject your own PR
if pr.Issue.IsPoster(ctx.Doer.ID) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("reject your own pull is not allowed"))
+ ctx.Error(http.StatusUnprocessableEntity, "", errors.New("reject your own pull is not allowed"))
return -1, true
}
reviewType = issues_model.ReviewTypeReject
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 2fc5f095cb..0bf958b523 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -4,21 +4,22 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- release_service "code.gitea.io/gitea/services/release"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ release_service "forgejo.org/services/release"
)
// GetRelease get a single release of a repository
@@ -226,7 +227,7 @@ func CreateRelease(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateReleaseOption)
if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
+ ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", errors.New("repo is empty"))
return
}
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
@@ -267,7 +268,7 @@ func CreateRelease(ctx *context.APIContext) {
}
} else {
if !rel.IsTag {
- ctx.Error(http.StatusConflict, "GetRelease", "Release is has no Tag")
+ ctx.Error(http.StatusConflict, "GetRelease", "Release has no Tag")
return
}
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index d569f6e928..ba273a8d2a 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -11,15 +11,15 @@ import (
"path"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/attachment"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/convert"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/attachment"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/convert"
)
func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index f845fad53b..b27f8584bc 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -6,11 +6,11 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- releaseservice "code.gitea.io/gitea/services/release"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ releaseservice "forgejo.org/services/release"
)
// GetReleaseByTag get a single release of a repository by tag name
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 457f179f39..3d6a40e9ab 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -5,38 +5,38 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
"slices"
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/issue"
- repo_service "code.gitea.io/gitea/services/repository"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/label"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/issue"
+ repo_service "forgejo.org/services/repository"
+ wiki_service "forgejo.org/services/wiki"
)
// Search repositories via options
@@ -647,7 +647,7 @@ func Edit(ctx *context.APIContext) {
return
}
- if err := updateRepoUnits(ctx, opts); err != nil {
+ if err := updateRepoUnits(ctx, ctx.Repo.Owner.Name, ctx.Repo.Repository, opts); err != nil {
return
}
@@ -724,7 +724,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
visibilityChanged = repo.IsPrivate != *opts.Private
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.Doer.IsAdmin {
- err := fmt.Errorf("cannot change private repository to public")
+ err := errors.New("cannot change private repository to public")
ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
return err
}
@@ -779,10 +779,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
}
// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
-func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
- owner := ctx.Repo.Owner
- repo := ctx.Repo.Repository
-
+func updateRepoUnits(ctx *context.APIContext, owner string, repo *repo_model.Repository, opts api.EditRepoOption) error {
var units []repo_model.RepoUnit
var deleteUnitTypes []unit_model.Type
@@ -795,12 +792,12 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if newHasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) {
- err := fmt.Errorf("External tracker URL not valid")
+ err := errors.New("External tracker URL not valid")
ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL", err)
return err
}
if len(opts.ExternalTracker.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) {
- err := fmt.Errorf("External tracker URL format not valid")
+ err := errors.New("External tracker URL format not valid")
ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL format", err)
return err
}
@@ -871,7 +868,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
- err := fmt.Errorf("External wiki URL not valid")
+ err := errors.New("External wiki URL not valid")
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid external wiki URL")
return err
}
@@ -1045,7 +1042,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
}
}
- log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
+ log.Trace("Repository advanced settings updated: %s/%s", owner, repo.Name)
return nil
}
@@ -1055,7 +1052,7 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
// archive / un-archive
if opts.Archived != nil {
if repo.IsMirror {
- err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
+ err := errors.New("repo is a mirror, cannot archive/un-archive")
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
return err
}
@@ -1065,7 +1062,7 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
return err
}
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
}
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go
index 8d6ca9e3b5..024376c146 100644
--- a/routers/api/v1/repo/repo_test.go
+++ b/routers/api/v1/repo/repo_test.go
@@ -7,11 +7,11 @@ import (
"net/http"
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
@@ -58,7 +58,7 @@ func TestRepoEdit(t *testing.T) {
web.SetForm(ctx, &opts)
Edit(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1,
}, unittest.Cond("name = ? AND is_archived = 1", *opts.Name))
@@ -78,7 +78,7 @@ func TestRepoEditNameChange(t *testing.T) {
web.SetForm(ctx, &opts)
Edit(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1,
diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go
index 99676de119..7a836cd506 100644
--- a/routers/api/v1/repo/star.go
+++ b/routers/api/v1/repo/star.go
@@ -6,11 +6,11 @@ package repo
import (
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListStargazers list a repository's stargazers
diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go
index 9e36ea0aed..f02150f881 100644
--- a/routers/api/v1/repo/status.go
+++ b/routers/api/v1/repo/status.go
@@ -7,14 +7,14 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ commitstatus_service "forgejo.org/services/repository/commitstatus"
)
// NewCommitStatus creates a new CommitStatus
diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go
index 8584182857..37a2bef85c 100644
--- a/routers/api/v1/repo/subscriber.go
+++ b/routers/api/v1/repo/subscriber.go
@@ -6,11 +6,11 @@ package repo
import (
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListSubscribers list a repo's subscribers (i.e. watchers)
diff --git a/routers/api/v1/repo/sync_fork.go b/routers/api/v1/repo/sync_fork.go
new file mode 100644
index 0000000000..c3a9bd26ba
--- /dev/null
+++ b/routers/api/v1/repo/sync_fork.go
@@ -0,0 +1,185 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ git_model "forgejo.org/models/git"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
+)
+
+func getSyncForkInfo(ctx *context.APIContext, branch string) {
+ if !ctx.Repo.Repository.IsFork {
+ ctx.Error(http.StatusBadRequest, "NoFork", "The Repo must be a fork")
+ return
+ }
+
+ syncForkInfo, err := repo_service.GetSyncForkInfo(ctx, ctx.Repo.Repository, branch)
+ if err != nil {
+ if git_model.IsErrBranchNotExist(err) {
+ ctx.NotFound(err, branch)
+ return
+ }
+
+ ctx.Error(http.StatusInternalServerError, "GetSyncForkInfo", err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, syncForkInfo)
+}
+
+// SyncForkBranchInfo returns information about syncing the default fork branch with the base branch
+func SyncForkDefaultInfo(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/sync_fork repository repoSyncForkDefaultInfo
+ // ---
+ // summary: Gets information about syncing the fork default branch with the base branch
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/SyncForkInfo"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ getSyncForkInfo(ctx, ctx.Repo.Repository.DefaultBranch)
+}
+
+// SyncForkBranchInfo returns information about syncing a fork branch with the base branch
+func SyncForkBranchInfo(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/sync_fork/{branch} repository repoSyncForkBranchInfo
+ // ---
+ // summary: Gets information about syncing a fork branch with the base branch
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: branch
+ // in: path
+ // description: The branch
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/SyncForkInfo"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ getSyncForkInfo(ctx, ctx.Params("branch"))
+}
+
+func syncForkBranch(ctx *context.APIContext, branch string) {
+ if !ctx.Repo.Repository.IsFork {
+ ctx.Error(http.StatusBadRequest, "NoFork", "The Repo must be a fork")
+ return
+ }
+
+ syncForkInfo, err := repo_service.GetSyncForkInfo(ctx, ctx.Repo.Repository, branch)
+ if err != nil {
+ if git_model.IsErrBranchNotExist(err) {
+ ctx.NotFound(err, branch)
+ return
+ }
+
+ ctx.Error(http.StatusInternalServerError, "GetSyncForkInfo", err)
+ return
+ }
+
+ if !syncForkInfo.Allowed {
+ ctx.Error(http.StatusBadRequest, "NotAllowed", "You can't sync this branch")
+ return
+ }
+
+ err = repo_service.SyncFork(ctx, ctx.Doer, ctx.Repo.Repository, branch)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "SyncFork", err)
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// SyncForkBranch syncs the default of a fork with the base branch
+func SyncForkDefault(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/sync_fork repository repoSyncForkDefault
+ // ---
+ // summary: Syncs the default branch of a fork with the base branch
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ syncForkBranch(ctx, ctx.Repo.Repository.DefaultBranch)
+}
+
+// SyncForkBranch syncs a fork branch with the base branch
+func SyncForkBranch(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/sync_fork/{branch} repository repoSyncForkBranch
+ // ---
+ // summary: Syncs a fork branch with the base branch
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: branch
+ // in: path
+ // description: The branch
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ syncForkBranch(ctx, ctx.Params("branch"))
+}
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index 7dbdd1fcbd..f53a6da811 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -9,17 +9,17 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- releaseservice "code.gitea.io/gitea/services/release"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ releaseservice "forgejo.org/services/release"
)
// ListTags list all the tags of a repository
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index 0ecf3a39d8..4e9d3c40a9 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -7,11 +7,11 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- org_service "code.gitea.io/gitea/services/org"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/organization"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ org_service "forgejo.org/services/org"
+ repo_service "forgejo.org/services/repository"
)
// ListTeams list a repository's teams
diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go
index 1d8e675bde..daa637936e 100644
--- a/routers/api/v1/repo/topic.go
+++ b/routers/api/v1/repo/topic.go
@@ -7,13 +7,13 @@ import (
"net/http"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListTopics returns list of current topics for repo
diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go
index 0715aed064..72cfeaf902 100644
--- a/routers/api/v1/repo/transfer.go
+++ b/routers/api/v1/repo/transfer.go
@@ -8,19 +8,19 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ repo_service "forgejo.org/services/repository"
)
// Transfer transfers the ownership of a repository
@@ -238,7 +238,7 @@ func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error {
if !repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer) {
ctx.Error(http.StatusForbidden, "CanUserAcceptTransfer", nil)
- return fmt.Errorf("user does not have permissions to do this")
+ return errors.New("user does not have permissions to do this")
}
if accept {
diff --git a/routers/api/v1/repo/tree.go b/routers/api/v1/repo/tree.go
index 353a996d5b..af92170abb 100644
--- a/routers/api/v1/repo/tree.go
+++ b/routers/api/v1/repo/tree.go
@@ -6,8 +6,8 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/services/context"
- files_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/services/context"
+ files_service "forgejo.org/services/repository/files"
)
// GetTree get the tree of a repository.
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index 12aaa8edf8..7b6a00408a 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -5,21 +5,22 @@ package repo
import (
"encoding/base64"
+ "errors"
"fmt"
"net/http"
"net/url"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "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/services/context"
- "code.gitea.io/gitea/services/convert"
- notify_service "code.gitea.io/gitea/services/notify"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ notify_service "forgejo.org/services/notify"
+ wiki_service "forgejo.org/services/wiki"
)
// NewWikiPage response for wiki create request
@@ -506,11 +507,8 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit)
// given tree entry, encoded with base64. Writes to ctx if an error occurs.
func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string {
blob := entry.Blob()
- if blob.Size() > setting.API.DefaultMaxBlobSize {
- return ""
- }
- content, err := blob.GetBlobContentBase64()
- if err != nil {
+ content, err := blob.GetContentBase64(setting.API.DefaultMaxBlobSize)
+ if err != nil && !errors.As(err, &git.BlobTooLargeError{}) {
ctx.Error(http.StatusInternalServerError, "GetBlobContentBase64", err)
return ""
}
diff --git a/routers/api/v1/settings/settings.go b/routers/api/v1/settings/settings.go
index c422315b22..32ef50423c 100644
--- a/routers/api/v1/settings/settings.go
+++ b/routers/api/v1/settings/settings.go
@@ -6,9 +6,9 @@ package settings
import (
"net/http"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
)
// GetGeneralUISettings returns instance's global settings for ui
diff --git a/routers/api/v1/shared/quota.go b/routers/api/v1/shared/quota.go
index b892df4b2f..ceba9fea57 100644
--- a/routers/api/v1/shared/quota.go
+++ b/routers/api/v1/shared/quota.go
@@ -6,10 +6,10 @@ package shared
import (
"net/http"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func GetQuota(ctx *context.APIContext, userID int64) {
diff --git a/routers/api/v1/shared/runners.go b/routers/api/v1/shared/runners.go
index f184786d7d..a7811a95b5 100644
--- a/routers/api/v1/shared/runners.go
+++ b/routers/api/v1/shared/runners.go
@@ -6,10 +6,13 @@ package shared
import (
"errors"
"net/http"
+ "strings"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
// RegistrationToken is a string used to register a runner with a server
@@ -30,3 +33,40 @@ func GetRegistrationToken(ctx *context.APIContext, ownerID, repoID int64) {
ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
}
+
+func GetActionRunJobs(ctx *context.APIContext, ownerID, repoID int64) {
+ labels := strings.Split(ctx.FormTrim("labels"), ",")
+
+ total, err := db.Find[actions_model.ActionRunJob](ctx, &actions_model.FindTaskOptions{
+ Status: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusRunning},
+ OwnerID: ownerID,
+ RepoID: repoID,
+ })
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "CountWaitingActionRunJobs", err)
+ return
+ }
+
+ res := fromRunJobModelToResponse(total, labels)
+
+ ctx.JSON(http.StatusOK, res)
+}
+
+func fromRunJobModelToResponse(job []*actions_model.ActionRunJob, labels []string) []*structs.ActionRunJob {
+ var res []*structs.ActionRunJob
+ for i := range job {
+ if job[i].ItRunsOn(labels) {
+ res = append(res, &structs.ActionRunJob{
+ ID: job[i].ID,
+ RepoID: job[i].RepoID,
+ OwnerID: job[i].OwnerID,
+ Name: job[i].Name,
+ Needs: job[i].Needs,
+ RunsOn: job[i].RunsOn,
+ TaskID: job[i].TaskID,
+ Status: job[i].Status.String(),
+ })
+ }
+ }
+ return res
+}
diff --git a/routers/api/v1/swagger/action.go b/routers/api/v1/swagger/action.go
index 665f4d0b85..6fc58abd76 100644
--- a/routers/api/v1/swagger/action.go
+++ b/routers/api/v1/swagger/action.go
@@ -3,7 +3,7 @@
package swagger
-import api "code.gitea.io/gitea/modules/structs"
+import api "forgejo.org/modules/structs"
// SecretList
// swagger:response SecretList
@@ -32,3 +32,17 @@ type swaggerResponseVariableList struct {
// in:body
Body []api.ActionVariable `json:"body"`
}
+
+// RunJobList is a list of action run jobs
+// swagger:response RunJobList
+type swaggerRunJobList struct {
+ // in:body
+ Body []*api.ActionRunJob `json:"body"`
+}
+
+// DispatchWorkflowRun is a Workflow Run after dispatching
+// swagger:response DispatchWorkflowRun
+type swaggerDispatchWorkflowRun struct {
+ // in:body
+ Body *api.DispatchWorkflowRun `json:"body"`
+}
diff --git a/routers/api/v1/swagger/activity.go b/routers/api/v1/swagger/activity.go
index 95e1ba9035..64466e5e7b 100644
--- a/routers/api/v1/swagger/activity.go
+++ b/routers/api/v1/swagger/activity.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// ActivityFeedsList
diff --git a/routers/api/v1/swagger/activitypub.go b/routers/api/v1/swagger/activitypub.go
index 91341669da..6235009572 100644
--- a/routers/api/v1/swagger/activitypub.go
+++ b/routers/api/v1/swagger/activitypub.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// ActivityPub
diff --git a/routers/api/v1/swagger/app.go b/routers/api/v1/swagger/app.go
index 6a08b11874..7d62b6d494 100644
--- a/routers/api/v1/swagger/app.go
+++ b/routers/api/v1/swagger/app.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// OAuth2Application
diff --git a/routers/api/v1/swagger/cron.go b/routers/api/v1/swagger/cron.go
index 00cfbe0adb..2c26b22441 100644
--- a/routers/api/v1/swagger/cron.go
+++ b/routers/api/v1/swagger/cron.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// CronList
diff --git a/routers/api/v1/swagger/issue.go b/routers/api/v1/swagger/issue.go
index 62458a3424..b2b5de2228 100644
--- a/routers/api/v1/swagger/issue.go
+++ b/routers/api/v1/swagger/issue.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// Issue
diff --git a/routers/api/v1/swagger/key.go b/routers/api/v1/swagger/key.go
index 8390833589..27aa72458d 100644
--- a/routers/api/v1/swagger/key.go
+++ b/routers/api/v1/swagger/key.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// PublicKey
diff --git a/routers/api/v1/swagger/misc.go b/routers/api/v1/swagger/misc.go
index 0553eac2a9..df95a94571 100644
--- a/routers/api/v1/swagger/misc.go
+++ b/routers/api/v1/swagger/misc.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// ServerVersion
diff --git a/routers/api/v1/swagger/nodeinfo.go b/routers/api/v1/swagger/nodeinfo.go
index 8650dfa092..227db61648 100644
--- a/routers/api/v1/swagger/nodeinfo.go
+++ b/routers/api/v1/swagger/nodeinfo.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// NodeInfo
diff --git a/routers/api/v1/swagger/notify.go b/routers/api/v1/swagger/notify.go
index 743d807a0a..cd60ef2bcb 100644
--- a/routers/api/v1/swagger/notify.go
+++ b/routers/api/v1/swagger/notify.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// NotificationThread
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 432e42d4e7..4860f10c98 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -5,9 +5,9 @@
package swagger
import (
- ffed "code.gitea.io/gitea/modules/forgefed"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/forms"
+ ffed "forgejo.org/modules/forgefed"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/forms"
)
// not actually a response, just a hack to get go-swagger to include definitions
@@ -216,6 +216,9 @@ type swaggerParameterBodies struct {
// in:body
CreateVariableOption api.CreateVariableOption
+ // in:body
+ RenameOrgOption api.RenameOrgOption
+
// in:body
UpdateVariableOption api.UpdateVariableOption
diff --git a/routers/api/v1/swagger/org.go b/routers/api/v1/swagger/org.go
index 0105446b00..2d081708a8 100644
--- a/routers/api/v1/swagger/org.go
+++ b/routers/api/v1/swagger/org.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// Organization
diff --git a/routers/api/v1/swagger/package.go b/routers/api/v1/swagger/package.go
index eada12d1ea..dd1b45e9aa 100644
--- a/routers/api/v1/swagger/package.go
+++ b/routers/api/v1/swagger/package.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// Package
diff --git a/routers/api/v1/swagger/quota.go b/routers/api/v1/swagger/quota.go
index 35e633c39d..b2ea59fb31 100644
--- a/routers/api/v1/swagger/quota.go
+++ b/routers/api/v1/swagger/quota.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// QuotaInfo
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index ca214b4900..cd4832e15f 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -4,7 +4,7 @@
package swagger
import (
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
)
// Repository
@@ -231,11 +231,18 @@ type swaggerGitTreeResponse struct {
Body api.GitTreeResponse `json:"body"`
}
-// GitBlobResponse
-// swagger:response GitBlobResponse
-type swaggerGitBlobResponse struct {
+// GitBlob
+// swagger:response GitBlob
+type swaggerGitBlob struct {
// in: body
- Body api.GitBlobResponse `json:"body"`
+ Body api.GitBlob `json:"body"`
+}
+
+// GitBlobList
+// swagger:response GitBlobList
+type swaggerGitBlobList struct {
+ // in: body
+ Body []api.GitBlob `json:"body"`
}
// Commit
@@ -448,3 +455,24 @@ type swaggerCompare struct {
// in:body
Body api.Compare `json:"body"`
}
+
+// SyncForkInfo
+// swagger:response SyncForkInfo
+type swaggerSyncForkInfo struct {
+ // in:body
+ Body []api.SyncForkInfo `json:"body"`
+}
+
+// ActionRunList
+// swagger:response ActionRunList
+type swaggerActionRunList struct {
+ // in:body
+ Body api.ListActionRunResponse `json:"body"`
+}
+
+// ActionRun
+// swagger:response ActionRun
+type swaggerActionRun struct {
+ // in:body
+ Body api.ActionRun `json:"body"`
+}
diff --git a/routers/api/v1/swagger/settings.go b/routers/api/v1/swagger/settings.go
index a9466699df..3a07eaf2e0 100644
--- a/routers/api/v1/swagger/settings.go
+++ b/routers/api/v1/swagger/settings.go
@@ -3,7 +3,7 @@
package swagger
-import api "code.gitea.io/gitea/modules/structs"
+import api "forgejo.org/modules/structs"
// GeneralRepoSettings
// swagger:response GeneralRepoSettings
diff --git a/routers/api/v1/swagger/user.go b/routers/api/v1/swagger/user.go
index 37e28664fb..805cfe3df4 100644
--- a/routers/api/v1/swagger/user.go
+++ b/routers/api/v1/swagger/user.go
@@ -4,8 +4,8 @@
package swagger
import (
- activities_model "code.gitea.io/gitea/models/activities"
- api "code.gitea.io/gitea/modules/structs"
+ activities_model "forgejo.org/models/activities"
+ api "forgejo.org/modules/structs"
)
// User
diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go
index c34c5950c0..dd816cb7ae 100644
--- a/routers/api/v1/user/action.go
+++ b/routers/api/v1/user/action.go
@@ -7,15 +7,15 @@ import (
"errors"
"net/http"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- 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"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
- secret_service "code.gitea.io/gitea/services/secrets"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
+ secret_service "forgejo.org/services/secrets"
)
// create or update one secret of the user scope
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index c4fb2ea38d..138ad5a1d2 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -11,13 +11,13 @@ import (
"strconv"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// ListAccessTokens list all the access tokens
diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go
index d3833a32bb..453682a37b 100644
--- a/routers/api/v1/user/avatar.go
+++ b/routers/api/v1/user/avatar.go
@@ -7,10 +7,10 @@ import (
"encoding/base64"
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
// UpdateAvatar updates the Avatar of an User
diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go
index 6bd7e10dd8..03d8d14b90 100644
--- a/routers/api/v1/user/email.go
+++ b/routers/api/v1/user/email.go
@@ -7,13 +7,13 @@ import (
"fmt"
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- user_service "code.gitea.io/gitea/services/user"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ user_service "forgejo.org/services/user"
)
// ListEmails list all of the authenticated user's email addresses
@@ -75,14 +75,11 @@ func AddEmail(ctx *context.APIContext) {
if err := user_service.AddEmailAddresses(ctx, ctx.Doer, form.Emails); err != nil {
if user_model.IsErrEmailAlreadyUsed(err) {
ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email)
- } else if validation.IsErrEmailCharIsNotSupported(err) || validation.IsErrEmailInvalid(err) {
+ } else if validation.IsErrEmailInvalid(err) {
email := ""
if typedError, ok := err.(validation.ErrEmailInvalid); ok {
email = typedError.Email
}
- if typedError, ok := err.(validation.ErrEmailCharIsNotSupported); ok {
- email = typedError.Email
- }
errMsg := fmt.Sprintf("Email address %q invalid", email)
ctx.Error(http.StatusUnprocessableEntity, "", errMsg)
diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go
index 784e2325a3..643ad49b80 100644
--- a/routers/api/v1/user/follower.go
+++ b/routers/api/v1/user/follower.go
@@ -8,11 +8,11 @@ import (
"errors"
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go
index 2fe4eb8e78..886e33b205 100644
--- a/routers/api/v1/user/gpg_key.go
+++ b/routers/api/v1/user/gpg_key.go
@@ -4,19 +4,20 @@
package user
import (
+ "errors"
"fmt"
"net/http"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions) {
@@ -143,7 +144,7 @@ func GetGPGKey(ctx *context.APIContext) {
// CreateUserGPGKey creates new GPG key to given user by ID.
func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
@@ -298,16 +299,12 @@ func DeleteGPGKey(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.ParamsInt64(":id")); err != nil {
- if asymkey_model.IsErrGPGKeyAccessDenied(err) {
- ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
- } else {
- ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err)
- }
+ ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err)
return
}
@@ -317,8 +314,6 @@ func DeleteGPGKey(ctx *context.APIContext) {
// HandleAddGPGKeyError handle add GPGKey error
func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) {
switch {
- case asymkey_model.IsErrGPGKeyAccessDenied(err):
- ctx.Error(http.StatusUnprocessableEntity, "GPGKeyAccessDenied", "You do not have access to this GPG key")
case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
ctx.Error(http.StatusUnprocessableEntity, "GPGKeyIDAlreadyUsed", "A key with the same id already exists")
case asymkey_model.IsErrGPGKeyParsing(err):
diff --git a/routers/api/v1/user/helper.go b/routers/api/v1/user/helper.go
index 8b5c64e291..fe0943091f 100644
--- a/routers/api/v1/user/helper.go
+++ b/routers/api/v1/user/helper.go
@@ -6,8 +6,8 @@ package user
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/services/context"
)
// GetUserByParamsName get user by name
diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go
index 47b6498d85..c8cdf5040d 100644
--- a/routers/api/v1/user/hook.go
+++ b/routers/api/v1/user/hook.go
@@ -6,11 +6,11 @@ package user
import (
"net/http"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
// ListHooks list the authenticated user's webhooks
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index 1b4ba0a40f..d8b5dfdfe9 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -5,28 +5,29 @@ package user
import (
std_ctx "context"
- "fmt"
+ "errors"
"net/http"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/v1/repo"
- "code.gitea.io/gitea/routers/api/v1/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/repo"
+ "forgejo.org/routers/api/v1/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// appendPrivateInformation appends the owner and key type information to api.PublicKey
func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *asymkey_model.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) {
- if key.Type == asymkey_model.KeyTypeDeploy {
+ switch key.Type {
+ case asymkey_model.KeyTypeDeploy:
apiKey.KeyType = "deploy"
- } else if key.Type == asymkey_model.KeyTypeUser {
+ case asymkey_model.KeyTypeUser:
apiKey.KeyType = "user"
if defaultUser.ID == key.OwnerID {
@@ -38,7 +39,7 @@ func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *a
}
apiKey.Owner = convert.ToUser(ctx, user, user)
}
- } else {
+ default:
apiKey.KeyType = "unknown"
}
apiKey.ReadOnly = key.Mode == perm.AccessModeRead
@@ -208,7 +209,7 @@ func GetPublicKey(ctx *context.APIContext) {
// CreateUserPublicKey creates new public key to given user by ID.
func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -284,7 +285,7 @@ func DeletePublicKey(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
diff --git a/routers/api/v1/user/quota.go b/routers/api/v1/user/quota.go
index ab2881b355..40c8ee43e9 100644
--- a/routers/api/v1/user/quota.go
+++ b/routers/api/v1/user/quota.go
@@ -4,8 +4,8 @@
package user
import (
- "code.gitea.io/gitea/routers/api/v1/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/services/context"
)
// GetQuota returns the quota information for the authenticated user
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 3b0654ef13..94dd3931e4 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -6,13 +6,13 @@ package user
import (
"net/http"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// listUserRepos - List the repositories owned by the given user.
@@ -101,8 +101,9 @@ func ListMyRepos(ctx *context.APIContext) {
// type: integer
// - name: order_by
// in: query
- // description: order the repositories by name (default), id, or size
+ // description: order the repositories
// type: string
+ // enum: [name, id, newest, oldest, recentupdate, leastupdate, reversealphabetically, alphabetically, reversesize, size, reversegitsize, gitsize, reverselfssize, lfssize, moststars, feweststars, mostforks, fewestforks]
// responses:
// "200":
// "$ref": "#/responses/RepositoryList"
@@ -124,14 +125,15 @@ func ListMyRepos(ctx *context.APIContext) {
switch orderBy {
case "name":
opts.OrderBy = "name ASC"
- case "size":
- opts.OrderBy = "size DESC"
case "id":
opts.OrderBy = "id ASC"
- case "":
default:
- ctx.Error(http.StatusUnprocessableEntity, "", "invalid order_by")
- return
+ if orderBy, ok := repo_model.OrderByFlatMap[orderBy]; ok {
+ opts.OrderBy = orderBy
+ } else if orderBy != "" {
+ ctx.Error(http.StatusUnprocessableEntity, "", "invalid order_by")
+ return
+ }
}
repos, count, err := repo_model.SearchRepository(ctx, opts)
@@ -153,7 +155,7 @@ func ListMyRepos(ctx *context.APIContext) {
results[i] = convert.ToRepo(ctx, repo, permission)
}
- ctx.SetLinkHeader(int(count), opts.ListOptions.PageSize)
+ ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, &results)
}
diff --git a/routers/api/v1/user/runners.go b/routers/api/v1/user/runners.go
index dc4c187ffe..579e3eb932 100644
--- a/routers/api/v1/user/runners.go
+++ b/routers/api/v1/user/runners.go
@@ -4,8 +4,8 @@
package user
import (
- "code.gitea.io/gitea/routers/api/v1/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/routers/api/v1/shared"
+ "forgejo.org/services/context"
)
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
@@ -28,3 +28,25 @@ func GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
}
+
+// SearchActionRunJobs return a list of actions jobs filtered by the provided parameters
+func SearchActionRunJobs(ctx *context.APIContext) {
+ // swagger:operation GET /user/actions/runners/jobs user userSearchRunJobs
+ // ---
+ // summary: Search for user's action jobs according filter conditions
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: labels
+ // in: query
+ // description: a comma separated list of run job labels to search for
+ // type: string
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RunJobList"
+ // "401":
+ // "$ref": "#/responses/unauthorized"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ shared.GetActionRunJobs(ctx, ctx.Doer.ID, 0)
+}
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index 173f06e474..134b448718 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -6,12 +6,12 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/modules/optional"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/modules/optional"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ user_service "forgejo.org/services/user"
)
// GetUserSettings returns user settings
@@ -63,6 +63,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
Theme: optional.FromPtr(form.Theme),
DiffViewStyle: optional.FromPtr(form.DiffViewStyle),
KeepEmailPrivate: optional.FromPtr(form.HideEmail),
+ KeepPronounsPrivate: optional.FromPtr(form.HidePronouns),
KeepActivityPrivate: optional.FromPtr(form.HideActivity),
EnableRepoUnitHints: optional.FromPtr(form.EnableRepoUnitHints),
}
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index be84b13204..19fa49f2ad 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -9,15 +9,15 @@ import (
std_context "context"
"net/http"
- "code.gitea.io/gitea/models/db"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/repository"
)
// getStarredRepos returns the repos that the user with the specified userID has
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index da1250b283..5bdd56c892 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -8,12 +8,12 @@ import (
"fmt"
"net/http"
- activities_model "code.gitea.io/gitea/models/activities"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ activities_model "forgejo.org/models/activities"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// Search search users
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index dc27a38a03..1358a63f51 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -7,14 +7,14 @@ import (
std_context "context"
"net/http"
- "code.gitea.io/gitea/models/db"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/api/v1/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// getWatchedRepos returns the repos that the user with the specified userID is watching
diff --git a/routers/api/v1/utils/block.go b/routers/api/v1/utils/block.go
index 34fad96034..a1f044d1ef 100644
--- a/routers/api/v1/utils/block.go
+++ b/routers/api/v1/utils/block.go
@@ -6,10 +6,10 @@ package utils
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
// ListUserBlockedUsers lists the blocked users of the provided doer.
diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go
index 4e25137817..65a8994405 100644
--- a/routers/api/v1/utils/git.go
+++ b/routers/api/v1/utils/git.go
@@ -5,13 +5,14 @@ package utils
import (
gocontext "context"
+ "errors"
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
// ResolveRefOrSha resolve ref to sha if exist
@@ -50,7 +51,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
// GetGitRefs return git references based on filter
func GetGitRefs(ctx *context.APIContext, filter string) ([]*git.Reference, string, error) {
if ctx.Repo.GitRepo == nil {
- return nil, "", fmt.Errorf("no open git repo found in context")
+ return nil, "", errors.New("no open git repo found in context")
}
if len(filter) > 0 {
filter = "refs/" + filter
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index f1abd49a7d..fc4b3293ac 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -9,16 +9,17 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/models/webhook"
- "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"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/validation"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
// ListOwnerHooks lists the webhooks of the provided owner
@@ -93,6 +94,10 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
return false
}
+ if !validation.IsValidURL(form.Config["url"]) {
+ ctx.Error(http.StatusUnprocessableEntity, "", "Invalid url")
+ return false
+ }
return true
}
@@ -322,6 +327,10 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6
func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool {
if form.Config != nil {
if url, ok := form.Config["url"]; ok {
+ if !validation.IsValidURL(url) {
+ ctx.Error(http.StatusUnprocessableEntity, "", "Invalid url")
+ return false
+ }
w.URL = url
}
if ct, ok := form.Config["content_type"]; ok {
diff --git a/routers/api/v1/utils/hook_test.go b/routers/api/v1/utils/hook_test.go
new file mode 100644
index 0000000000..3d0e6db079
--- /dev/null
+++ b/routers/api/v1/utils/hook_test.go
@@ -0,0 +1,86 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "net/http"
+ "testing"
+
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTestHookValidation(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+
+ t.Run("Test Validation", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadGitRepo(t, ctx)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, 0, ctx.Resp.WrittenStatus()) // not written yet
+ })
+
+ t.Run("Test Validation with invalid URL", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadGitRepo(t, ctx)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with invalid webhook type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadGitRepo(t, ctx)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with empty content type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadGitRepo(t, ctx)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+}
diff --git a/routers/api/v1/utils/main_test.go b/routers/api/v1/utils/main_test.go
new file mode 100644
index 0000000000..f243572436
--- /dev/null
+++ b/routers/api/v1/utils/main_test.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "testing"
+
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ webhook_service "forgejo.org/services/webhook"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ SetUp: func() error {
+ setting.LoadQueueSettings()
+ return webhook_service.Init()
+ },
+ })
+}
diff --git a/routers/api/v1/utils/page.go b/routers/api/v1/utils/page.go
index 024ba7b8d9..4ab141ca64 100644
--- a/routers/api/v1/utils/page.go
+++ b/routers/api/v1/utils/page.go
@@ -4,9 +4,9 @@
package utils
import (
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetListOptions returns list options using the page and limit parameters
diff --git a/routers/common/auth.go b/routers/common/auth.go
index 115d65ed10..d4b3b1fea7 100644
--- a/routers/common/auth.go
+++ b/routers/common/auth.go
@@ -4,10 +4,10 @@
package common
import (
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/web/middleware"
- auth_service "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/web/middleware"
+ auth_service "forgejo.org/services/auth"
+ "forgejo.org/services/context"
)
type AuthResult struct {
@@ -31,6 +31,7 @@ func AuthShared(ctx *context.Base, sessionStore auth_service.SessionStore, authM
ctx.Data["SignedUserID"] = ar.Doer.ID
ctx.Data["IsAdmin"] = ar.Doer.IsAdmin
} else {
+ ctx.Data["IsSigned"] = false
ctx.Data["SignedUserID"] = int64(0)
}
return ar, nil
diff --git a/routers/common/compare.go b/routers/common/compare.go
index 4d1cc2f0d8..9c158814d1 100644
--- a/routers/common/compare.go
+++ b/routers/common/compare.go
@@ -4,9 +4,9 @@
package common
import (
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
)
// CompareInfo represents the collected results from ParseCompareInfo
diff --git a/routers/common/db.go b/routers/common/db.go
index ac24303989..ec31ced1bf 100644
--- a/routers/common/db.go
+++ b/routers/common/db.go
@@ -5,15 +5,15 @@ package common
import (
"context"
- "fmt"
+ "errors"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/migrations"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/setting/config"
+ "forgejo.org/models/db"
+ "forgejo.org/models/migrations"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/setting/config"
"xorm.io/xorm"
)
@@ -24,11 +24,11 @@ func InitDBEngine(ctx context.Context) (err error) {
for i := 0; i < setting.Database.DBConnectRetries; i++ {
select {
case <-ctx.Done():
- return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization")
+ return errors.New("Aborted due to shutdown:\nin retry ORM engine initialization")
default:
}
log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries)
- if err = db.InitEngineWithMigration(ctx, migrateWithSetting); err == nil {
+ if err = db.InitEngineWithMigration(ctx, func(eng db.Engine) error { return migrateWithSetting(eng.(*xorm.Engine)) }); err == nil {
break
} else if i == setting.Database.DBConnectRetries-1 {
return err
diff --git a/routers/common/errpage.go b/routers/common/errpage.go
index 402ca44c12..907c278ab1 100644
--- a/routers/common/errpage.go
+++ b/routers/common/errpage.go
@@ -7,15 +7,15 @@ import (
"fmt"
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/modules/web/routing"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/modules/web/routing"
+ "forgejo.org/services/context"
)
const tplStatus500 base.TplName = "status/500"
diff --git a/routers/common/errpage_test.go b/routers/common/errpage_test.go
index 4fd63ba49e..3a492ea304 100644
--- a/routers/common/errpage_test.go
+++ b/routers/common/errpage_test.go
@@ -4,16 +4,15 @@
package common
import (
- "context"
"errors"
"net/http"
"net/http/httptest"
"net/url"
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/web/middleware"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/web/middleware"
"github.com/stretchr/testify/assert"
)
@@ -21,7 +20,7 @@ import (
func TestRenderPanicErrorPage(t *testing.T) {
w := httptest.NewRecorder()
req := &http.Request{URL: &url.URL{}}
- req = req.WithContext(middleware.WithContextData(context.Background()))
+ req = req.WithContext(middleware.WithContextData(t.Context()))
RenderPanicErrorPage(w, req, errors.New("fake panic error (for test only)"))
respContent := w.Body.String()
assert.Contains(t, respContent, `class="page-content status-page-500"`)
diff --git a/routers/common/markup.go b/routers/common/markup.go
index ce3a8acdb0..715d7d883f 100644
--- a/routers/common/markup.go
+++ b/routers/common/markup.go
@@ -9,11 +9,11 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
"mvdan.cc/xurls/v2"
)
diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index 856c10e678..d44f046a1e 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -6,14 +6,15 @@ package common
import (
"fmt"
"net/http"
+ "runtime/trace"
"strings"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/modules/web/routing"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/modules/web/routing"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/session"
"github.com/chi-middleware/proxy"
@@ -43,6 +44,8 @@ func ProtocolMiddlewares() (handlers []any) {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
defer finished()
+ trace.Log(ctx, "method", req.Method)
+ trace.Log(ctx, "url", req.RequestURI)
next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
})
})
@@ -74,27 +77,27 @@ func ProtocolMiddlewares() (handlers []any) {
func stripSlashesMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- // First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
+ // Ensure that URL.RawPath is always set.
req.URL.RawPath = req.URL.EscapedPath()
- urlPath := req.URL.RawPath
- rctx := chi.RouteContext(req.Context())
- if rctx != nil && rctx.RoutePath != "" {
- urlPath = rctx.RoutePath
- }
-
- sanitizedPath := &strings.Builder{}
- prevWasSlash := false
- for _, chr := range strings.TrimRight(urlPath, "/") {
- if chr != '/' || !prevWasSlash {
- sanitizedPath.WriteRune(chr)
+ sanitize := func(path string) string {
+ sanitizedPath := &strings.Builder{}
+ prevWasSlash := false
+ for _, chr := range strings.TrimRight(path, "/") {
+ if chr != '/' || !prevWasSlash {
+ sanitizedPath.WriteRune(chr)
+ }
+ prevWasSlash = chr == '/'
}
- prevWasSlash = chr == '/'
+ return sanitizedPath.String()
}
- req.URL.Path = sanitizedPath.String()
+ // Sanitize the unescaped path for application logic.
+ req.URL.Path = sanitize(req.URL.Path)
+ rctx := chi.RouteContext(req.Context())
if rctx != nil {
- rctx.RoutePath = req.URL.Path
+ // Sanitize the escaped path for routing.
+ rctx.RoutePath = sanitize(req.URL.RawPath)
}
next.ServeHTTP(resp, req)
})
diff --git a/routers/common/middleware_test.go b/routers/common/middleware_test.go
index 0e111e1261..b9c1b226e8 100644
--- a/routers/common/middleware_test.go
+++ b/routers/common/middleware_test.go
@@ -7,7 +7,7 @@ import (
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/modules/web"
+ "forgejo.org/modules/web"
chi "github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
@@ -15,9 +15,10 @@ import (
func TestStripSlashesMiddleware(t *testing.T) {
type test struct {
- name string
- expectedPath string
- inputPath string
+ name string
+ expectedPath string
+ expectedNormalPath string
+ inputPath string
}
tests := []test{
@@ -57,9 +58,16 @@ func TestStripSlashesMiddleware(t *testing.T) {
expectedPath: "/repo/migrate",
},
{
- name: "path with encoded slash",
- inputPath: "/user2/%2F%2Frepo1",
- expectedPath: "/user2/%2F%2Frepo1",
+ name: "path with encoded slash",
+ inputPath: "/user2/%2F%2Frepo1",
+ expectedPath: "/user2/%2F%2Frepo1",
+ expectedNormalPath: "/user2/repo1",
+ },
+ {
+ name: "path with space",
+ inputPath: "/assets/css/theme%20cappuccino.css",
+ expectedPath: "/assets/css/theme%20cappuccino.css",
+ expectedNormalPath: "/assets/css/theme cappuccino.css",
},
}
@@ -69,7 +77,11 @@ func TestStripSlashesMiddleware(t *testing.T) {
called := false
r.Get("*", func(w http.ResponseWriter, r *http.Request) {
- assert.Equal(t, tt.expectedPath, r.URL.Path)
+ if tt.expectedNormalPath != "" {
+ assert.Equal(t, tt.expectedNormalPath, r.URL.Path)
+ } else {
+ assert.Equal(t, tt.expectedPath, r.URL.Path)
+ }
rctx := chi.RouteContext(r.Context())
assert.Equal(t, tt.expectedPath, rctx.RoutePath)
diff --git a/routers/common/redirect.go b/routers/common/redirect.go
index 9bf2025e19..8c13911a9c 100644
--- a/routers/common/redirect.go
+++ b/routers/common/redirect.go
@@ -6,7 +6,7 @@ package common
import (
"net/http"
- "code.gitea.io/gitea/modules/httplib"
+ "forgejo.org/modules/httplib"
)
// FetchRedirectDelegate helps the "fetch" requests to redirect to the correct location
diff --git a/routers/common/serve.go b/routers/common/serve.go
index 446908db75..9d017ec5a1 100644
--- a/routers/common/serve.go
+++ b/routers/common/serve.go
@@ -7,11 +7,11 @@ import (
"io"
"time"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/httplib"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
// ServeBlob download a git.Blob
diff --git a/routers/init.go b/routers/init.go
index 821a0ef38c..90a1cb1e89 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -8,50 +8,50 @@ import (
"reflect"
"runtime"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- authmodel "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/eventsource"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/highlight"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/external"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/ssh"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/svg"
- "code.gitea.io/gitea/modules/system"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web"
- actions_router "code.gitea.io/gitea/routers/api/actions"
- forgejo "code.gitea.io/gitea/routers/api/forgejo/v1"
- packages_router "code.gitea.io/gitea/routers/api/packages"
- apiv1 "code.gitea.io/gitea/routers/api/v1"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/routers/private"
- web_routers "code.gitea.io/gitea/routers/web"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/automerge"
- "code.gitea.io/gitea/services/cron"
- feed_service "code.gitea.io/gitea/services/feed"
- indexer_service "code.gitea.io/gitea/services/indexer"
- "code.gitea.io/gitea/services/mailer"
- mailer_incoming "code.gitea.io/gitea/services/mailer/incoming"
- markup_service "code.gitea.io/gitea/services/markup"
- repo_migrations "code.gitea.io/gitea/services/migrations"
- mirror_service "code.gitea.io/gitea/services/mirror"
- pull_service "code.gitea.io/gitea/services/pull"
- release_service "code.gitea.io/gitea/services/release"
- repo_service "code.gitea.io/gitea/services/repository"
- "code.gitea.io/gitea/services/repository/archiver"
- "code.gitea.io/gitea/services/task"
- "code.gitea.io/gitea/services/uinotification"
- "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ authmodel "forgejo.org/models/auth"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/eventsource"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/highlight"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/external"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/ssh"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/svg"
+ "forgejo.org/modules/system"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/web"
+ actions_router "forgejo.org/routers/api/actions"
+ forgejo "forgejo.org/routers/api/forgejo/v1"
+ packages_router "forgejo.org/routers/api/packages"
+ apiv1 "forgejo.org/routers/api/v1"
+ "forgejo.org/routers/common"
+ "forgejo.org/routers/private"
+ web_routers "forgejo.org/routers/web"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/automerge"
+ "forgejo.org/services/cron"
+ feed_service "forgejo.org/services/feed"
+ indexer_service "forgejo.org/services/indexer"
+ "forgejo.org/services/mailer"
+ mailer_incoming "forgejo.org/services/mailer/incoming"
+ markup_service "forgejo.org/services/markup"
+ repo_migrations "forgejo.org/services/migrations"
+ mirror_service "forgejo.org/services/mirror"
+ pull_service "forgejo.org/services/pull"
+ release_service "forgejo.org/services/release"
+ repo_service "forgejo.org/services/repository"
+ "forgejo.org/services/repository/archiver"
+ "forgejo.org/services/task"
+ "forgejo.org/services/uinotification"
+ "forgejo.org/services/webhook"
)
func mustInit(fn func() error) {
diff --git a/routers/install/install.go b/routers/install/install.go
index 24db25f459..f64f395a7f 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -15,26 +15,25 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- db_install "code.gitea.io/gitea/models/db/install"
- "code.gitea.io/gitea/models/migrations"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password/hash"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/generate"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/user"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ db_install "forgejo.org/models/db/install"
+ "forgejo.org/models/migrations"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password/hash"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/generate"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
"code.forgejo.org/go-chi/session"
)
@@ -119,15 +118,7 @@ func Install(ctx *context.Context) {
form.AppSlogan = "Beyond coding. We Forge."
form.RepoRootPath = setting.RepoRootPath
form.LFSRootPath = setting.LFS.Storage.Path
-
- // Note(unknown): it's hard for Windows users change a running user,
- // so just use current one if config says default.
- if setting.IsWindows && setting.RunUser == "git" {
- form.RunUser = user.CurrentUsername()
- } else {
- form.RunUser = setting.RunUser
- }
-
+ form.RunUser = setting.RunUser
form.Domain = setting.Domain
form.SSHPort = setting.SSH.Port
form.HTTPPort = setting.HTTPPort
@@ -370,7 +361,8 @@ func SubmitInstall(ctx *context.Context) {
}
// Init the engine with migration
- if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil {
+ // Wrap migrations.Migrate into a function of type func(db.Engine) error to fix diagnostics.
+ if err = db.InitEngineWithMigration(ctx, migrations.WrapperMigrate); err != nil {
db.UnsetDefaultEngine()
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form)
@@ -416,11 +408,7 @@ func SubmitInstall(ctx *context.Context) {
if form.LFSRootPath != "" {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
- var lfsJwtSecret string
- if _, lfsJwtSecret, err = generate.NewJwtSecret(); err != nil {
- ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
- return
- }
+ _, lfsJwtSecret := generate.NewJwtSecret()
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
} else {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
@@ -491,11 +479,7 @@ func SubmitInstall(ctx *context.Context) {
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
// see the "loadOAuth2From" in "setting/oauth2.go"
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
- _, jwtSecretBase64, err := generate.NewJwtSecret()
- if err != nil {
- ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
- return
- }
+ _, jwtSecretBase64 := generate.NewJwtSecret()
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
}
@@ -596,7 +580,7 @@ func SubmitInstall(ctx *context.Context) {
go func() {
// Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js)
- // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future ....
+ // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Forgejo in the future ....
time.Sleep(3 * time.Second)
// Now get the http.Server from this request and shut it down
diff --git a/routers/install/routes.go b/routers/install/routes.go
index 06c9d389a6..f7fb40f688 100644
--- a/routers/install/routes.go
+++ b/routers/install/routes.go
@@ -8,12 +8,12 @@ import (
"html"
"net/http"
- "code.gitea.io/gitea/modules/public"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/routers/web/healthcheck"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/modules/public"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/routers/web/healthcheck"
+ "forgejo.org/services/forms"
)
// Routes registers the installation routes
diff --git a/routers/install/routes_test.go b/routers/install/routes_test.go
index 2aa7f5d7b7..9b10f05b3b 100644
--- a/routers/install/routes_test.go
+++ b/routers/install/routes_test.go
@@ -7,7 +7,7 @@ import (
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
)
@@ -19,18 +19,18 @@ func TestRoutes(t *testing.T) {
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 200, w.Code)
+ assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), `class="page-content install"`)
w = httptest.NewRecorder()
req = httptest.NewRequest("GET", "/no-such", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 404, w.Code)
+ assert.Equal(t, 404, w.Code)
w = httptest.NewRecorder()
- req = httptest.NewRequest("GET", "/assets/img/gitea.svg", nil)
+ req = httptest.NewRequest("GET", "/assets/img/forgejo.svg", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 200, w.Code)
+ assert.Equal(t, 200, w.Code)
}
func TestMain(m *testing.M) {
diff --git a/routers/private/actions.go b/routers/private/actions.go
index 425c480b3e..441fb881ed 100644
--- a/routers/private/actions.go
+++ b/routers/private/actions.go
@@ -10,14 +10,14 @@ import (
"net/http"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
// GenerateActionsRunnerToken generates a new runner token for a given scope
diff --git a/routers/private/default_branch.go b/routers/private/default_branch.go
index af5d75634b..da185e1ab1 100644
--- a/routers/private/default_branch.go
+++ b/routers/private/default_branch.go
@@ -7,10 +7,10 @@ import (
"fmt"
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/private"
- gitea_context "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/private"
+ gitea_context "forgejo.org/services/context"
)
// SetDefaultBranch updates the default branch
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index 11d1161e85..a856a7a00a 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -10,25 +10,25 @@ import (
"strconv"
"time"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/git/pushoptions"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- timeutil "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- gitea_context "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/git/pushoptions"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ timeutil "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ gitea_context "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
// HookPostReceive updates services and users
@@ -205,7 +205,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
// post update for agit pull request
// FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR
- if git.SupportProcReceive && refFullName.IsPull() {
+ if refFullName.IsPull() {
if repo == nil {
repo = loadRepository(ctx, ownerName, repoName)
if ctx.Written() {
diff --git a/routers/private/hook_post_receive_test.go b/routers/private/hook_post_receive_test.go
index 28f1a7d0be..dde4ec08f4 100644
--- a/routers/private/hook_post_receive_test.go
+++ b/routers/private/hook_post_receive_test.go
@@ -6,15 +6,15 @@ package private
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/private"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/private"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -44,7 +44,7 @@ func TestHandlePullRequestMerging(t *testing.T) {
pr, err = issues_model.GetPullRequestByID(db.DefaultContext, pr.ID)
require.NoError(t, err)
assert.True(t, pr.HasMerged)
- assert.EqualValues(t, "01234567", pr.MergedCommitID)
+ assert.Equal(t, "01234567", pr.MergedCommitID)
unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{ID: autoMerge.ID})
}
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 4b8439d2da..45992e8522 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -9,22 +9,22 @@ import (
"net/http"
"os"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- perm_model "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- gitea_context "code.gitea.io/gitea/services/context"
- pull_service "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ perm_model "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ gitea_context "forgejo.org/services/context"
+ pull_service "forgejo.org/services/pull"
)
type preReceiveContext struct {
@@ -155,7 +155,7 @@ func (ctx *preReceiveContext) checkQuota() error {
return nil
}
- ok, err := quota_model.EvaluateForUser(ctx, ctx.PrivateContext.Repo.Repository.OwnerID, quota_model.LimitSubjectSizeReposAll)
+ ok, err := quota_model.EvaluateForUser(ctx, ctx.Repo.Repository.OwnerID, quota_model.LimitSubjectSizeReposAll)
if err != nil {
log.Error("quota_model.EvaluateForUser: %v", err)
ctx.JSON(http.StatusInternalServerError, private.Response{
@@ -205,7 +205,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName)
case refFullName.IsTag():
preReceiveTag(ourCtx, oldCommitID, newCommitID, refFullName)
- case git.SupportProcReceive && refFullName.IsFor():
+ case refFullName.IsFor():
preReceiveFor(ourCtx, oldCommitID, newCommitID, refFullName)
default:
if ourCtx.isOverQuota {
@@ -531,10 +531,7 @@ func preReceiveFor(ctx *preReceiveContext, oldCommitID, newCommitID string, refF
baseBranchName := refFullName.ForBranchName()
- baseBranchExist := false
- if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) {
- baseBranchExist = true
- }
+ baseBranchExist := ctx.Repo.GitRepo.IsBranchExist(baseBranchName)
if !baseBranchExist {
for p, v := range baseBranchName {
diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go
index e4aabd858c..9f6e23f158 100644
--- a/routers/private/hook_proc_receive.go
+++ b/routers/private/hook_proc_receive.go
@@ -6,22 +6,17 @@ package private
import (
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/agit"
- gitea_context "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/agit"
+ gitea_context "forgejo.org/services/context"
)
// HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present
func HookProcReceive(ctx *gitea_context.PrivateContext) {
opts := web.GetForm(ctx).(*private.HookOptions)
- if !git.SupportProcReceive {
- ctx.Status(http.StatusNotFound)
- return
- }
results, err := agit.ProcReceive(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, opts)
if err != nil {
diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go
index 764c976fa9..e9a1967bd2 100644
--- a/routers/private/hook_verification.go
+++ b/routers/private/hook_verification.go
@@ -10,9 +10,9 @@ import (
"io"
"os"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
// This file contains commit verification functions for refs passed across in hooks
diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go
index 5f0d1d0f4f..35458f672e 100644
--- a/routers/private/hook_verification_test.go
+++ b/routers/private/hook_verification_test.go
@@ -4,11 +4,10 @@
package private
import (
- "context"
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
"github.com/stretchr/testify/require"
)
@@ -18,7 +17,7 @@ var testReposDir = "tests/repos/"
func TestVerifyCommits(t *testing.T) {
unittest.PrepareTestEnv(t)
- gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification")
+ gitRepo, err := git.OpenRepository(t.Context(), testReposDir+"repo1_hook_verification")
defer gitRepo.Close()
require.NoError(t, err)
diff --git a/routers/private/internal.go b/routers/private/internal.go
index dfbdc6967b..5e8d51d970 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -9,11 +9,11 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
chi_middleware "github.com/go-chi/chi/v5/middleware"
diff --git a/routers/private/internal_repo.go b/routers/private/internal_repo.go
index e8ee8ba8ac..f237d2c676 100644
--- a/routers/private/internal_repo.go
+++ b/routers/private/internal_repo.go
@@ -8,11 +8,11 @@ import (
"fmt"
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- gitea_context "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ gitea_context "forgejo.org/services/context"
)
// This file contains common functions relating to setting the Repository for the internal routes
diff --git a/routers/private/key.go b/routers/private/key.go
index 5b8f238a83..2d77c9c5be 100644
--- a/routers/private/key.go
+++ b/routers/private/key.go
@@ -6,10 +6,10 @@ package private
import (
"net/http"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/services/context"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/services/context"
)
// UpdatePublicKeyInRepo update public key and deploy key updates
diff --git a/routers/private/mail.go b/routers/private/mail.go
index cf3abb31c6..2b96ce910e 100644
--- a/routers/private/mail.go
+++ b/routers/private/mail.go
@@ -9,14 +9,14 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/mailer"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/mailer"
)
// SendEmail pushes messages to mail queue
diff --git a/routers/private/main_test.go b/routers/private/main_test.go
index a6bec72b41..1b7f00f439 100644
--- a/routers/private/main_test.go
+++ b/routers/private/main_test.go
@@ -6,7 +6,7 @@ package private
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/private/manager.go b/routers/private/manager.go
index a6aa03e4ec..7ab198f71b 100644
--- a/routers/private/manager.go
+++ b/routers/private/manager.go
@@ -7,16 +7,16 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/graceful/releasereopen"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/graceful/releasereopen"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
)
// ReloadTemplates reloads all the templates
diff --git a/routers/private/manager_process.go b/routers/private/manager_process.go
index 9a0298a37c..e60ed04879 100644
--- a/routers/private/manager_process.go
+++ b/routers/private/manager_process.go
@@ -11,10 +11,10 @@ import (
"runtime"
"time"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- process_module "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ process_module "forgejo.org/modules/process"
+ "forgejo.org/services/context"
)
// Processes prints out the processes
@@ -122,7 +122,7 @@ func writeProcess(out io.Writer, process *process_module.Process, indent string,
if stack.Count > 1 {
_, _ = fmt.Fprintf(sb, "* %d", stack.Count)
}
- _, _ = fmt.Fprintf(sb, "\n")
+ _, _ = fmt.Fprintln(sb)
indent += "| "
if len(stack.Labels) > 0 {
_, _ = fmt.Fprintf(sb, "%sLabels: %q:%q", indent, stack.Labels[0].Name, stack.Labels[0].Value)
@@ -132,7 +132,7 @@ func writeProcess(out io.Writer, process *process_module.Process, indent string,
_, _ = fmt.Fprintf(sb, ", %q:%q", label.Name, label.Value)
}
}
- _, _ = fmt.Fprintf(sb, "\n")
+ _, _ = fmt.Fprintln(sb)
}
_, _ = fmt.Fprintf(sb, "%sStack:\n", indent)
indent += " "
diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go
index 0c63ebc918..c831b44036 100644
--- a/routers/private/manager_unix.go
+++ b/routers/private/manager_unix.go
@@ -1,15 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//go:build !windows
-
package private
import (
"net/http"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/services/context"
)
// Restart causes the server to perform a graceful restart
diff --git a/routers/private/restore_repo.go b/routers/private/restore_repo.go
index 4e95d3071d..6586c9bb2b 100644
--- a/routers/private/restore_repo.go
+++ b/routers/private/restore_repo.go
@@ -7,10 +7,10 @@ import (
"io"
"net/http"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/private"
- myCtx "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/migrations"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/private"
+ myCtx "forgejo.org/services/context"
+ "forgejo.org/services/migrations"
)
// RestoreRepo restore a repository from data
diff --git a/routers/private/serv.go b/routers/private/serv.go
index ef3920d359..a4029e354c 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -8,19 +8,18 @@ import (
"net/http"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
+ wiki_service "forgejo.org/services/wiki"
)
// ServNoCommand returns information about the provided keyid
@@ -296,8 +295,14 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
} else {
- // Because of the special ref "refs/for" we will need to delay write permission check
- if git.SupportProcReceive && unitType == unit.TypeCode {
+ // We don't know yet which references the Git client wants to write to,
+ // but for AGit flow we have to degrade this check to a read permission.
+ // So if we support proc-receive (needed for AGit flow) and the unit type
+ // is code and we know the Git client wants to write to us, then degrade
+ // the permission check to read. The pre-receive hook will do another
+ // permission check which ensure for non AGit flow references the write
+ // permission is checked.
+ if unitType == unit.TypeCode && ctx.FormString("verb") == "git-receive-pack" {
mode = perm.AccessModeRead
}
diff --git a/routers/private/ssh_log.go b/routers/private/ssh_log.go
index 5bec632ead..f6974967c0 100644
--- a/routers/private/ssh_log.go
+++ b/routers/private/ssh_log.go
@@ -6,11 +6,11 @@ package private
import (
"net/http"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
)
// SSHLog hook to response ssh log
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index 067203b28b..96a714376b 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -11,20 +11,20 @@ import (
"runtime"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/updatechecker"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/cron"
- "code.gitea.io/gitea/services/forms"
- release_service "code.gitea.io/gitea/services/release"
- repo_service "code.gitea.io/gitea/services/repository"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/updatechecker"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/cron"
+ "forgejo.org/services/forms"
+ release_service "forgejo.org/services/release"
+ repo_service "forgejo.org/services/repository"
)
const (
diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go
index 3518869ede..0bad4402aa 100644
--- a/routers/web/admin/admin_test.go
+++ b/routers/web/admin/admin_test.go
@@ -6,11 +6,11 @@ package admin
import (
"testing"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/services/contexttest"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
@@ -69,7 +69,7 @@ func TestShadowPassword(t *testing.T) {
}
for _, k := range kases {
- assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
+ assert.Equal(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
}
}
diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go
index 8583398074..ba15e0a000 100644
--- a/routers/web/admin/applications.go
+++ b/routers/web/admin/applications.go
@@ -7,12 +7,12 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- user_setting "code.gitea.io/gitea/routers/web/user/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ user_setting "forgejo.org/routers/web/user/setting"
+ "forgejo.org/services/context"
)
var (
diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go
index 799b7e8a84..2c6dc76305 100644
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -4,30 +4,26 @@
package admin
import (
- "errors"
"fmt"
"net/http"
"net/url"
- "regexp"
"strconv"
"strings"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/auth/pam"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- auth_service "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/ldap"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- pam_service "code.gitea.io/gitea/services/auth/source/pam"
- "code.gitea.io/gitea/services/auth/source/smtp"
- "code.gitea.io/gitea/services/auth/source/sspi"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/auth/pam"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ auth_service "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/ldap"
+ "forgejo.org/services/auth/source/oauth2"
+ pam_service "forgejo.org/services/auth/source/pam"
+ "forgejo.org/services/auth/source/smtp"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
"xorm.io/xorm/convert"
)
@@ -38,11 +34,6 @@ const (
tplAuthEdit base.TplName = "admin/auth/edit"
)
-var (
- separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`)
- langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`)
-)
-
// Authentications show authentication config page
func Authentications(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.authentication")
@@ -70,7 +61,6 @@ var (
{auth.DLDAP.String(), auth.DLDAP},
{auth.SMTP.String(), auth.SMTP},
{auth.OAuth2.String(), auth.OAuth2},
- {auth.SSPI.String(), auth.SSPI},
}
if pam.Supported {
items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM})
@@ -102,12 +92,6 @@ func NewAuthSource(ctx *context.Context) {
oauth2providers := oauth2.GetSupportedOAuth2Providers()
ctx.Data["OAuth2Providers"] = oauth2providers
- ctx.Data["SSPIAutoCreateUsers"] = true
- ctx.Data["SSPIAutoActivateUsers"] = true
- ctx.Data["SSPIStripDomainNames"] = true
- ctx.Data["SSPISeparatorReplacement"] = "_"
- ctx.Data["SSPIDefaultLanguage"] = ""
-
// only the first as default
ctx.Data["oauth2_provider"] = oauth2providers[0].Name()
@@ -197,6 +181,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
CustomURLMapping: customURLMapping,
IconURL: form.Oauth2IconURL,
Scopes: scopes,
+ AttributeSSHPublicKey: form.Oauth2AttributeSSHPublicKey,
RequiredClaimName: form.Oauth2RequiredClaimName,
RequiredClaimValue: form.Oauth2RequiredClaimValue,
SkipLocalTwoFA: form.SkipLocalTwoFA,
@@ -208,30 +193,6 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
}
}
-func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi.Source, error) {
- if util.IsEmptyString(form.SSPISeparatorReplacement) {
- ctx.Data["Err_SSPISeparatorReplacement"] = true
- return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.require_error"))
- }
- if separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) {
- ctx.Data["Err_SSPISeparatorReplacement"] = true
- return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.alpha_dash_dot_error"))
- }
-
- if form.SSPIDefaultLanguage != "" && !langCodePattern.MatchString(form.SSPIDefaultLanguage) {
- ctx.Data["Err_SSPIDefaultLanguage"] = true
- return nil, errors.New(ctx.Locale.TrString("form.lang_select_error"))
- }
-
- return &sspi.Source{
- AutoCreateUsers: form.SSPIAutoCreateUsers,
- AutoActivateUsers: form.SSPIAutoActivateUsers,
- StripDomainNames: form.SSPIStripDomainNames,
- SeparatorReplacement: form.SSPISeparatorReplacement,
- DefaultLanguage: form.SSPIDefaultLanguage,
- }, nil
-}
-
// NewAuthSourcePost response for adding an auth source
func NewAuthSourcePost(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.AuthenticationForm)
@@ -246,12 +207,6 @@ func NewAuthSourcePost(ctx *context.Context) {
oauth2providers := oauth2.GetSupportedOAuth2Providers()
ctx.Data["OAuth2Providers"] = oauth2providers
- ctx.Data["SSPIAutoCreateUsers"] = true
- ctx.Data["SSPIAutoActivateUsers"] = true
- ctx.Data["SSPIStripDomainNames"] = true
- ctx.Data["SSPISeparatorReplacement"] = "_"
- ctx.Data["SSPIDefaultLanguage"] = ""
-
hasTLS := false
var config convert.Conversion
switch auth.Type(form.Type) {
@@ -278,19 +233,6 @@ func NewAuthSourcePost(ctx *context.Context) {
return
}
}
- case auth.SSPI:
- var err error
- config, err = parseSSPIConfig(ctx, form)
- if err != nil {
- ctx.RenderWithErr(err.Error(), tplAuthNew, form)
- return
- }
- existing, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{LoginType: auth.SSPI})
- if err != nil || len(existing) > 0 {
- ctx.Data["Err_Type"] = true
- ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form)
- return
- }
default:
ctx.Error(http.StatusBadRequest)
return
@@ -407,12 +349,6 @@ func EditAuthSourcePost(ctx *context.Context) {
return
}
}
- case auth.SSPI:
- config, err = parseSSPIConfig(ctx, form)
- if err != nil {
- ctx.RenderWithErr(err.Error(), tplAuthEdit, form)
- return
- }
default:
ctx.Error(http.StatusBadRequest)
return
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index 06d0ea60fb..dcc99ff1a8 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -1,5 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package admin
@@ -10,17 +11,17 @@ import (
"strconv"
"strings"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/setting/config"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/mailer"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/setting/config"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ "forgejo.org/services/mailer"
"code.forgejo.org/go-chi/session"
)
@@ -145,6 +146,7 @@ func Config(ctx *context.Context) {
ctx.Data["Service"] = setting.Service
ctx.Data["DbCfg"] = setting.Database
ctx.Data["Webhook"] = setting.Webhook
+ ctx.Data["Moderation"] = setting.Moderation
ctx.Data["MailerEnabled"] = false
if setting.MailService != nil {
diff --git a/routers/web/admin/diagnosis.go b/routers/web/admin/diagnosis.go
index 020554a35a..8b0ec45214 100644
--- a/routers/web/admin/diagnosis.go
+++ b/routers/web/admin/diagnosis.go
@@ -5,27 +5,26 @@ package admin
import (
"archive/zip"
+ "bytes"
"fmt"
+ "io"
+ "runtime"
"runtime/pprof"
+ "runtime/trace"
"time"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/httplib"
+ "forgejo.org/services/context"
)
func MonitorDiagnosis(ctx *context.Context) {
seconds := ctx.FormInt64("seconds")
- if seconds <= 5 {
- seconds = 5
- }
- if seconds > 300 {
- seconds = 300
- }
+ seconds = max(5, min(300, seconds))
httplib.ServeSetHeaders(ctx.Resp, &httplib.ServeHeaderOptions{
ContentType: "application/zip",
Disposition: "attachment",
- Filename: fmt.Sprintf("gitea-diagnosis-%s.zip", time.Now().Format("20060102-150405")),
+ Filename: fmt.Sprintf("forgejo-diagnosis-%s.zip", time.Now().Format("20060102-150405")),
})
zipWriter := zip.NewWriter(ctx.Resp)
@@ -44,14 +43,33 @@ func MonitorDiagnosis(ctx *context.Context) {
return
}
- err = pprof.StartCPUProfile(f)
- if err == nil {
- time.Sleep(time.Duration(seconds) * time.Second)
- pprof.StopCPUProfile()
- } else {
+ if err := pprof.StartCPUProfile(f); err != nil {
_, _ = f.Write([]byte(err.Error()))
}
+ traceBuf := &bytes.Buffer{}
+ if err := trace.Start(traceBuf); err != nil {
+ _, _ = traceBuf.Write([]byte(err.Error()))
+ }
+
+ select {
+ case <-time.After(time.Duration(seconds) * time.Second):
+ case <-ctx.Done():
+ }
+ pprof.StopCPUProfile()
+ trace.Stop()
+
+ f, err = zipWriter.CreateHeader(&zip.FileHeader{Name: "trace.dat", Method: zip.Deflate, Modified: time.Now()})
+ if err != nil {
+ ctx.ServerError("Failed to create zip file", err)
+ return
+ }
+
+ if _, err := io.Copy(f, traceBuf); err != nil {
+ ctx.ServerError("Failed to create zip file", err)
+ return
+ }
+
f, err = zipWriter.CreateHeader(&zip.FileHeader{Name: "goroutine-after.txt", Method: zip.Deflate, Modified: time.Now()})
if err != nil {
ctx.ServerError("Failed to create zip file", err)
@@ -64,5 +82,8 @@ func MonitorDiagnosis(ctx *context.Context) {
ctx.ServerError("Failed to create zip file", err)
return
}
+ // To avoid showing memory that actually can be cleaned, run the garbage
+ // collector.
+ runtime.GC()
_ = pprof.Lookup("heap").WriteTo(f, 0)
}
diff --git a/routers/web/admin/emails.go b/routers/web/admin/emails.go
index f0d8555070..a4421cf495 100644
--- a/routers/web/admin/emails.go
+++ b/routers/web/admin/emails.go
@@ -8,14 +8,14 @@ import (
"net/http"
"net/url"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/user"
)
const (
diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go
index cdca0a5c2d..aeceffe848 100644
--- a/routers/web/admin/hooks.go
+++ b/routers/web/admin/hooks.go
@@ -6,11 +6,11 @@ package admin
import (
"net/http"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
const (
diff --git a/routers/web/admin/main_test.go b/routers/web/admin/main_test.go
index e1294ddbb4..bccb0d7058 100644
--- a/routers/web/admin/main_test.go
+++ b/routers/web/admin/main_test.go
@@ -6,7 +6,7 @@ package admin
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/admin/notice.go b/routers/web/admin/notice.go
index 36303cbc06..8bcaadf915 100644
--- a/routers/web/admin/notice.go
+++ b/routers/web/admin/notice.go
@@ -8,12 +8,12 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models/db"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go
index cea28f8220..6ece35dcaf 100644
--- a/routers/web/admin/orgs.go
+++ b/routers/web/admin/orgs.go
@@ -5,13 +5,13 @@
package admin
import (
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/routers/web/explore"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/routers/web/explore"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go
index 39f064a1be..5c80a1eada 100644
--- a/routers/web/admin/packages.go
+++ b/routers/web/admin/packages.go
@@ -8,14 +8,14 @@ import (
"net/url"
"time"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- packages_service "code.gitea.io/gitea/services/packages"
- packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ packages_service "forgejo.org/services/packages"
+ packages_cleanup_service "forgejo.org/services/packages/cleanup"
)
const (
diff --git a/routers/web/admin/queue.go b/routers/web/admin/queue.go
index 246ab379b5..03bbfe5af4 100644
--- a/routers/web/admin/queue.go
+++ b/routers/web/admin/queue.go
@@ -7,9 +7,9 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
func Queues(ctx *context.Context) {
diff --git a/routers/web/admin/queue_tester.go b/routers/web/admin/queue_tester.go
index 8f713b3bb1..831947fe41 100644
--- a/routers/web/admin/queue_tester.go
+++ b/routers/web/admin/queue_tester.go
@@ -8,11 +8,11 @@ import (
"sync"
"time"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
)
var testQueueOnce sync.Once
diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go
index d0339fdd93..a94b9bb5c3 100644
--- a/routers/web/admin/repos.go
+++ b/routers/web/admin/repos.go
@@ -8,16 +8,16 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/web/explore"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/web/explore"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
const (
diff --git a/routers/web/admin/runners.go b/routers/web/admin/runners.go
index d73290a8db..c6451a9329 100644
--- a/routers/web/admin/runners.go
+++ b/routers/web/admin/runners.go
@@ -4,8 +4,8 @@
package admin
import (
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
diff --git a/routers/web/admin/stacktrace.go b/routers/web/admin/stacktrace.go
index d6def94bb4..7c6cd98a56 100644
--- a/routers/web/admin/stacktrace.go
+++ b/routers/web/admin/stacktrace.go
@@ -7,9 +7,9 @@ import (
"net/http"
"runtime"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// Stacktrace show admin monitor goroutines page
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index fb4a1ffa61..964326291e 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -11,26 +11,25 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/web/explore"
- user_setting "code.gitea.io/gitea/routers/web/user/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/web/explore"
+ user_setting "forgejo.org/routers/web/user/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
+ user_service "forgejo.org/services/user"
)
const (
@@ -77,12 +76,13 @@ func Users(ctx *context.Context) {
PageSize: setting.UI.Admin.UserPagingNum,
},
SearchByEmail: true,
- IsActive: util.OptionalBoolParse(statusFilterMap["is_active"]),
- IsAdmin: util.OptionalBoolParse(statusFilterMap["is_admin"]),
- IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
- IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
- IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
+ IsActive: optional.ParseBool(statusFilterMap["is_active"]),
+ IsAdmin: optional.ParseBool(statusFilterMap["is_admin"]),
+ IsRestricted: optional.ParseBool(statusFilterMap["is_restricted"]),
+ IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]),
+ IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
+ Load2FAStatus: true,
ExtraParamStrings: extraParamStrings,
}, tplUsers)
}
@@ -186,7 +186,7 @@ func NewUserPost(ctx *context.Context) {
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form)
- case validation.IsErrEmailInvalid(err), validation.IsErrEmailCharIsNotSupported(err):
+ case validation.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form)
case db.IsErrNameReserved(err):
@@ -195,9 +195,6 @@ func NewUserPost(ctx *context.Context) {
case db.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_UserName"] = true
ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
- case db.IsErrNameCharsNotAllowed(err):
- ctx.Data["Err_UserName"] = true
- ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
default:
ctx.ServerError("CreateUser", err)
}
@@ -248,17 +245,12 @@ func prepareUserInfo(ctx *context.Context) *user_model.User {
}
ctx.Data["Sources"] = sources
- hasTOTP, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
if err != nil {
- ctx.ServerError("auth.HasTwoFactorByUID", err)
+ ctx.ServerError("HasTwoFactorByUID", err)
return nil
}
- hasWebAuthn, err := auth.HasWebAuthnRegistrationsByUID(ctx, u.ID)
- if err != nil {
- ctx.ServerError("auth.HasWebAuthnRegistrationsByUID", err)
- return nil
- }
- ctx.Data["TwoFactorEnabled"] = hasTOTP || hasWebAuthn
+ ctx.Data["TwoFactorEnabled"] = hasTwoFactor
return u
}
@@ -321,6 +313,9 @@ func editUserCommon(ctx *context.Context) {
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
+ ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
+ ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
+ ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
}
// EditUser show editing user page
@@ -349,7 +344,7 @@ func EditUserPost(ctx *context.Context) {
}
if form.UserName != "" {
- if err := user_service.RenameUser(ctx, u, form.UserName); err != nil {
+ if err := user_service.AdminRenameUser(ctx, u, form.UserName); err != nil {
switch {
case user_model.IsErrUserIsNotLocal(err):
ctx.Data["Err_UserName"] = true
@@ -415,7 +410,7 @@ func EditUserPost(ctx *context.Context) {
if form.Email != "" {
if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil {
switch {
- case validation.IsErrEmailCharIsNotSupported(err), validation.IsErrEmailInvalid(err):
+ case validation.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form)
case user_model.IsErrEmailAlreadyUsed(err):
diff --git a/routers/web/admin/users_test.go b/routers/web/admin/users_test.go
index ae3b130101..c8e6f8cb86 100644
--- a/routers/web/admin/users_test.go
+++ b/routers/web/admin/users_test.go
@@ -6,13 +6,13 @@ package admin
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go
index f93177bf96..ff769ffd5d 100644
--- a/routers/web/auth/2fa.go
+++ b/routers/web/auth/2fa.go
@@ -7,14 +7,14 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/externalaccount"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/externalaccount"
+ "forgejo.org/services/forms"
)
var (
@@ -133,11 +133,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
// Validate the passcode with the stored TOTP secret.
if twofa.VerifyScratchToken(form.Token) {
// Invalidate the scratch token.
- _, err = twofa.GenerateScratchToken()
- if err != nil {
- ctx.ServerError("UserSignIn", err)
- return
- }
+ twofa.GenerateScratchToken()
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
ctx.ServerError("UserSignIn", err)
return
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 2afaad45a2..dbb6665398 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -9,31 +9,32 @@ import (
"fmt"
"net/http"
"strings"
+ "time"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/eventsource"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/session"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- auth_service "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/externalaccount"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
- notify_service "code.gitea.io/gitea/services/notify"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/eventsource"
+ "forgejo.org/modules/httplib"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/session"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ auth_service "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/context"
+ "forgejo.org/services/externalaccount"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
+ notify_service "forgejo.org/services/notify"
+ user_service "forgejo.org/services/user"
"github.com/markbates/goth"
)
@@ -163,7 +164,6 @@ func SignIn(ctx *context.Context) {
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
- ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn
if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
@@ -189,7 +189,6 @@ func SignInPost(ctx *context.Context) {
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
- ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn
ctx.Data["DisablePassword"] = !setting.Service.EnableInternalSignIn
@@ -227,15 +226,6 @@ func SignInPost(ctx *context.Context) {
log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
- } else if user_model.IsErrUserInactive(err) {
- if setting.Service.RegisterEmailConfirm {
- ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
- ctx.HTML(http.StatusOK, TplActivate)
- } else {
- log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
- ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
- ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
- }
} else {
ctx.ServerError("UserSignIn", err)
}
@@ -252,7 +242,7 @@ func SignInPost(ctx *context.Context) {
// If this user is enrolled in 2FA TOTP, we can't sign the user in just yet.
// Instead, redirect them to the 2FA authentication page.
- hasTOTPtwofa, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ hasTOTPtwofa, err := auth.HasTOTPByUID(ctx, u.ID)
if err != nil {
ctx.ServerError("UserSignIn", err)
return
@@ -522,7 +512,8 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any
func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) {
if err := user_model.CreateUser(ctx, u, overwrites); err != nil {
if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
- if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
+ switch setting.OAuth2Client.AccountLinking {
+ case setting.OAuth2AccountLinkingAuto:
var user *user_model.User
user = &user_model.User{Name: u.Name}
hasUser, err := user_model.GetUser(ctx, user)
@@ -538,7 +529,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us
// TODO: probably we should respect 'remember' user's choice...
linkAccount(ctx, user, *gothUser, true)
return false // user is already created here, all redirects are handled
- } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin {
+ case setting.OAuth2AccountLinkingLogin:
showLinkingLogin(ctx, *gothUser)
return false // user will be created only after linking login
}
@@ -558,9 +549,9 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form)
- case validation.IsErrEmailCharIsNotSupported(err):
- ctx.Data["Err_Email"] = true
- ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
+ case user_model.IsErrCooldownPeriod(err):
+ ctx.Data["Err_UserName"] = true
+ ctx.RenderWithErr(ctx.Locale.Tr("form.username_claiming_cooldown", err.(user_model.ErrCooldownPeriod).ExpireTime.Format(time.RFC1123Z)), tpl, form)
case validation.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
@@ -773,7 +764,7 @@ func ActivatePost(ctx *context.Context) {
ctx.HTML(http.StatusOK, TplActivate)
return
}
- if !user.ValidatePassword(password) {
+ if !user.ValidatePassword(ctx, password) {
ctx.Data["IsPasswordInvalid"] = true
ctx.HTML(http.StatusOK, TplActivate)
return
@@ -790,11 +781,7 @@ func ActivatePost(ctx *context.Context) {
func handleAccountActivation(ctx *context.Context, user *user_model.User) {
user.IsActive = true
- var err error
- if user.Rands, err = user_model.GetUserSalt(); err != nil {
- ctx.ServerError("UpdateUser", err)
- return
- }
+ user.Rands = user_model.GetUserSalt()
if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.NotFound("UpdateUserCols", err)
diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go
index c6afbf877c..7a33a3841c 100644
--- a/routers/web/auth/auth_test.go
+++ b/routers/web/auth/auth_test.go
@@ -8,8 +8,8 @@ import (
"net/url"
"testing"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/modules/test"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go
index 4b85250915..2bba614d8c 100644
--- a/routers/web/auth/linkaccount.go
+++ b/routers/web/auth/linkaccount.go
@@ -9,18 +9,18 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- auth_service "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/externalaccount"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ auth_service "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/context"
+ "forgejo.org/services/externalaccount"
+ "forgejo.org/services/forms"
"github.com/markbates/goth"
)
@@ -100,16 +100,6 @@ func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl
log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
- } else if user_model.IsErrUserInactive(err) {
- ctx.Data["user_exists"] = true
- if setting.Service.RegisterEmailConfirm {
- ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
- ctx.HTML(http.StatusOK, TplActivate)
- } else {
- log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err)
- ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
- ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
- }
} else {
ctx.ServerError(invoker, err)
}
@@ -165,15 +155,14 @@ func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, r
// If this user is enrolled in 2FA, we can't sign the user in just yet.
// Instead, redirect them to the 2FA authentication page.
// We deliberately ignore the skip local 2fa setting here because we are linking to a previous user here
- _, err := auth.GetTwoFactorByUID(ctx, u.ID)
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
if err != nil {
- if !auth.IsErrTwoFactorNotEnrolled(err) {
- ctx.ServerError("UserLinkAccount", err)
- return
- }
+ ctx.ServerError("HasTwoFactorByUID", err)
+ return
+ }
- err = externalaccount.LinkAccountToUser(ctx, u, gothUser)
- if err != nil {
+ if !hasTwoFactor {
+ if err := externalaccount.LinkAccountToUser(ctx, u, gothUser); err != nil {
ctx.ServerError("UserLinkAccount", err)
return
}
diff --git a/routers/web/auth/main_test.go b/routers/web/auth/main_test.go
index b438e5d518..a8a32b71f2 100644
--- a/routers/web/auth/main_test.go
+++ b/routers/web/auth/main_test.go
@@ -6,7 +6,7 @@ package auth
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index fbdd47479a..e8e5d2c54b 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -17,28 +17,29 @@ import (
"sort"
"strings"
- "code.gitea.io/gitea/models/auth"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- auth_module "code.gitea.io/gitea/modules/auth"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- auth_service "code.gitea.io/gitea/services/auth"
- source_service "code.gitea.io/gitea/services/auth/source"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/externalaccount"
- "code.gitea.io/gitea/services/forms"
- remote_service "code.gitea.io/gitea/services/remote"
- user_service "code.gitea.io/gitea/services/user"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ org_model "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ auth_module "forgejo.org/modules/auth"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ auth_service "forgejo.org/services/auth"
+ source_service "forgejo.org/services/auth/source"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/context"
+ "forgejo.org/services/externalaccount"
+ "forgejo.org/services/forms"
+ remote_service "forgejo.org/services/remote"
+ user_service "forgejo.org/services/user"
"code.forgejo.org/go-chi/binding"
"github.com/golang-jwt/jwt/v5"
@@ -224,7 +225,7 @@ func newAccessTokenResponse(ctx go_context.Context, grant *auth.OAuth2Grant, ser
idToken := &oauth2.OIDCToken{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationDate.AsTime()),
- Issuer: setting.AppURL,
+ Issuer: strings.TrimSuffix(setting.AppURL, "/"),
Audience: []string{app.ClientID},
Subject: fmt.Sprint(grant.UserID),
},
@@ -408,7 +409,7 @@ func IntrospectOAuth(ctx *context.Context) {
if err == nil && app != nil {
response.Active = true
response.Scope = grant.Scope
- response.Issuer = setting.AppURL
+ response.Issuer = strings.TrimSuffix(setting.AppURL, "/")
response.Audience = []string{app.ClientID}
response.Subject = fmt.Sprint(grant.UserID)
}
@@ -668,6 +669,7 @@ func GrantApplicationOAuth(ctx *context.Context) {
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
func OIDCWellKnown(ctx *context.Context) {
ctx.Data["SigningKey"] = oauth2.DefaultSigningKey
+ ctx.Data["Issuer"] = strings.TrimSuffix(setting.AppURL, "/")
ctx.JSONTemplate("user/auth/oidc_wellknown")
}
@@ -1078,7 +1080,7 @@ func SignInOAuthCallback(ctx *context.Context) {
isAdmin, isRestricted := getUserAdminAndRestrictedFromGroupClaims(source, &gothUser)
u.IsAdmin = isAdmin.ValueOrDefault(false)
- u.IsRestricted = isRestricted.ValueOrDefault(false)
+ u.IsRestricted = isRestricted.ValueOrDefault(setting.Service.DefaultUserIsRestricted)
if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
// error already handled
@@ -1183,17 +1185,70 @@ func updateAvatarIfNeed(ctx *context.Context, url string, u *user_model.User) {
}
}
+func getSSHKeys(source *oauth2.Source, gothUser *goth.User) ([]string, error) {
+ key := source.AttributeSSHPublicKey
+ value, exists := gothUser.RawData[key]
+ if !exists {
+ return []string{}, nil
+ }
+
+ rawSlice, ok := value.([]any)
+ if !ok {
+ return nil, fmt.Errorf("unexpected type for SSH public key, expected []interface{} but got %T", value)
+ }
+
+ sshKeys := make([]string, 0, len(rawSlice))
+ for i, v := range rawSlice {
+ str, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf("unexpected element type at index %d in SSH public key array, expected string but got %T", i, v)
+ }
+ sshKeys = append(sshKeys, str)
+ }
+
+ return sshKeys, nil
+}
+
+func updateSSHPubIfNeed(
+ ctx *context.Context,
+ authSource *auth.Source,
+ fetchedUser *goth.User,
+ user *user_model.User,
+) error {
+ oauth2Source := authSource.Cfg.(*oauth2.Source)
+
+ if oauth2Source.ProvidesSSHKeys() {
+ sshKeys, err := getSSHKeys(oauth2Source, fetchedUser)
+ if err != nil {
+ return err
+ }
+
+ if asymkey_model.SynchronizePublicKeys(ctx, user, authSource, sshKeys) {
+ err = asymkey_model.RewriteAllPublicKeys(ctx)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) {
updateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
+ err := updateSSHPubIfNeed(ctx, source, &gothUser, u)
+ if err != nil {
+ ctx.ServerError("updateSSHPubIfNeed", err)
+ return
+ }
needs2FA := false
if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA {
- _, err := auth.GetTwoFactorByUID(ctx, u.ID)
- if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
+ needs2FA, err = auth.HasTwoFactorByUID(ctx, u.ID)
+ if err != nil {
ctx.ServerError("UserSignIn", err)
return
}
- needs2FA = err == nil
}
oauth2Source := source.Cfg.(*oauth2.Source)
diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go
index a5f2dd7713..9782711dd0 100644
--- a/routers/web/auth/oauth_test.go
+++ b/routers/web/auth/oauth_test.go
@@ -6,12 +6,12 @@ package auth
import (
"testing"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/services/auth/source/oauth2"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/services/auth/source/oauth2"
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
@@ -51,6 +51,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) {
// Scopes: openid
oidcToken := createAndParseToken(t, grants[0])
+ assert.Equal(t, "https://try.gitea.io", oidcToken.RegisteredClaims.Issuer)
assert.Empty(t, oidcToken.Name)
assert.Empty(t, oidcToken.PreferredUsername)
assert.Empty(t, oidcToken.Profile)
@@ -67,11 +68,12 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) {
// Scopes: openid profile email
oidcToken = createAndParseToken(t, grants[0])
+ assert.Equal(t, "https://try.gitea.io", oidcToken.RegisteredClaims.Issuer)
assert.Equal(t, "User Five", oidcToken.Name)
assert.Equal(t, "user5", oidcToken.PreferredUsername)
assert.Equal(t, "https://try.gitea.io/user5", oidcToken.Profile)
assert.Equal(t, "https://try.gitea.io/assets/img/avatar_default.png", oidcToken.Picture)
- assert.Equal(t, "", oidcToken.Website)
+ assert.Empty(t, oidcToken.Website)
assert.Equal(t, timeutil.TimeStamp(0), oidcToken.UpdatedAt)
assert.Equal(t, "user5@example.com", oidcToken.Email)
assert.True(t, oidcToken.EmailVerified)
diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go
index 83268faacb..fcb2155953 100644
--- a/routers/web/auth/openid.go
+++ b/routers/web/auth/openid.go
@@ -4,20 +4,21 @@
package auth
import (
+ "errors"
"fmt"
"net/http"
"net/url"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/openid"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/openid"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -55,13 +56,13 @@ func allowedOpenIDURI(uri string) (err error) {
}
}
// must match one of this or be refused
- return fmt.Errorf("URI not allowed by whitelist")
+ return errors.New("URI not allowed by whitelist")
}
// A blacklist match expliclty forbids
for _, pat := range setting.Service.OpenIDBlacklist {
if pat.MatchString(uri) {
- return fmt.Errorf("URI forbidden by blacklist")
+ return errors.New("URI forbidden by blacklist")
}
}
diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go
index 84f343bfca..cb6b22e5b7 100644
--- a/routers/web/auth/password.go
+++ b/routers/web/auth/password.go
@@ -8,20 +8,20 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
+ user_service "forgejo.org/services/user"
)
var (
@@ -242,12 +242,8 @@ func ResetPasswdPost(ctx *context.Context) {
if regenerateScratchToken {
// Invalidate the scratch token.
- _, err := twofa.GenerateScratchToken()
- if err != nil {
- ctx.ServerError("UserSignIn", err)
- return
- }
- if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
+ twofa.GenerateScratchToken()
+ if err := auth.UpdateTwoFactor(ctx, twofa); err != nil {
ctx.ServerError("UserSignIn", err)
return
}
diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go
index 5c93c1410e..3da6199b6e 100644
--- a/routers/web/auth/webauthn.go
+++ b/routers/web/auth/webauthn.go
@@ -7,14 +7,14 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- wa "code.gitea.io/gitea/modules/auth/webauthn"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/externalaccount"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ wa "forgejo.org/modules/auth/webauthn"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/externalaccount"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
@@ -36,7 +36,7 @@ func WebAuthn(ctx *context.Context) {
return
}
- hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, ctx.Session.Get("twofaUid").(int64))
+ hasTwoFactor, err := auth.HasTOTPByUID(ctx, ctx.Session.Get("twofaUid").(int64))
if err != nil {
ctx.ServerError("HasTwoFactorByUID", err)
return
diff --git a/routers/web/base.go b/routers/web/base.go
index 285d1ecddc..c1bc7fef5e 100644
--- a/routers/web/base.go
+++ b/routers/web/base.go
@@ -11,12 +11,12 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/routing"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web/routing"
)
func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) http.HandlerFunc {
diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go
index dd20663f94..9b5804b976 100644
--- a/routers/web/devtest/devtest.go
+++ b/routers/web/devtest/devtest.go
@@ -1,17 +1,21 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package devtest
import (
+ "errors"
"net/http"
"path"
"strings"
"time"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/asymkey"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/templates"
+ "forgejo.org/services/context"
)
// List all devtest templates, they will be used for e2e tests for the UI components
@@ -42,6 +46,17 @@ func FetchActionTest(ctx *context.Context) {
ctx.JSONRedirect("")
}
+func ErrorPage(ctx *context.Context) {
+ if ctx.Params("errcode") == "404" {
+ ctx.NotFound("Example error", errors.New("Example error"))
+ return
+ } else if ctx.Params("errcode") == "413" {
+ ctx.HTML(http.StatusRequestEntityTooLarge, base.TplName("status/413"))
+ return
+ }
+ ctx.ServerError("Example error", errors.New("Example error"))
+}
+
func Tmpl(ctx *context.Context) {
now := time.Now()
ctx.Data["TimeNow"] = now
@@ -52,6 +67,19 @@ func Tmpl(ctx *context.Context) {
ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second)
ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second)
+ userNonZero := &user.User{ID: 1}
+ ctx.Data["TrustedVerif"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userNonZero, TrustStatus: "trusted"}
+ ctx.Data["UntrustedVerif"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userNonZero, TrustStatus: "untrusted"}
+ ctx.Data["UnmatchedVerif"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userNonZero, TrustStatus: ""}
+ ctx.Data["WarnVerif"] = &asymkey.ObjectVerification{Verified: false, Warning: true, Reason: asymkey.NotSigned, SigningUser: userNonZero}
+ ctx.Data["UnknownVerif"] = &asymkey.ObjectVerification{Verified: false, Warning: false, Reason: asymkey.NotSigned, SigningUser: userNonZero}
+ userUnknown := &user.User{ID: 0}
+ ctx.Data["TrustedVerifUnk"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userUnknown, TrustStatus: "trusted"}
+ ctx.Data["UntrustedVerifUnk"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userUnknown, TrustStatus: "untrusted"}
+ ctx.Data["UnmatchedVerifUnk"] = &asymkey.ObjectVerification{Verified: true, Reason: asymkey.NotSigned, SigningUser: userUnknown, TrustStatus: ""}
+ ctx.Data["WarnVerifUnk"] = &asymkey.ObjectVerification{Verified: false, Warning: true, Reason: asymkey.NotSigned, SigningUser: userUnknown}
+ ctx.Data["UnknownVerifUnk"] = &asymkey.ObjectVerification{Verified: false, Warning: false, Reason: asymkey.NotSigned, SigningUser: userUnknown}
+
if ctx.Req.Method == "POST" {
_ = ctx.Req.ParseForm()
ctx.Flash.Info("form: "+ctx.Req.Method+" "+ctx.Req.RequestURI+"
"+
diff --git a/routers/web/events/events.go b/routers/web/events/events.go
index 52f20e07dc..1672f12bda 100644
--- a/routers/web/events/events.go
+++ b/routers/web/events/events.go
@@ -7,11 +7,11 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/modules/eventsource"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/routers/web/auth"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/eventsource"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/routers/web/auth"
+ "forgejo.org/services/context"
)
// Events listens for events
diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go
index 76238e80fb..f0b12e9142 100644
--- a/routers/web/explore/code.go
+++ b/routers/web/explore/code.go
@@ -6,12 +6,12 @@ package explore
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/base"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ code_indexer "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
@@ -37,19 +37,17 @@ func Code(ctx *context.Context) {
keyword := ctx.FormTrim("q")
path := ctx.FormTrim("path")
- isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
- if mode := ctx.FormTrim("mode"); len(mode) > 0 {
- isFuzzy = mode == "fuzzy"
+ mode := code_indexer.SearchModeExact
+ if m := ctx.FormTrim("mode"); m == "union" ||
+ m == "fuzzy" ||
+ ctx.FormBool("fuzzy") {
+ mode = code_indexer.SearchModeUnion
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
- ctx.Data["CodeSearchOptions"] = []string{"exact", "fuzzy"}
- if isFuzzy {
- ctx.Data["CodeSearchMode"] = "fuzzy"
- } else {
- ctx.Data["CodeSearchMode"] = "exact"
- }
+ ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions
+ ctx.Data["CodeSearchMode"] = mode.String()
ctx.Data["PageIsViewCode"] = true
if keyword == "" {
@@ -88,11 +86,11 @@ func Code(ctx *context.Context) {
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: repoIDs,
- Keyword: keyword,
- IsKeywordFuzzy: isFuzzy,
- Language: language,
- Filename: path,
+ RepoIDs: repoIDs,
+ Keyword: keyword,
+ Mode: mode,
+ Language: language,
+ Filename: path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go
index d13271ae53..6c9293e959 100644
--- a/routers/web/explore/org.go
+++ b/routers/web/explore/org.go
@@ -4,12 +4,12 @@
package explore
import (
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
)
// Organizations render explore organizations page
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index 798fdf5654..0707420a8d 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -7,13 +7,13 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/sitemap"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/sitemap"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/explore/topic.go b/routers/web/explore/topic.go
index 95fecfe2b8..3b67bd48b1 100644
--- a/routers/web/explore/topic.go
+++ b/routers/web/explore/topic.go
@@ -6,11 +6,11 @@ package explore
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// TopicSearch search for creating topic
diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go
index da5dd39f3e..3d4dbcd104 100644
--- a/routers/web/explore/user.go
+++ b/routers/web/explore/user.go
@@ -7,20 +7,20 @@ import (
"bytes"
"net/http"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/sitemap"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/sitemap"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
)
const (
- // tplExploreUsers explore users page template
+ // `tplExploreUsers` explore users page template.
tplExploreUsers base.TplName = "explore/users"
)
@@ -30,9 +30,9 @@ func isKeywordValid(keyword string) bool {
return !bytes.Contains([]byte(keyword), nullByte)
}
-// RenderUserSearch render user search page
+// `RenderUserSearch` render user search page.
func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName base.TplName) {
- // Sitemap index for sitemap paths
+ // Sitemap index for sitemap paths.
opts.Page = int(ctx.ParamsInt64("idx"))
isSitemap := ctx.Params("idx") != ""
if opts.Page <= 1 {
@@ -47,42 +47,24 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
}
var (
- users []*user_model.User
- count int64
- err error
- orderBy db.SearchOrderBy
+ users []*user_model.User
+ count int64
+ err error
)
- // we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
-
sortOrder := ctx.FormString("sort")
if sortOrder == "" {
sortOrder = setting.UI.ExploreDefaultSort
}
ctx.Data["SortType"] = sortOrder
- switch sortOrder {
- case "newest":
- orderBy = "`user`.id DESC"
- case "oldest":
- orderBy = "`user`.id ASC"
- case "leastupdate":
- orderBy = "`user`.updated_unix ASC"
- case "reversealphabetically":
- orderBy = "`user`.name DESC"
- case "lastlogin":
- orderBy = "`user`.last_login_unix ASC"
- case "reverselastlogin":
- orderBy = "`user`.last_login_unix DESC"
- case "alphabetically":
- orderBy = "`user`.name ASC"
- case "recentupdate":
- fallthrough
- default:
- // in case the sortType is not valid, we set it to recentupdate
+ orderBy := MapSortOrder(sortOrder)
+
+ if orderBy == "" {
+ // In case the `sortType` is not valid, we set it to `recentupdate`.
sortOrder = "recentupdate"
ctx.Data["SortType"] = "recentupdate"
- orderBy = "`user`.updated_unix DESC"
+ orderBy = MapSortOrder(sortOrder)
}
if opts.SupportedSortOrders != nil && !opts.SupportedSortOrders.Contains(sortOrder) {
@@ -114,7 +96,9 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
ctx.Data["Keyword"] = opts.Keyword
ctx.Data["Total"] = count
ctx.Data["Users"] = users
- ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx)
+ if opts.Load2FAStatus {
+ ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx)
+ }
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
@@ -128,7 +112,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
ctx.HTML(http.StatusOK, tplName)
}
-// Users render explore users page
+// Users render explore users page.
func Users(ctx *context.Context) {
if setting.Service.Explore.DisableUsersPage {
ctx.Redirect(setting.AppSubURL + "/explore")
@@ -167,3 +151,37 @@ func Users(ctx *context.Context) {
SupportedSortOrders: supportedSortOrders,
}, tplExploreUsers)
}
+
+// Maps a sort query to a database search order.
+//
+// We cannot use `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns.
+func MapSortOrder(sortOrder string) db.SearchOrderBy {
+ switch sortOrder {
+ case "newest":
+ return "`user`.created_unix DESC"
+
+ case "oldest":
+ return "`user`.created_unix ASC"
+
+ case "leastupdate":
+ return "`user`.updated_unix ASC"
+
+ case "reversealphabetically":
+ return "`user`.name DESC"
+
+ case "lastlogin":
+ return "`user`.last_login_unix ASC"
+
+ case "reverselastlogin":
+ return "`user`.last_login_unix DESC"
+
+ case "alphabetically":
+ return "`user`.name ASC"
+
+ case "recentupdate":
+ return "`user`.updated_unix DESC"
+
+ default:
+ return ""
+ }
+}
diff --git a/routers/web/explore/user_test.go b/routers/web/explore/user_test.go
new file mode 100644
index 0000000000..04573a149f
--- /dev/null
+++ b/routers/web/explore/user_test.go
@@ -0,0 +1,23 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package explore
+
+import (
+ "testing"
+
+ "forgejo.org/models/db"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMapSortOrder(t *testing.T) {
+ assert.Equal(t, MapSortOrder("newest"), db.SearchOrderBy("`user`.created_unix DESC"))
+ assert.Equal(t, MapSortOrder("oldest"), db.SearchOrderBy("`user`.created_unix ASC"))
+ assert.Equal(t, MapSortOrder("leastupdate"), db.SearchOrderBy("`user`.updated_unix ASC"))
+ assert.Equal(t, MapSortOrder("reversealphabetically"), db.SearchOrderBy("`user`.name DESC"))
+ assert.Equal(t, MapSortOrder("lastlogin"), db.SearchOrderBy("`user`.last_login_unix ASC"))
+ assert.Equal(t, MapSortOrder("reverselastlogin"), db.SearchOrderBy("`user`.last_login_unix DESC"))
+ assert.Equal(t, MapSortOrder("alphabetically"), db.SearchOrderBy("`user`.name ASC"))
+ assert.Equal(t, MapSortOrder("recentupdate"), db.SearchOrderBy("`user`.updated_unix DESC"))
+}
diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go
index 80ce2ad198..2337b43d4c 100644
--- a/routers/web/feed/branch.go
+++ b/routers/web/feed/branch.go
@@ -8,8 +8,8 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/repo"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
)
@@ -43,6 +43,7 @@ func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType stri
},
Description: commit.Message(),
Content: commit.Message(),
+ Created: commit.Committer.When,
})
}
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index d3d01a87e0..7b09c92ee5 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -1,4 +1,5 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package feed
@@ -12,19 +13,19 @@ import (
"strconv"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
- "github.com/jaytaylor/html2text"
+ "github.com/inbucket/html2text"
)
func toBranchLink(ctx *context.Context, act *activities_model.Action) string {
@@ -209,7 +210,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
{
switch act.OpType {
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
- push := templates.ActionContent2Commits(act)
+ push := templates.ActionContent2Commits(ctx, act)
for _, commit := range push.Commits {
if len(desc) != 0 {
@@ -298,14 +299,14 @@ func GetFeedType(name string, req *http.Request) (bool, string, string) {
return false, name, ""
}
-// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
-func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (items []*feeds.Item, err error) {
- for _, rel := range releases {
- err := rel.LoadAttributes(ctx)
- if err != nil {
- return nil, err
- }
+// feedActionsToFeedItems convert repository releases into feed items.
+func releasesToFeedItems(ctx *context.Context, releases repo_model.ReleaseList) (items []*feeds.Item, err error) {
+ if err := releases.LoadAttributes(ctx); err != nil {
+ return nil, err
+ }
+ composeCache := make(map[int64]map[string]string)
+ for _, rel := range releases {
var title string
var content template.HTML
@@ -315,13 +316,19 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (
title = rel.Title
}
+ metas, ok := composeCache[rel.RepoID]
+ if !ok {
+ metas = rel.Repo.ComposeMetas(ctx)
+ composeCache[rel.RepoID] = metas
+ }
+
link := &feeds.Link{Href: rel.HTMLURL()}
content, err = markdown.RenderString(&markup.RenderContext{
Ctx: ctx,
Links: markup.Links{
Base: rel.Repo.Link(),
},
- Metas: rel.Repo.ComposeMetas(ctx),
+ Metas: metas,
}, rel.Note)
if err != nil {
return nil, err
diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go
index 1ab768ff27..45ceedac12 100644
--- a/routers/web/feed/file.go
+++ b/routers/web/feed/file.go
@@ -8,10 +8,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
)
@@ -55,6 +55,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
},
Description: commit.Message(),
Content: commit.Message(),
+ Created: commit.Committer.When,
})
}
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 08cbcd9e12..dd2fec186f 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -6,10 +6,10 @@ package feed
import (
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/services/context"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
)
diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go
index fb6e3add65..646241c021 100644
--- a/routers/web/feed/release.go
+++ b/routers/web/feed/release.go
@@ -6,9 +6,9 @@ package feed
import (
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
)
diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go
index dc99fb49ed..79681dd0fb 100644
--- a/routers/web/feed/render.go
+++ b/routers/web/feed/render.go
@@ -4,7 +4,7 @@
package feed
import (
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/services/context"
)
// RenderBranchFeed render format for branch or file
diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go
index a0033c7d45..0d105dc3a7 100644
--- a/routers/web/feed/repo.go
+++ b/routers/web/feed/repo.go
@@ -6,9 +6,9 @@ package feed
import (
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/services/context"
+ activities_model "forgejo.org/models/activities"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/services/context"
"github.com/gorilla/feeds"
)
diff --git a/routers/web/githttp.go b/routers/web/githttp.go
index 5f1dedce76..e5ed806f2e 100644
--- a/routers/web/githttp.go
+++ b/routers/web/githttp.go
@@ -6,10 +6,10 @@ package web
import (
"net/http"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/web/repo"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/web/repo"
+ "forgejo.org/services/context"
)
func requireSignIn(ctx *context.Context) {
diff --git a/routers/web/goget.go b/routers/web/goget.go
index 8d5612ebfe..0fcd755ca1 100644
--- a/routers/web/goget.go
+++ b/routers/web/goget.go
@@ -11,10 +11,10 @@ import (
"path"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
func goGet(ctx *context.Context) {
diff --git a/routers/web/healthcheck/check.go b/routers/web/healthcheck/check.go
index 83dfe62537..f0b51aa515 100644
--- a/routers/web/healthcheck/check.go
+++ b/routers/web/healthcheck/check.go
@@ -9,11 +9,11 @@ import (
"os"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
type status string
diff --git a/routers/web/home.go b/routers/web/home.go
index d4be0931e8..bd9942748a 100644
--- a/routers/web/home.go
+++ b/routers/web/home.go
@@ -1,5 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package web
@@ -8,19 +9,19 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/sitemap"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/routers/web/auth"
- "code.gitea.io/gitea/routers/web/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/sitemap"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/routers/web/auth"
+ "forgejo.org/routers/web/user"
+ "forgejo.org/services/context"
)
const (
@@ -61,6 +62,9 @@ func Home(ctx *context.Context) {
ctx.Data["PageIsHome"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+
+ ctx.Data["OpenGraphDescription"] = setting.UI.Meta.Description
+
ctx.HTML(http.StatusOK, tplHome)
}
@@ -109,9 +113,3 @@ func HomeSitemap(ctx *context.Context) {
log.Error("Failed writing sitemap: %v", err)
}
}
-
-// NotFound render 404 page
-func NotFound(ctx *context.Context) {
- ctx.Data["Title"] = "Page Not Found"
- ctx.NotFound("home.NotFound", nil)
-}
diff --git a/routers/web/metrics.go b/routers/web/metrics.go
index 46c13f0a24..8c188e206e 100644
--- a/routers/web/metrics.go
+++ b/routers/web/metrics.go
@@ -7,7 +7,7 @@ import (
"crypto/subtle"
"net/http"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go
index 2bae122b91..d2b67f88c8 100644
--- a/routers/web/misc/markup.go
+++ b/routers/web/misc/markup.go
@@ -5,10 +5,10 @@
package misc
import (
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
)
// Markup render markup document to HTML
diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go
index 54c93763f6..22fdccf79f 100644
--- a/routers/web/misc/misc.go
+++ b/routers/web/misc/misc.go
@@ -7,20 +7,15 @@ import (
"net/http"
"path"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
)
func SSHInfo(rw http.ResponseWriter, req *http.Request) {
- if !git.SupportProcReceive {
- rw.WriteHeader(http.StatusNotFound)
- return
- }
rw.Header().Set("content-type", "text/json;charset=UTF-8")
- _, err := rw.Write([]byte(`{"type":"gitea","version":1}`))
+ _, err := rw.Write([]byte(`{"type":"agit","version":1}`))
if err != nil {
log.Error("fail to write result: err: %v", err)
rw.WriteHeader(http.StatusInternalServerError)
@@ -33,17 +28,96 @@ func DummyOK(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
}
-func RobotsTxt(w http.ResponseWriter, req *http.Request) {
- robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt")
- if ok, _ := util.IsExist(robotsTxt); !ok {
- robotsTxt = util.FilePathJoinAbs(setting.CustomPath, "robots.txt") // the legacy "robots.txt"
- }
- httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
- http.ServeFile(w, req, robotsTxt)
-}
-
func StaticRedirect(target string) func(w http.ResponseWriter, req *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, target), http.StatusMovedPermanently)
}
}
+
+var defaultRobotsTxt = []byte(`# The default Forgejo robots.txt
+# For more information: https://forgejo.org/docs/latest/admin/search-engines-indexation/
+
+User-agent: *
+Disallow: /api/
+Disallow: /avatars/
+Disallow: /user/
+Disallow: /swagger.*.json
+Disallow: /explore/*?*
+
+Disallow: /repo/create
+Disallow: /repo/migrate
+Disallow: /org/create
+Disallow: /*/*/fork
+
+Disallow: /*/*/watchers
+Disallow: /*/*/stargazers
+Disallow: /*/*/forks
+
+Disallow: /*/*/src/
+Disallow: /*/*/blame/
+Disallow: /*/*/commit/
+Disallow: /*/*/commits/
+Disallow: /*/*/raw/
+Disallow: /*/*/media/
+Disallow: /*/*/tags
+Disallow: /*/*/graph
+Disallow: /*/*/branches
+Disallow: /*/*/compare
+Disallow: /*/*/lastcommit/
+Disallow: /*/*/rss/branch/
+Disallow: /*/*/atom/branch/
+
+Disallow: /*/*/activity
+Disallow: /*/*/activity_author_data
+
+Disallow: /*/*/actions
+Disallow: /*/*/projects
+Disallow: /*/*/labels
+Disallow: /*/*/milestones
+
+Disallow: /*/*/find/
+Disallow: /*/*/tree-list/
+Disallow: /*/*/search/
+Disallow: /*/-/code
+
+Disallow: /*/*/issues/new
+Disallow: /*/*/pulls/*/files
+Disallow: /*/*/pulls/*/commits
+
+Disallow: /attachments/
+Disallow: /*/*/attachments/
+Disallow: /*/*/issues/*/attachments/
+Disallow: /*/*/pulls/*/attachments/
+Disallow: /*/*/releases/attachments
+Disallow: /*/*/releases/download
+
+Disallow: /*/*/archive/
+Disallow: /*.bundle$
+Disallow: /*.patch$
+Disallow: /*.diff$
+Disallow: /*.atom$
+Disallow: /*.rss$
+
+Disallow: /*lang=*
+Disallow: /*redirect_to=*
+Disallow: /*tab=*
+Disallow: /*q=*
+Disallow: /*sort=*
+Disallow: /*repo-search-archived=*
+`)
+
+func RobotsTxt(w http.ResponseWriter, req *http.Request) {
+ httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
+ w.Header().Set("Content-Type", "text/plain")
+
+ robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt")
+ if ok, _ := util.IsExist(robotsTxt); ok {
+ http.ServeFile(w, req, robotsTxt)
+ return
+ }
+
+ _, err := w.Write(defaultRobotsTxt)
+ if err != nil {
+ log.Error("failed to write robots.txt: %v", err)
+ }
+}
diff --git a/routers/web/misc/swagger-forgejo.go b/routers/web/misc/swagger-forgejo.go
index e3aff02c5f..17e3814712 100644
--- a/routers/web/misc/swagger-forgejo.go
+++ b/routers/web/misc/swagger-forgejo.go
@@ -6,8 +6,8 @@ package misc
import (
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
)
// tplSwagger swagger page template
diff --git a/routers/web/misc/swagger.go b/routers/web/misc/swagger.go
index 5fddfa8885..226dddaff2 100644
--- a/routers/web/misc/swagger.go
+++ b/routers/web/misc/swagger.go
@@ -6,8 +6,8 @@ package misc
import (
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
)
// tplSwagger swagger page template
diff --git a/routers/web/moderation/report.go b/routers/web/moderation/report.go
new file mode 100644
index 0000000000..39ca9e8824
--- /dev/null
+++ b/routers/web/moderation/report.go
@@ -0,0 +1,125 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ "errors"
+ "net/http"
+
+ "forgejo.org/models/moderation"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ moderation_service "forgejo.org/services/moderation"
+)
+
+const (
+ tplSubmitAbuseReport base.TplName = "moderation/new_abuse_report"
+)
+
+// NewReport renders the page for new abuse reports.
+func NewReport(ctx *context.Context) {
+ contentID := ctx.FormInt64("id")
+ if contentID <= 0 {
+ setMinimalContextData(ctx)
+ ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil)
+ log.Warn("The content ID is expected to be an integer greater that 0; the provided value is %s.", ctx.FormString("id"))
+ return
+ }
+
+ contentTypeString := ctx.FormString("type")
+ var contentType moderation.ReportedContentType
+ switch contentTypeString {
+ case "user", "org":
+ contentType = moderation.ReportedContentTypeUser
+ case "repo":
+ contentType = moderation.ReportedContentTypeRepository
+ case "issue", "pull":
+ contentType = moderation.ReportedContentTypeIssue
+ case "comment":
+ contentType = moderation.ReportedContentTypeComment
+ default:
+ setMinimalContextData(ctx)
+ ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil)
+ log.Warn("The provided content type `%s` is not among the expected values.", contentTypeString)
+ return
+ }
+
+ if moderation.AlreadyReportedByAndOpen(ctx, ctx.Doer.ID, contentType, contentID) {
+ setMinimalContextData(ctx)
+ ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.already_reported"), tplSubmitAbuseReport, nil)
+ return
+ }
+
+ setContextDataAndRender(ctx, contentType, contentID)
+}
+
+// setMinimalContextData adds minimal values (Title and CancelLink) into context data.
+func setMinimalContextData(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("moderation.report_abuse")
+ ctx.Data["CancelLink"] = ctx.Doer.DashboardLink()
+}
+
+// setContextDataAndRender adds some values into context data and renders the new abuse report page.
+func setContextDataAndRender(ctx *context.Context, contentType moderation.ReportedContentType, contentID int64) {
+ setMinimalContextData(ctx)
+ ctx.Data["ContentID"] = contentID
+ ctx.Data["ContentType"] = contentType
+ ctx.Data["AbuseCategories"] = moderation.GetAbuseCategoriesList()
+ ctx.HTML(http.StatusOK, tplSubmitAbuseReport)
+}
+
+// CreatePost handles the POST for creating a new abuse report.
+func CreatePost(ctx *context.Context) {
+ form := *web.GetForm(ctx).(*forms.ReportAbuseForm)
+
+ if form.ContentID <= 0 || !form.ContentType.IsValid() {
+ setMinimalContextData(ctx)
+ ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil)
+ return
+ }
+
+ if ctx.HasError() {
+ setContextDataAndRender(ctx, form.ContentType, form.ContentID)
+ return
+ }
+
+ can, err := moderation_service.CanReport(*ctx, ctx.Doer, form.ContentType, form.ContentID)
+ if err != nil {
+ if errors.Is(err, moderation_service.ErrContentDoesNotExist) || errors.Is(err, moderation_service.ErrDoerNotAllowed) {
+ ctx.Flash.Error(ctx.Tr("moderation.report_abuse_form.invalid"))
+ ctx.Redirect(ctx.Doer.DashboardLink())
+ } else {
+ ctx.ServerError("Failed to check if user can report content", err)
+ }
+ return
+ } else if !can {
+ ctx.Flash.Error(ctx.Tr("moderation.report_abuse_form.invalid"))
+ ctx.Redirect(ctx.Doer.DashboardLink())
+ return
+ }
+
+ report := moderation.AbuseReport{
+ ReporterID: ctx.Doer.ID,
+ ContentType: form.ContentType,
+ ContentID: form.ContentID,
+ Category: form.AbuseCategory,
+ Remarks: form.Remarks,
+ }
+
+ if err := moderation.ReportAbuse(ctx, &report); err != nil {
+ if errors.Is(err, moderation.ErrSelfReporting) {
+ ctx.Flash.Error(ctx.Tr("moderation.reporting_failed", err))
+ ctx.Redirect(ctx.Doer.DashboardLink())
+ } else {
+ ctx.ServerError("Failed to save new abuse report", err)
+ }
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("moderation.reported_thank_you"))
+ ctx.Redirect(ctx.Doer.DashboardLink())
+}
diff --git a/routers/web/nodeinfo.go b/routers/web/nodeinfo.go
index f1cc7bf530..d8c1727479 100644
--- a/routers/web/nodeinfo.go
+++ b/routers/web/nodeinfo.go
@@ -7,8 +7,8 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
type nodeInfoLinks struct {
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index 9ebefa334c..8f14f8899c 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -9,18 +9,18 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
@@ -47,6 +47,12 @@ func Home(ctx *context.Context) {
ctx.Data["PageIsUserProfile"] = true
ctx.Data["Title"] = org.DisplayName()
+ ctx.Data["OpenGraphTitle"] = ctx.ContextUser.DisplayName()
+ ctx.Data["OpenGraphType"] = "profile"
+ ctx.Data["OpenGraphImageURL"] = ctx.ContextUser.AvatarLink(ctx)
+ ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL()
+ ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description
+
var orderBy db.SearchOrderBy
sortOrder := ctx.FormString("sort")
if _, ok := repo_model.OrderByFlatMap[sortOrder]; !ok {
@@ -169,10 +175,12 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor
return
}
- if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
- log.Error("failed to GetBlobContent: %v", err)
+ if rc, _, err := profileReadme.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err != nil {
+ log.Error("failed to NewTruncatedReader: %v", err)
} else {
- if profileContent, err := markdown.RenderString(&markup.RenderContext{
+ defer rc.Close()
+
+ if profileContent, err := markdown.RenderReader(&markup.RenderContext{
Ctx: ctx,
GitRepo: profileGitRepo,
Links: markup.Links{
@@ -182,7 +190,7 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor
BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
},
Metas: map[string]string{"mode": "document"},
- }, bytes); err != nil {
+ }, rc); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
diff --git a/routers/web/org/main_test.go b/routers/web/org/main_test.go
index 92237d6e88..d1d4e89120 100644
--- a/routers/web/org/main_test.go
+++ b/routers/web/org/main_test.go
@@ -6,7 +6,7 @@ package org_test
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/org/members.go b/routers/web/org/members.go
index 3a5509f911..65e2b032e8 100644
--- a/routers/web/org/members.go
+++ b/routers/web/org/members.go
@@ -7,13 +7,13 @@ package org
import (
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models"
+ "forgejo.org/models/organization"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
@@ -60,8 +60,8 @@ func Members(ctx *context.Context) {
}
pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5)
- opts.ListOptions.Page = page
- opts.ListOptions.PageSize = setting.UI.MembersPagingNum
+ opts.Page = page
+ opts.PageSize = setting.UI.MembersPagingNum
members, membersIsPublic, err := organization.FindOrgMembers(ctx, opts)
if err != nil {
ctx.ServerError("GetMembers", err)
diff --git a/routers/web/org/org.go b/routers/web/org/org.go
index dd3aab458b..f3d23df6a5 100644
--- a/routers/web/org/org.go
+++ b/routers/web/org/org.go
@@ -8,15 +8,15 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -27,11 +27,14 @@ const (
// Create render the page for create organization
func Create(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_org.title")
- ctx.Data["DefaultOrgVisibilityMode"] = setting.Service.DefaultOrgVisibilityMode
if !ctx.Doer.CanCreateOrganization() {
ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed")))
return
}
+
+ ctx.Data["visibility"] = setting.Service.DefaultOrgVisibilityMode
+ ctx.Data["repo_admin_change_team_access"] = true
+
ctx.HTML(http.StatusOK, tplCreateOrg)
}
diff --git a/routers/web/org/org_labels.go b/routers/web/org/org_labels.go
index 02eae8052e..dc18c55aa3 100644
--- a/routers/web/org/org_labels.go
+++ b/routers/web/org/org_labels.go
@@ -6,13 +6,13 @@ package org
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/label"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/label"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// RetrieveLabels find all the labels of an organization
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 6cc2a24c0f..96bd0f1ee2 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -9,20 +9,20 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- project_model "code.gitea.io/gitea/models/project"
- attachment_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ project_model "forgejo.org/models/project"
+ attachment_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/web"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -222,7 +222,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
- ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
+ ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.ContextUser, id))
}
// DeleteProject delete a project
@@ -272,7 +272,7 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["redirect"] = ctx.FormString("redirect")
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["card_type"] = p.CardType
- ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), p.ID)
+ ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, p.ID)
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@@ -286,7 +286,7 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["PageIsViewProjects"] = true
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
- ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), projectID)
+ ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, projectID)
shared_user.RenderUserHeader(ctx)
diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go
index ab419cc878..dec78502f2 100644
--- a/routers/web/org/projects_test.go
+++ b/routers/web/org/projects_test.go
@@ -6,9 +6,9 @@ package org_test
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/routers/web/org"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/unittest"
+ "forgejo.org/routers/web/org"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index 8bd8ae6126..c83242754b 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -7,26 +7,27 @@ package org
import (
"net/http"
"net/url"
+ "time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- user_setting "code.gitea.io/gitea/routers/web/user/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- org_service "code.gitea.io/gitea/services/org"
- repo_service "code.gitea.io/gitea/services/repository"
- user_service "code.gitea.io/gitea/services/user"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ shared_user "forgejo.org/routers/web/shared/user"
+ user_setting "forgejo.org/routers/web/user/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ org_service "forgejo.org/services/org"
+ repo_service "forgejo.org/services/repository"
+ user_service "forgejo.org/services/user"
+ webhook_service "forgejo.org/services/webhook"
)
const (
@@ -48,6 +49,10 @@ func Settings(ctx *context.Context) {
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess
ctx.Data["ContextUser"] = ctx.ContextUser
+ ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
+ ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
+ ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
+ ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
err := shared_user.LoadHeaderCount(ctx)
if err != nil {
@@ -65,6 +70,7 @@ func SettingsPost(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsOptions"] = true
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
+ ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplSettingsOptions)
@@ -78,6 +84,9 @@ func SettingsPost(ctx *context.Context) {
if user_model.IsErrUserAlreadyExist(err) {
ctx.Data["Err_Name"] = true
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
+ } else if user_model.IsErrCooldownPeriod(err) {
+ ctx.Data["Err_UserName"] = true
+ ctx.RenderWithErr(ctx.Locale.Tr("form.username_claiming_cooldown", err.(user_model.ErrCooldownPeriod).ExpireTime.Format(time.RFC1123Z)), tplSettingsOptions, form)
} else if db.IsErrNameReserved(err) {
ctx.Data["Err_Name"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
diff --git a/routers/web/org/setting/blocked_users.go b/routers/web/org/setting/blocked_users.go
index 0c7f245c13..77b2791874 100644
--- a/routers/web/org/setting/blocked_users.go
+++ b/routers/web/org/setting/blocked_users.go
@@ -8,11 +8,11 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
const tplBlockedUsers = "org/settings/blocked_users"
@@ -53,6 +53,12 @@ func BlockedUsersBlock(ctx *context.Context) {
return
}
+ if u.ID == ctx.Doer.ID {
+ ctx.Flash.Error(ctx.Tr("settings.user_block_yourself"))
+ ctx.Redirect(ctx.Org.OrgLink + "/settings/blocked_users")
+ return
+ }
+
if err := user_service.BlockUser(ctx, ctx.Org.Organization.ID, u.ID); err != nil {
ctx.ServerError("BlockUser", err)
return
diff --git a/routers/web/org/setting/runners.go b/routers/web/org/setting/runners.go
index fe05709237..8053ed7729 100644
--- a/routers/web/org/setting/runners.go
+++ b/routers/web/org/setting/runners.go
@@ -4,7 +4,7 @@
package setting
import (
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
diff --git a/routers/web/org/setting/storage_overview.go b/routers/web/org/setting/storage_overview.go
new file mode 100644
index 0000000000..5714d7ee23
--- /dev/null
+++ b/routers/web/org/setting/storage_overview.go
@@ -0,0 +1,20 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package setting
+
+import (
+ "forgejo.org/modules/base"
+ "forgejo.org/routers/web/shared"
+ "forgejo.org/services/context"
+)
+
+const (
+ tplSettingsStorageOverview base.TplName = "org/settings/storage_overview"
+)
+
+// StorageOverview render a size overview of the organization, as well as relevant
+// quota limits of the instance.
+func StorageOverview(ctx *context.Context) {
+ shared.StorageOverview(ctx, ctx.Org.Organization.ID, tplSettingsStorageOverview)
+}
diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go
index 7f855795d3..9c31063974 100644
--- a/routers/web/org/setting_oauth2.go
+++ b/routers/web/org/setting_oauth2.go
@@ -7,13 +7,13 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- user_setting "code.gitea.io/gitea/routers/web/user/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ shared_user "forgejo.org/routers/web/shared/user"
+ user_setting "forgejo.org/routers/web/user/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/org/setting_packages.go b/routers/web/org/setting_packages.go
index af9836e42c..4457c8fb0f 100644
--- a/routers/web/org/setting_packages.go
+++ b/routers/web/org/setting_packages.go
@@ -7,11 +7,11 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- shared "code.gitea.io/gitea/routers/web/shared/packages"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ shared "forgejo.org/routers/web/shared/packages"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index df9de4af98..659bee469f 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -12,24 +12,24 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- org_service "code.gitea.io/gitea/services/org"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ org_service "forgejo.org/services/org"
+ repo_service "forgejo.org/services/repository"
)
const (
diff --git a/routers/web/repo/action_aggregator_test.go b/routers/web/repo/action_aggregator_test.go
new file mode 100644
index 0000000000..94e6d506c5
--- /dev/null
+++ b/routers/web/repo/action_aggregator_test.go
@@ -0,0 +1,829 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package repo
+
+import (
+ "strings"
+ "testing"
+
+ issue_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// *************** Helper functions for the tests ***************
+
+func testComment(t int64) *issue_model.Comment {
+ return &issue_model.Comment{PosterID: 1, CreatedUnix: timeutil.TimeStamp(t)}
+}
+
+func nameToID(name string) int64 {
+ var id int64
+ for c, letter := range name {
+ id += int64((c+1)*1000) * int64(letter)
+ }
+ return id
+}
+
+func createReqReviewTarget(name string) issue_model.RequestReviewTarget {
+ if strings.HasSuffix(name, "-team") {
+ team := createTeam(name)
+ return issue_model.RequestReviewTarget{Team: &team}
+ }
+ user := createUser(name)
+ return issue_model.RequestReviewTarget{User: &user}
+}
+
+func createUser(name string) user_model.User {
+ return user_model.User{Name: name, ID: nameToID(name)}
+}
+
+func createTeam(name string) organization.Team {
+ return organization.Team{Name: name, ID: nameToID(name)}
+}
+
+func createLabel(name string) issue_model.Label {
+ return issue_model.Label{Name: name, ID: nameToID(name)}
+}
+
+func addLabel(t int64, name string) *issue_model.Comment {
+ c := testComment(t)
+ c.Type = issue_model.CommentTypeLabel
+ c.Content = "1"
+ lbl := createLabel(name)
+ c.Label = &lbl
+ c.AddedLabels = []*issue_model.Label{&lbl}
+ return c
+}
+
+func delLabel(t int64, name string) *issue_model.Comment {
+ c := addLabel(t, name)
+ c.Content = ""
+ c.RemovedLabels = c.AddedLabels
+ c.AddedLabels = nil
+ return c
+}
+
+func openOrClose(t int64, close bool) *issue_model.Comment {
+ c := testComment(t)
+ if close {
+ c.Type = issue_model.CommentTypeClose
+ } else {
+ c.Type = issue_model.CommentTypeReopen
+ }
+ return c
+}
+
+func reqReview(t int64, name string, delReq bool) *issue_model.Comment {
+ c := testComment(t)
+ c.Type = issue_model.CommentTypeReviewRequest
+ if strings.HasSuffix(name, "-team") {
+ team := createTeam(name)
+ c.AssigneeTeam = &team
+ c.AssigneeTeamID = team.ID
+ } else {
+ user := createUser(name)
+ c.Assignee = &user
+ c.AssigneeID = user.ID
+ }
+ c.RemovedAssignee = delReq
+ return c
+}
+
+func ghostReqReview(t, id int64) *issue_model.Comment {
+ c := testComment(t)
+ c.Type = issue_model.CommentTypeReviewRequest
+ c.AssigneeTeam = organization.NewGhostTeam()
+ c.AssigneeTeamID = id
+ return c
+}
+
+func reqReviewList(t int64, del bool, names ...string) *issue_model.Comment {
+ req := []issue_model.RequestReviewTarget{}
+ for _, name := range names {
+ req = append(req, createReqReviewTarget(name))
+ }
+ cmnt := testComment(t)
+ cmnt.Type = issue_model.CommentTypeReviewRequest
+ if del {
+ cmnt.RemovedRequestReview = req
+ } else {
+ cmnt.AddedRequestReview = req
+ }
+ return cmnt
+}
+
+func aggregatedComment(t int64,
+ closed bool,
+ addLabels []*issue_model.Label,
+ delLabels []*issue_model.Label,
+ addReqReview []issue_model.RequestReviewTarget,
+ delReqReview []issue_model.RequestReviewTarget,
+) *issue_model.Comment {
+ cmnt := testComment(t)
+ cmnt.Type = issue_model.CommentTypeAggregator
+ cmnt.Aggregator = &issue_model.ActionAggregator{
+ IsClosed: closed,
+ AddedLabels: addLabels,
+ RemovedLabels: delLabels,
+ AddedRequestReview: addReqReview,
+ RemovedRequestReview: delReqReview,
+ }
+ if len(addLabels) > 0 {
+ cmnt.AddedLabels = addLabels
+ }
+ if len(delLabels) > 0 {
+ cmnt.RemovedLabels = delLabels
+ }
+ if len(addReqReview) > 0 {
+ cmnt.AddedRequestReview = addReqReview
+ }
+ if len(delReqReview) > 0 {
+ cmnt.RemovedRequestReview = delReqReview
+ }
+ return cmnt
+}
+
+func commentText(t int64, text string) *issue_model.Comment {
+ c := testComment(t)
+ c.Type = issue_model.CommentTypeComment
+ c.Content = text
+ return c
+}
+
+// ****************************************************************
+
+type testCase struct {
+ name string
+ beforeCombined []*issue_model.Comment
+ afterCombined []*issue_model.Comment
+ sameAfter bool
+ timestampCombination int64
+}
+
+func (kase *testCase) doTest(t *testing.T) {
+ issue := issue_model.Issue{Comments: kase.beforeCombined}
+
+ var now int64 = -9223372036854775808
+ for c := 0; c < len(kase.beforeCombined); c++ {
+ assert.Greater(t, int64(kase.beforeCombined[c].CreatedUnix), now)
+ now = int64(kase.beforeCombined[c].CreatedUnix)
+ }
+
+ if kase.timestampCombination != 0 {
+ now = kase.timestampCombination
+ }
+
+ issue_model.CombineCommentsHistory(&issue, now)
+
+ after := kase.afterCombined
+ if kase.sameAfter {
+ after = kase.beforeCombined
+ }
+
+ if len(after) != len(issue.Comments) {
+ t.Logf("Expected %v comments, got %v", len(after), len(issue.Comments))
+ t.Log("Comments got after combination:")
+ for c := 0; c < len(issue.Comments); c++ {
+ cmt := issue.Comments[c]
+ t.Logf("%v %v %v\n", cmt.Type, cmt.CreatedUnix, cmt.Content)
+ }
+ assert.Len(t, issue.Comments, len(after))
+ t.Fail()
+ return
+ }
+
+ for c := 0; c < len(after); c++ {
+ l := (after)[c]
+ r := issue.Comments[c]
+
+ // Ignore some inner data of the aggregator to facilitate testing
+ if l.Type == issue_model.CommentTypeAggregator {
+ r.Aggregator.StartUnix = 0
+ r.Aggregator.PrevClosed = false
+ r.Aggregator.PosterID = 0
+ r.Aggregator.StartInd = 0
+ r.Aggregator.EndInd = 0
+ r.Aggregator.AggAge = 0
+ }
+
+ // We can safely ignore this if the rest matches
+ if l.Type == issue_model.CommentTypeLabel {
+ l.Label = nil
+ l.Content = ""
+ } else if l.Type == issue_model.CommentTypeReviewRequest {
+ l.Assignee = nil
+ l.AssigneeID = 0
+ l.AssigneeTeam = nil
+ l.AssigneeTeamID = 0
+ }
+
+ assert.Equal(t, (after)[c], issue.Comments[c],
+ "Comment %v is not equal", c,
+ )
+ }
+}
+
+// **************** Start of the tests ******************
+
+func TestCombineLabelComments(t *testing.T) {
+ var tmon int64 = 60 * 60 * 24 * 30
+ var tday int64 = 60 * 60 * 24
+ var thour int64 = 60 * 60
+ kases := []testCase{
+ // ADD single = normal label comment
+ {
+ name: "add_single_label",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ commentText(10, "I'm a salmon"),
+ },
+ sameAfter: true,
+ },
+
+ // ADD then REMOVE = Nothing
+ {
+ name: "add_label_then_remove",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ delLabel(1, "a"),
+ commentText(65, "I'm a salmon"),
+ },
+ afterCombined: []*issue_model.Comment{
+ commentText(65, "I'm a salmon"),
+ },
+ },
+
+ // ADD 1 then comment then REMOVE = separate comments
+ {
+ name: "add_label_then_comment_then_remove",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ commentText(10, "I'm a salmon"),
+ delLabel(20, "a"),
+ },
+ sameAfter: true,
+ },
+
+ // ADD 2 = Combined labels
+ {
+ name: "combine_labels",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ addLabel(10, "b"),
+ commentText(20, "I'm a salmon"),
+ addLabel(30, "c"),
+ addLabel(80, "d"),
+ addLabel(85, "e"),
+ delLabel(90, "c"),
+ },
+ afterCombined: []*issue_model.Comment{
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(0),
+ AddedLabels: []*issue_model.Label{
+ {Name: "a", ID: nameToID("a")},
+ {Name: "b", ID: nameToID("b")},
+ },
+ },
+ commentText(20, "I'm a salmon"),
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(30),
+ AddedLabels: []*issue_model.Label{
+ {Name: "d", ID: nameToID("d")},
+ {Name: "e", ID: nameToID("e")},
+ },
+ },
+ },
+ },
+
+ // ADD 1, then 1 later = 2 separate comments
+ {
+ name: "add_then_later_label",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ addLabel(60, "b"),
+ addLabel(121, "c"),
+ },
+ afterCombined: []*issue_model.Comment{
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(0),
+ AddedLabels: []*issue_model.Label{
+ {Name: "a", ID: nameToID("a")},
+ {Name: "b", ID: nameToID("b")},
+ },
+ },
+ addLabel(121, "c"),
+ },
+ },
+
+ // ADD 2 then REMOVE 1 = label
+ {
+ name: "add_2_remove_1",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ addLabel(10, "b"),
+ delLabel(20, "a"),
+ },
+ afterCombined: []*issue_model.Comment{
+ // The timestamp will be the one of the first aggregated comment
+ addLabel(0, "b"),
+ },
+ },
+
+ // ADD then REMOVE multiple = nothing
+ {
+ name: "add_multiple_remove_all",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ addLabel(1, "b"),
+ addLabel(2, "c"),
+ addLabel(3, "d"),
+ addLabel(4, "e"),
+ delLabel(5, "d"),
+ delLabel(6, "a"),
+ delLabel(7, "e"),
+ delLabel(8, "c"),
+ delLabel(9, "b"),
+ },
+ afterCombined: nil,
+ },
+
+ // ADD 2, wait, REMOVE 2 = +2 then -2 comments
+ {
+ name: "add2_wait_rm2_labels",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(0, "a"),
+ addLabel(1, "b"),
+ delLabel(120, "a"),
+ delLabel(121, "b"),
+ },
+ afterCombined: []*issue_model.Comment{
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(0),
+ AddedLabels: []*issue_model.Label{
+ {Name: "a", ID: nameToID("a")},
+ {Name: "b", ID: nameToID("b")},
+ },
+ },
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(120),
+ RemovedLabels: []*issue_model.Label{
+ {Name: "a", ID: nameToID("a")},
+ {Name: "b", ID: nameToID("b")},
+ },
+ },
+ },
+ },
+
+ // Regression check on edge case
+ {
+ name: "regression_edgecase_finalagg",
+ beforeCombined: []*issue_model.Comment{
+ commentText(0, "hey"),
+ commentText(1, "ho"),
+ addLabel(2, "a"),
+ addLabel(3, "b"),
+ delLabel(4, "a"),
+ delLabel(5, "b"),
+
+ addLabel(120, "a"),
+
+ addLabel(220, "c"),
+ addLabel(221, "d"),
+ addLabel(222, "e"),
+ delLabel(223, "d"),
+
+ delLabel(400, "a"),
+ },
+ afterCombined: []*issue_model.Comment{
+ commentText(0, "hey"),
+ commentText(1, "ho"),
+ addLabel(120, "a"),
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeLabel,
+ CreatedUnix: timeutil.TimeStamp(220),
+ AddedLabels: []*issue_model.Label{
+ {Name: "c", ID: nameToID("c")},
+ {Name: "e", ID: nameToID("e")},
+ },
+ },
+ delLabel(400, "a"),
+ },
+ },
+
+ {
+ name: "combine_label_high_timestamp_separated",
+ timestampCombination: tmon + 1,
+ beforeCombined: []*issue_model.Comment{
+ // 1 month old, comments separated by 1 Day + 1 sec (not agg)
+ addLabel(0, "d"),
+ delLabel(tday+1, "d"),
+
+ // 1 day old, comments separated by 1 hour + 1 sec (not agg)
+ addLabel((tmon-tday)-thour, "c"),
+ delLabel((tmon-tday)+1, "c"),
+
+ // 1 hour old, comments separated by 10 mins + 1 sec (not agg)
+ addLabel(tmon-thour, "b"),
+ delLabel((tmon-(50*60))+1, "b"),
+
+ // Else, aggregate by minute
+ addLabel(tmon-61, "a"),
+ delLabel(tmon, "a"),
+ },
+ sameAfter: true,
+ },
+
+ // Test higher timestamp diff
+ {
+ name: "combine_label_high_timestamp_merged",
+ timestampCombination: tmon + 1,
+ beforeCombined: []*issue_model.Comment{
+ // 1 month old, comments separated by 1 Day (aggregated)
+ addLabel(0, "d"),
+ delLabel(tday, "d"),
+
+ // 1 day old, comments separated by 1 hour (aggregated)
+ addLabel((tmon-tday)-thour, "c"),
+ delLabel(tmon-tday, "c"),
+
+ // 1 hour old, comments separated by 10 mins (aggregated)
+ addLabel(tmon-thour, "b"),
+ delLabel(tmon-(50*60), "b"),
+
+ addLabel(tmon-60, "a"),
+ delLabel(tmon, "a"),
+ },
+ },
+ }
+
+ for _, kase := range kases {
+ t.Run(kase.name, kase.doTest)
+ }
+}
+
+func TestCombineReviewRequests(t *testing.T) {
+ kases := []testCase{
+ // ADD single = normal request review comment
+ {
+ name: "add_single_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "toto", false),
+ commentText(10, "I'm a salmon"),
+ reqReview(20, "toto-team", false),
+ },
+ sameAfter: true,
+ },
+
+ // ADD then REMOVE = Nothing
+ {
+ name: "add_then_remove_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "toto", false),
+ reqReview(5, "toto", true),
+ commentText(10, "I'm a salmon"),
+ },
+ afterCombined: []*issue_model.Comment{
+ commentText(10, "I'm a salmon"),
+ },
+ },
+
+ // ADD 1 then comment then REMOVE = separate comments
+ {
+ name: "add_comment_del_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "toto", false),
+ commentText(5, "I'm a salmon"),
+ reqReview(10, "toto", true),
+ },
+ sameAfter: true,
+ },
+
+ // ADD 2 = Combined request reviews
+ {
+ name: "combine_reviews",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "toto", false),
+ reqReview(10, "tutu-team", false),
+ commentText(20, "I'm a salmon"),
+ reqReview(30, "titi", false),
+ reqReview(80, "tata", false),
+ reqReview(85, "tyty-team", false),
+ reqReview(90, "titi", true),
+ },
+ afterCombined: []*issue_model.Comment{
+ reqReviewList(0, false, "toto", "tutu-team"),
+ commentText(20, "I'm a salmon"),
+ reqReviewList(30, false, "tata", "tyty-team"),
+ },
+ },
+
+ // ADD 1, then 1 later = 2 separate comments
+ {
+ name: "add_then_later_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "titi", false),
+ reqReview(60, "toto-team", false),
+ reqReview(121, "tutu", false),
+ },
+ afterCombined: []*issue_model.Comment{
+ reqReviewList(0, false, "titi", "toto-team"),
+ reqReviewList(121, false, "tutu"),
+ },
+ },
+
+ // ADD 2 then REMOVE 1 = single request review
+ {
+ name: "add_2_then_remove_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "titi-team", false),
+ reqReview(59, "toto", false),
+ reqReview(60, "titi-team", true),
+ },
+ afterCombined: []*issue_model.Comment{
+ reqReviewList(0, false, "toto"),
+ },
+ },
+
+ // ADD then REMOVE multiple = nothing
+ {
+ name: "add_multiple_then_remove_all_review",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(0, "titi0-team", false),
+ reqReview(1, "toto1", false),
+ reqReview(2, "titi2", false),
+ reqReview(3, "titi3-team", false),
+ reqReview(4, "titi4", false),
+ reqReview(5, "titi5", false),
+ reqReview(6, "titi6-team", false),
+ reqReview(10, "titi0-team", true),
+ reqReview(11, "toto1", true),
+ reqReview(12, "titi2", true),
+ reqReview(13, "titi3-team", true),
+ reqReview(14, "titi4", true),
+ reqReview(15, "titi5", true),
+ reqReview(16, "titi6-team", true),
+ },
+ afterCombined: nil,
+ },
+
+ // ADD 2, wait, REMOVE 2 = +2 then -2 comments
+ {
+ name: "add2_wait_rm2_requests",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(1, "titi", false),
+ reqReview(2, "toto-team", false),
+ reqReview(121, "titi", true),
+ reqReview(122, "toto-team", true),
+ },
+ afterCombined: []*issue_model.Comment{
+ reqReviewList(1, false, "titi", "toto-team"),
+ reqReviewList(121, true, "titi", "toto-team"),
+ },
+ },
+
+ // Ghost.
+ {
+ name: "ghost reviews",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(1, "titi", false),
+ ghostReqReview(2, 50),
+ ghostReqReview(3, 51),
+ ghostReqReview(4, 50),
+ },
+ afterCombined: []*issue_model.Comment{
+ {
+ PosterID: 1,
+ Type: issue_model.CommentTypeReviewRequest,
+ CreatedUnix: timeutil.TimeStamp(1),
+ AddedRequestReview: []issue_model.RequestReviewTarget{
+ createReqReviewTarget("titi"), {Team: organization.NewGhostTeam()},
+ },
+ },
+ },
+ },
+ }
+
+ for _, kase := range kases {
+ t.Run(kase.name, kase.doTest)
+ }
+}
+
+func TestCombineOpenClose(t *testing.T) {
+ kases := []testCase{
+ // Close then open = nullified
+ {
+ name: "close_open_nullified",
+ beforeCombined: []*issue_model.Comment{
+ openOrClose(0, true),
+ openOrClose(10, false),
+ },
+ afterCombined: nil,
+ },
+
+ // Close then open later = separate comments
+ {
+ name: "close_open_later",
+ beforeCombined: []*issue_model.Comment{
+ openOrClose(0, true),
+ openOrClose(61, false),
+ },
+ sameAfter: true,
+ },
+
+ // Close then comment then open = separate comments
+ {
+ name: "close_comment_open",
+ beforeCombined: []*issue_model.Comment{
+ openOrClose(0, true),
+ commentText(1, "I'm a salmon"),
+ openOrClose(2, false),
+ },
+ sameAfter: true,
+ },
+ }
+
+ for _, kase := range kases {
+ t.Run(kase.name, kase.doTest)
+ }
+}
+
+func TestCombineMultipleDifferentComments(t *testing.T) {
+ lblA := createLabel("a")
+ kases := []testCase{
+ // Add Label + Close + ReqReview = Combined
+ {
+ name: "label_close_reqreview_combined",
+ beforeCombined: []*issue_model.Comment{
+ reqReview(1, "toto", false),
+ addLabel(2, "a"),
+ openOrClose(3, true),
+
+ reqReview(101, "toto", true),
+ openOrClose(102, false),
+ delLabel(103, "a"),
+ },
+ afterCombined: []*issue_model.Comment{
+ aggregatedComment(1,
+ true,
+ []*issue_model.Label{&lblA},
+ []*issue_model.Label{},
+ []issue_model.RequestReviewTarget{createReqReviewTarget("toto")},
+ []issue_model.RequestReviewTarget{},
+ ),
+ aggregatedComment(101,
+ false,
+ []*issue_model.Label{},
+ []*issue_model.Label{&lblA},
+ []issue_model.RequestReviewTarget{},
+ []issue_model.RequestReviewTarget{createReqReviewTarget("toto")},
+ ),
+ },
+ },
+
+ // Add Req + Add Label + Close + Del Req + Del Label = Close only
+ {
+ name: "req_label_close_dellabel_delreq",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(2, "a"),
+ reqReview(3, "titi", false),
+ openOrClose(4, true),
+ delLabel(5, "a"),
+ reqReview(6, "titi", true),
+ },
+ afterCombined: []*issue_model.Comment{
+ openOrClose(2, true),
+ },
+ },
+
+ // Close + Add Req + Add Label + Del Req + Open = Label only
+ {
+ name: "close_req_label_open_delreq",
+ beforeCombined: []*issue_model.Comment{
+ openOrClose(2, true),
+ reqReview(4, "titi", false),
+ addLabel(5, "a"),
+ reqReview(6, "titi", true),
+ openOrClose(8, false),
+ },
+ afterCombined: []*issue_model.Comment{
+ addLabel(2, "a"),
+ },
+ },
+
+ // Add Label + Close + Add ReqReview + Del Label + Open = ReqReview only
+ {
+ name: "label_close_req_dellabel_open",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(1, "a"),
+ openOrClose(2, true),
+ reqReview(4, "titi", false),
+ openOrClose(7, false),
+ delLabel(8, "a"),
+ },
+ afterCombined: []*issue_model.Comment{
+ reqReviewList(1, false, "titi"),
+ },
+ },
+
+ // Add Label + Close + ReqReview, then delete everything = nothing
+ {
+ name: "add_multiple_delete_everything",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(1, "a"),
+ openOrClose(2, true),
+ reqReview(4, "titi", false),
+ openOrClose(7, false),
+ delLabel(8, "a"),
+ reqReview(10, "titi", true),
+ },
+ afterCombined: nil,
+ },
+
+ // Add multiple, then comment, then delete everything = separate aggregation
+ {
+ name: "add_multiple_comment_delete_everything",
+ beforeCombined: []*issue_model.Comment{
+ addLabel(1, "a"),
+ openOrClose(2, true),
+ reqReview(4, "titi", false),
+
+ commentText(6, "I'm a salmon"),
+
+ openOrClose(7, false),
+ delLabel(8, "a"),
+ reqReview(10, "titi", true),
+ },
+ afterCombined: []*issue_model.Comment{
+ aggregatedComment(1,
+ true,
+ []*issue_model.Label{&lblA},
+ []*issue_model.Label{},
+ []issue_model.RequestReviewTarget{createReqReviewTarget("titi")},
+ []issue_model.RequestReviewTarget{},
+ ),
+ commentText(6, "I'm a salmon"),
+ aggregatedComment(7,
+ false,
+ []*issue_model.Label{},
+ []*issue_model.Label{&lblA},
+ []issue_model.RequestReviewTarget{},
+ []issue_model.RequestReviewTarget{createReqReviewTarget("titi")},
+ ),
+ },
+ },
+
+ {
+ name: "regression_edgecase_finalagg",
+ beforeCombined: []*issue_model.Comment{
+ commentText(0, "hey"),
+ commentText(1, "ho"),
+ addLabel(2, "a"),
+ reqReview(3, "titi", false),
+ delLabel(4, "a"),
+ reqReview(5, "titi", true),
+
+ addLabel(120, "a"),
+
+ openOrClose(220, true),
+ addLabel(221, "d"),
+ reqReview(222, "toto-team", false),
+ delLabel(223, "d"),
+
+ delLabel(400, "a"),
+ },
+ afterCombined: []*issue_model.Comment{
+ commentText(0, "hey"),
+ commentText(1, "ho"),
+ addLabel(120, "a"),
+ aggregatedComment(220,
+ true,
+ []*issue_model.Label{},
+ []*issue_model.Label{},
+ []issue_model.RequestReviewTarget{createReqReviewTarget("toto-team")},
+ []issue_model.RequestReviewTarget{},
+ ),
+ delLabel(400, "a"),
+ },
+ },
+ }
+
+ for _, kase := range kases {
+ t.Run(kase.name, kase.doTest)
+ }
+}
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index e5134c1f62..7aa52ddd4c 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -11,28 +11,29 @@ import (
"slices"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/web/repo"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/web/repo"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
"github.com/nektos/act/pkg/model"
)
const (
- tplListActions base.TplName = "repo/actions/list"
- tplViewActions base.TplName = "repo/actions/view"
+ tplListActions base.TplName = "repo/actions/list"
+ tplListActionsInner base.TplName = "repo/actions/list_inner"
+ tplViewActions base.TplName = "repo/actions/view"
)
type Workflow struct {
@@ -67,6 +68,8 @@ func List(ctx *context.Context) {
curWorkflow := ctx.FormString("workflow")
ctx.Data["CurWorkflow"] = curWorkflow
+ listInner := ctx.FormBool("list_inner")
+
var workflows []Workflow
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
ctx.ServerError("IsEmpty", err)
@@ -240,7 +243,7 @@ func List(ctx *context.Context) {
}
ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors)
- ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx)
+ ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx, ctx.Locale)
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
@@ -250,7 +253,11 @@ func List(ctx *context.Context) {
ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
- ctx.HTML(http.StatusOK, tplListActions)
+ if listInner {
+ ctx.HTML(http.StatusOK, tplListActionsInner)
+ } else {
+ ctx.HTML(http.StatusOK, tplListActions)
+ }
}
// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.
diff --git a/routers/web/repo/actions/actions_test.go b/routers/web/repo/actions/actions_test.go
index 939c4aaf57..232aacf96b 100644
--- a/routers/web/repo/actions/actions_test.go
+++ b/routers/web/repo/actions/actions_test.go
@@ -6,9 +6,9 @@ package actions
import (
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- unittest "code.gitea.io/gitea/models/unittest"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ unittest "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/routers/web/repo/actions/main_test.go b/routers/web/repo/actions/main_test.go
index a82f9c6672..0f82a0e7ea 100644
--- a/routers/web/repo/actions/main_test.go
+++ b/routers/web/repo/actions/main_test.go
@@ -6,7 +6,7 @@ package actions
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/repo/actions/manual.go b/routers/web/repo/actions/manual.go
index 949469fa21..413b087e8b 100644
--- a/routers/web/repo/actions/manual.go
+++ b/routers/web/repo/actions/manual.go
@@ -6,8 +6,8 @@ package actions
import (
"net/url"
- actions_service "code.gitea.io/gitea/services/actions"
- context_module "code.gitea.io/gitea/services/context"
+ actions_service "forgejo.org/services/actions"
+ context_module "forgejo.org/services/context"
)
func ManualRunWorkflow(ctx *context_module.Context) {
@@ -46,7 +46,8 @@ func ManualRunWorkflow(ctx *context_module.Context) {
return ctx.Req.PostFormValue(formKey)
}
- if err := workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer); err != nil {
+ _, _, err = workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer)
+ if err != nil {
if actions_service.IsInputRequiredErr(err) {
ctx.Flash.Error(ctx.Locale.Tr("actions.workflow.dispatch.input_required", err.(actions_service.InputRequiredErr).Name))
ctx.Redirect(location)
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index dea31bb1c4..260468f207 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -18,24 +18,24 @@ import (
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- actions_service "code.gitea.io/gitea/services/actions"
- context_module "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ actions_service "forgejo.org/services/actions"
+ context_module "forgejo.org/services/context"
"xorm.io/builder"
)
@@ -383,7 +383,7 @@ func Rerun(ctx *context_module.Context) {
run.PreviousDuration = run.Duration()
run.Started = 0
run.Stopped = 0
- if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration"); err != nil {
+ if err := actions_service.UpdateRun(ctx, run, "started", "stopped", "previous_duration"); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return
}
@@ -436,7 +436,7 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shou
job.Stopped = 0
if err := db.WithTx(ctx, func(ctx context.Context) error {
- _, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped")
+ _, err := actions_service.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped")
return err
}); err != nil {
return err
@@ -512,16 +512,16 @@ func Cancel(ctx *context_module.Context) {
if job.TaskID == 0 {
job.Status = actions_model.StatusCancelled
job.Stopped = timeutil.TimeStampNow()
- n, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
+ n, err := actions_service.UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
if err != nil {
return err
}
if n == 0 {
- return fmt.Errorf("job has changed, try again")
+ return errors.New("job has changed, try again")
}
continue
}
- if err := actions_model.StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
+ if err := actions_service.StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
return err
}
}
@@ -549,13 +549,13 @@ func Approve(ctx *context_module.Context) {
if err := db.WithTx(ctx, func(ctx context.Context) error {
run.NeedApproval = false
run.ApprovedBy = doer.ID
- if err := actions_model.UpdateRun(ctx, run, "need_approval", "approved_by"); err != nil {
+ if err := actions_service.UpdateRun(ctx, run, "need_approval", "approved_by"); err != nil {
return err
}
for _, job := range jobs {
if len(job.Needs) == 0 && job.Status.IsBlocked() {
job.Status = actions_model.StatusWaiting
- _, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
+ _, err := actions_service.UpdateRunJob(ctx, job, nil, "status")
if err != nil {
return err
}
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index af9cea0f33..c9cd2c13bb 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -7,10 +7,10 @@ import (
"net/http"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go
index b5078e1f63..5b2eaef889 100644
--- a/routers/web/repo/attachment.go
+++ b/routers/web/repo/attachment.go
@@ -7,18 +7,18 @@ import (
"fmt"
"net/http"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/attachment"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- repo_service "code.gitea.io/gitea/services/repository"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/attachment"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ repo_service "forgejo.org/services/repository"
)
// UploadIssueAttachment response for Issue/PR attachments
@@ -106,7 +106,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
}
if repository == nil { // If not linked
- if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { // We block if not the uploader
+ if !ctx.IsSigned || attach.UploaderID != ctx.Doer.ID { // We block if not the uploader
ctx.Error(http.StatusNotFound)
return
}
diff --git a/routers/web/repo/badges/badges.go b/routers/web/repo/badges/badges.go
index a2306d5836..e623a21fc0 100644
--- a/routers/web/repo/badges/badges.go
+++ b/routers/web/repo/badges/badges.go
@@ -8,11 +8,11 @@ import (
"net/url"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/setting"
- context_module "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/setting"
+ context_module "forgejo.org/services/context"
)
func getBadgeURL(ctx *context_module.Context, label, text, color string) string {
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index c7fbaaefcb..f4cc2a2cea 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -10,16 +10,16 @@ import (
"net/url"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/highlight"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- files_service "code.gitea.io/gitea/services/repository/files"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/highlight"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ files_service "forgejo.org/services/repository/files"
)
type blameRow struct {
@@ -82,19 +82,19 @@ func RefBlame(ctx *context.Context) {
return
}
- ctx.Data["NumLinesSet"] = true
- ctx.Data["NumLines"], err = blob.GetBlobLineCount()
- if err != nil {
- ctx.ServerError("GetBlobLineCount", err)
- return
- }
-
result, err := performBlame(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormBool("bypass-blame-ignore"))
if err != nil {
ctx.ServerError("performBlame", err)
return
}
+ ctx.Data["NumLinesSet"] = true
+ numLines := 0
+ for _, p := range result.Parts {
+ numLines += len(p.Lines)
+ }
+ ctx.Data["NumLines"] = numLines
+
ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs
ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index 4897a5f4fc..0fe52bfb48 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -11,23 +11,23 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- release_service "code.gitea.io/gitea/services/release"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ release_service "forgejo.org/services/release"
+ repo_service "forgejo.org/services/repository"
)
const (
@@ -70,11 +70,6 @@ func Branches(ctx *context.Context) {
ctx.ServerError("LoadBranches", err)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- for key := range commitStatuses {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
- }
- }
commitStatus := make(map[string]*git_model.CommitStatus)
for commitID, cs := range commitStatuses {
diff --git a/routers/web/repo/card.go b/routers/web/repo/card.go
index e73971cd94..449e5c4890 100644
--- a/routers/web/repo/card.go
+++ b/routers/web/repo/card.go
@@ -15,17 +15,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- issue_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/card"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ issue_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/card"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/services/context"
)
// drawUser draws a user avatar in a summary card
diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go
index 90dae704f4..0f57eb66f0 100644
--- a/routers/web/repo/cherry_pick.go
+++ b/routers/web/repo/cherry_pick.go
@@ -8,17 +8,17 @@ import (
"errors"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/repository/files"
)
var tplCherryPick base.TplName = "repo/editor/cherry_pick"
diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go
index c76f492da0..44c07e617e 100644
--- a/routers/web/repo/code_frequency.go
+++ b/routers/web/repo/code_frequency.go
@@ -7,9 +7,9 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
- contributors_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
+ contributors_service "forgejo.org/services/repository"
)
const (
@@ -34,7 +34,7 @@ func CodeFrequencyData(ctx *context.Context) {
ctx.Status(http.StatusAccepted)
return
}
- ctx.ServerError("GetCodeFrequencyData", err)
+ ctx.ServerError("GetContributorStats", err)
} else {
ctx.JSON(http.StatusOK, contributorStats["total"].Weeks)
}
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index a06da71429..f3192266ad 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -12,26 +12,25 @@ import (
"path"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitgraph"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/gitdiff"
- git_service "code.gitea.io/gitea/services/repository"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/gitdiff"
+ git_service "forgejo.org/services/repository"
+ "forgejo.org/services/repository/gitgraph"
)
const (
@@ -84,7 +83,7 @@ func Commits(ctx *context.Context) {
ctx.ServerError("CommitsByRange", err)
return
}
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@@ -202,7 +201,7 @@ func SearchCommits(ctx *context.Context) {
return
}
ctx.Data["CommitCount"] = len(commits)
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Keyword"] = query
if all {
@@ -267,7 +266,7 @@ func FileHistory(ctx *context.Context) {
}
}
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@@ -315,11 +314,7 @@ func Diff(ctx *context.Context) {
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound("Repo.GitRepo.GetCommit", err)
- } else {
- ctx.ServerError("Repo.GitRepo.GetCommit", err)
- }
+ ctx.NotFoundOrServerError("gitRepo.GetCommit", git.IsErrNotExist, err)
return
}
if len(commitID) != commit.ID.Type().FullLength() {
@@ -333,7 +328,7 @@ func Diff(ctx *context.Context) {
maxLines, maxFiles = -1, -1
}
- diff, err := gitdiff.GetDiff(ctx, gitRepo, &gitdiff.DiffOptions{
+ diff, err := gitdiff.GetDiffFull(ctx, gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: maxLines,
@@ -343,7 +338,7 @@ func Diff(ctx *context.Context) {
FileOnly: fileOnly,
}, files...)
if err != nil {
- ctx.NotFound("GetDiff", err)
+ ctx.ServerError("GetDiff", err)
return
}
@@ -379,9 +374,6 @@ func Diff(ctx *context.Context) {
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
- if !ctx.Repo.CanRead(unit_model.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, statuses)
- }
ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
@@ -419,6 +411,10 @@ func Diff(ctx *context.Context) {
}
}
+ ctx.Data["OpenGraphTitle"] = commit.Summary() + " · " + base.ShortSha(commitID)
+ ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s/commit/%s", ctx.Repo.Repository.HTMLURL(), commitID)
+ _, ctx.Data["OpenGraphDescription"], _ = strings.Cut(commit.Message(), "\n")
+
ctx.HTML(http.StatusOK, tplCommitPage)
}
@@ -456,20 +452,6 @@ func RawDiff(ctx *context.Context) {
}
}
-func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) []*git_model.SignCommitWithStatuses {
- commits := git_model.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
- if !ctx.Repo.CanRead(unit_model.TypeActions) {
- for _, commit := range commits {
- if commit.Status == nil {
- continue
- }
- commit.Status.HideActionsURL(ctx)
- git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
- }
- }
- return commits
-}
-
func SetCommitNotes(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CommitNotesForm)
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 03d49fa692..59538d8a0e 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -16,29 +16,29 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- csv_module "code.gitea.io/gitea/modules/csv"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/gitdiff"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ csv_module "forgejo.org/modules/csv"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/gitdiff"
)
const (
@@ -231,6 +231,13 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
if infoPath == "" {
infos = []string{baseRepo.DefaultBranch, baseRepo.DefaultBranch}
} else {
+ infoPath, isDiff := strings.CutSuffix(infoPath, ".diff")
+ ctx.Data["ComparingDiff"] = isDiff
+ if !isDiff {
+ var isPatch bool
+ infoPath, isPatch = strings.CutSuffix(infoPath, ".patch")
+ ctx.Data["ComparingPatch"] = isPatch
+ }
infos = strings.SplitN(infoPath, "...", 2)
if len(infos) != 2 {
if infos = strings.SplitN(infoPath, "..", 2); len(infos) == 2 {
@@ -305,22 +312,16 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
if !baseIsCommit && !baseIsBranch && !baseIsTag {
- // Check if baseBranch is short sha commit hash
- if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil {
- ci.BaseBranch = baseCommit.ID.String()
- ctx.Data["BaseBranch"] = ci.BaseBranch
- baseIsCommit = true
- } else if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() {
+ if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() {
if isSameRepo {
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
} else {
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadRepo.FullName()) + ":" + util.PathEscapeSegments(ci.HeadBranch))
}
- return nil
} else {
ctx.NotFound("IsRefExist", nil)
- return nil
}
+ return nil
}
ctx.Data["BaseIsCommit"] = baseIsCommit
ctx.Data["BaseIsBranch"] = baseIsBranch
@@ -507,15 +508,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch)
headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch)
if !headIsCommit && !headIsBranch && !headIsTag {
- // Check if headBranch is short sha commit hash
- if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil {
- ci.HeadBranch = headCommit.ID.String()
- ctx.Data["HeadBranch"] = ci.HeadBranch
- headIsCommit = true
- } else {
- ctx.NotFound("IsRefExist", nil)
- return nil
- }
+ ctx.NotFound("IsRefExist", nil)
+ return nil
}
ctx.Data["HeadIsCommit"] = headIsCommit
ctx.Data["HeadIsBranch"] = headIsBranch
@@ -590,7 +584,7 @@ func PrepareCompareDiff(
config := unit.PullRequestsConfig()
if !config.AutodetectManualMerge {
- allowEmptyPr := !(ci.BaseBranch == ci.HeadBranch && ctx.Repo.Repository.Name == ci.HeadRepo.Name)
+ allowEmptyPr := ci.BaseBranch != ci.HeadBranch || ctx.Repo.Repository.Name != ci.HeadRepo.Name
ctx.Data["AllowEmptyPr"] = allowEmptyPr
return !allowEmptyPr
@@ -614,7 +608,7 @@ func PrepareCompareDiff(
fileOnly := ctx.FormBool("file-only")
- diff, err := gitdiff.GetDiff(ctx, ci.HeadGitRepo,
+ diff, err := gitdiff.GetDiffFull(ctx, ci.HeadGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: beforeCommitID,
AfterCommitID: headCommitID,
@@ -647,15 +641,15 @@ func PrepareCompareDiff(
return false
}
- commits := processGitCommits(ctx, ci.CompareInfo.Commits)
+ commits := git_model.ParseCommitsWithStatus(ctx, ci.CompareInfo.Commits, ctx.Repo.Repository)
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)
if len(commits) == 1 {
c := commits[0]
- title = strings.TrimSpace(c.UserCommit.Summary())
+ title = strings.TrimSpace(c.Summary())
- body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n")
+ body := strings.Split(strings.TrimSpace(c.Message()), "\n")
if len(body) > 1 {
ctx.Data["content"] = strings.Join(body[1:], "\n")
}
@@ -717,6 +711,22 @@ func CompareDiff(ctx *context.Context) {
return
}
+ if ctx.Data["ComparingDiff"] != nil && ctx.Data["ComparingDiff"].(bool) {
+ err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffNormal, "", ctx.Resp)
+ if err != nil {
+ ctx.ServerError("ComparingDiff", err)
+ return
+ }
+ }
+
+ if ctx.Data["ComparingPatch"] != nil && ctx.Data["ComparingPatch"].(bool) {
+ err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffPatch, "", ctx.Resp)
+ if err != nil {
+ ctx.ServerError("ComparingPatch", err)
+ return
+ }
+ }
+
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
ctx.Data["DirectComparison"] = ci.DirectComparison
ctx.Data["OtherCompareSeparator"] = ".."
@@ -802,7 +812,8 @@ func CompareDiff(ctx *context.Context) {
if ci.DirectComparison {
separator = ".."
}
- ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID)
+ ctx.Data["Comparing"] = base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID)
+ ctx.Data["Title"] = "Comparing " + ctx.Data["Comparing"].(string)
ctx.Data["IsDiffCompare"] = true
_, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
@@ -928,9 +939,10 @@ func ExcerptBlob(ctx *context.Context) {
RightHunkSize: rightHunkSize,
},
}
- if direction == "up" {
+ switch direction {
+ case "up":
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...)
- } else if direction == "down" {
+ case "down":
section.Lines = append(section.Lines, lineSection)
}
}
@@ -942,7 +954,7 @@ func ExcerptBlob(ctx *context.Context) {
}
func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) {
- blob, err := commit.Tree.GetBlobByPath(filePath)
+ blob, err := commit.GetBlobByPath(filePath)
if err != nil {
return nil, err
}
diff --git a/routers/web/repo/contributors.go b/routers/web/repo/contributors.go
index 762fbf9379..094d13b54b 100644
--- a/routers/web/repo/contributors.go
+++ b/routers/web/repo/contributors.go
@@ -7,9 +7,9 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
- contributors_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
+ contributors_service "forgejo.org/services/repository"
)
const (
diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go
index 1e87bbf015..fc82ece4cb 100644
--- a/routers/web/repo/download.go
+++ b/routers/web/repo/download.go
@@ -5,18 +5,17 @@
package repo
import (
- "path"
"time"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
)
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
@@ -82,7 +81,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
}
-func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.Time) {
+func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
@@ -98,19 +97,14 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.T
return nil, nil
}
- info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
+ latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
- ctx.ServerError("GetCommitsInfo", err)
+ ctx.ServerError("GetTreePathLatestCommit", err)
return nil, nil
}
+ lastModified := &latestCommit.Committer.When
- if len(info) == 1 {
- // Not Modified
- lastModified = &info[0].Commit.Committer.When
- }
- blob = entry.Blob()
-
- return blob, lastModified
+ return entry.Blob(), lastModified
}
// SingleDownload download a file by repos path
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index f27ad62982..5114cc9c05 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -10,26 +10,26 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/utils"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/forms"
- files_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/forms"
+ files_service "forgejo.org/services/repository/files"
)
const (
@@ -585,7 +585,7 @@ func DeleteFilePost(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
+ ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
diff --git a/routers/web/repo/editor_test.go b/routers/web/repo/editor_test.go
index 4d565b5fd6..b5d40abdab 100644
--- a/routers/web/repo/editor_test.go
+++ b/routers/web/repo/editor_test.go
@@ -6,11 +6,11 @@ package repo
import (
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/services/contexttest"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
@@ -37,7 +37,7 @@ func TestCleanUploadName(t *testing.T) {
"..a.dotty../.folder../.name...": "..a.dotty../.folder../.name...",
}
for k, v := range kases {
- assert.EqualValues(t, cleanUploadFileName(k), v)
+ assert.Equal(t, cleanUploadFileName(k), v)
}
}
diff --git a/routers/web/repo/find.go b/routers/web/repo/find.go
index 9da4237c1e..808323631c 100644
--- a/routers/web/repo/find.go
+++ b/routers/web/repo/find.go
@@ -6,9 +6,9 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/flags/manage.go b/routers/web/repo/flags/manage.go
index 377a5c20f8..c97ef54818 100644
--- a/routers/web/repo/flags/manage.go
+++ b/routers/web/repo/flags/manage.go
@@ -6,10 +6,10 @@ package flags
import (
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index c1adca174f..42302d0e02 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -18,20 +18,20 @@ import (
"sync"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
"github.com/go-chi/cors"
)
@@ -78,7 +78,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") {
isPull = true
} else {
- isPull = ctx.Req.Method == "GET"
+ isPull = ctx.Req.Method == "GET" || ctx.Req.Method == "HEAD"
}
var accessMode perm.AccessMode
@@ -183,9 +183,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
if repoExist {
// Because of special ref "refs/for" .. , need delay write permission check
- if git.SupportProcReceive {
- accessMode = perm.AccessModeRead
- }
+ accessMode = perm.AccessModeRead
if ctx.Data["IsActionsToken"] == true {
taskID := ctx.Data["ActionsTaskID"].(int64)
diff --git a/routers/web/repo/githttp_test.go b/routers/web/repo/githttp_test.go
index 5ba8de3d63..0164b11f66 100644
--- a/routers/web/repo/githttp_test.go
+++ b/routers/web/repo/githttp_test.go
@@ -37,6 +37,6 @@ func TestContainsParentDirectorySeparator(t *testing.T) {
}
for i := range tests {
- assert.EqualValues(t, tests[i].b, containsParentDirectorySeparator(tests[i].v))
+ assert.Equal(t, tests[i].b, containsParentDirectorySeparator(tests[i].v))
}
}
diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go
index 6fa7579231..9d67f142fb 100644
--- a/routers/web/repo/helper.go
+++ b/routers/web/repo/helper.go
@@ -7,9 +7,9 @@ import (
"net/url"
"slices"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/services/context"
)
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
diff --git a/routers/web/repo/helper_test.go b/routers/web/repo/helper_test.go
index 844ad5bf79..2607fd32f8 100644
--- a/routers/web/repo/helper_test.go
+++ b/routers/web/repo/helper_test.go
@@ -6,7 +6,7 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
)
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 779cbd4b6a..a4f6f97a05 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -1,5 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
@@ -19,44 +20,44 @@ import (
"strings"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/emoji"
- "code.gitea.io/gitea/modules/git"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- issue_template "code.gitea.io/gitea/modules/issue/template"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/templates/vars"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- issue_service "code.gitea.io/gitea/services/issue"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ project_model "forgejo.org/models/project"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/emoji"
+ "forgejo.org/modules/git"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ issue_template "forgejo.org/modules/issue/template"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/templates/vars"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ issue_service "forgejo.org/services/issue"
+ pull_service "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
"code.forgejo.org/go-chi/binding"
)
@@ -186,9 +187,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
// 0 means issues with no label
// blank means labels will not be filtered for issues
selectLabels := ctx.FormString("labels")
- if selectLabels == "" {
+ switch selectLabels {
+ case "":
ctx.Data["AllLabels"] = true
- } else if selectLabels == "0" {
+ case "0":
ctx.Data["NoLabel"] = true
}
if len(selectLabels) > 0 {
@@ -203,8 +205,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
keyword = ""
}
- isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
-
var mileIDs []int64
if milestoneID > 0 || milestoneID == db.NoConditionID { // -1 to get those issues which have no any milestone assigned
mileIDs = []int64{milestoneID}
@@ -225,7 +225,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
IssueIDs: nil,
}
if keyword != "" {
- allIssueIDs, err := issueIDsFromSearch(ctx, keyword, isFuzzy, statsOpts)
+ allIssueIDs, err := issueIDsFromSearch(ctx, keyword, statsOpts)
if err != nil {
if issue_indexer.IsAvailable(ctx) {
ctx.ServerError("issueIDsFromSearch", err)
@@ -293,7 +293,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
var issues issues_model.IssueList
{
- ids, err := issueIDsFromSearch(ctx, keyword, isFuzzy, &issues_model.IssuesOptions{
+ ids, err := issueIDsFromSearch(ctx, keyword, &issues_model.IssuesOptions{
Paginator: &db.ListOptions{
Page: pager.Paginater.Current(),
PageSize: setting.UI.IssuePagingNum,
@@ -348,11 +348,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.ServerError("GetIssuesAllCommitStatus", err)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- for key := range commitStatuses {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
- }
- }
if err := issues.LoadAttributes(ctx); err != nil {
ctx.ServerError("issues.LoadAttributes", err)
@@ -427,9 +422,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
@@ -457,16 +453,16 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["OpenCount"] = issueStats.OpenCount
ctx.Data["ClosedCount"] = issueStats.ClosedCount
ctx.Data["AllCount"] = issueStats.AllCount
- linkStr := "?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&fuzzy=%t&archived=%t"
+ linkStr := "?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t"
ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr,
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels),
- milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
+ milestoneID, projectID, assigneeID, posterID, archived)
ctx.Data["OpenLink"] = fmt.Sprintf(linkStr,
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels),
- milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
+ milestoneID, projectID, assigneeID, posterID, archived)
ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr,
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "closed", url.QueryEscape(selectLabels),
- milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
+ milestoneID, projectID, assigneeID, posterID, archived)
ctx.Data["SelLabelIDs"] = labelIDs
ctx.Data["SelectLabels"] = selectLabels
ctx.Data["ViewType"] = viewType
@@ -475,7 +471,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["ProjectID"] = projectID
ctx.Data["AssigneeID"] = assigneeID
ctx.Data["PosterID"] = posterID
- ctx.Data["IsFuzzy"] = isFuzzy
ctx.Data["Keyword"] = keyword
ctx.Data["IsShowClosed"] = isShowClosed
switch {
@@ -498,17 +493,12 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
pager.AddParam(ctx, "assignee", "AssigneeID")
pager.AddParam(ctx, "poster", "PosterID")
pager.AddParam(ctx, "archived", "ShowArchivedLabels")
- pager.AddParam(ctx, "fuzzy", "IsFuzzy")
ctx.Data["Page"] = pager
}
-func issueIDsFromSearch(ctx *context.Context, keyword string, fuzzy bool, opts *issues_model.IssuesOptions) ([]int64, error) {
- ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
- func(o *issue_indexer.SearchOptions) {
- o.IsFuzzyKeyword = fuzzy
- },
- ))
+func issueIDsFromSearch(ctx *context.Context, keyword string, opts *issues_model.IssuesOptions) ([]int64, error) {
+ ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
if err != nil {
return nil, fmt.Errorf("SearchIssues: %w", err)
}
@@ -1267,7 +1257,11 @@ func NewIssuePost(ctx *context.Context) {
if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs); err != nil {
if errors.Is(err, user_model.ErrBlockedByUser) {
- ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user"))
+ if issue.IsPull {
+ ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user"))
+ } else {
+ ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user"))
+ }
return
} else if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
@@ -1291,28 +1285,41 @@ func NewIssuePost(ctx *context.Context) {
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" && projectID > 0 {
- ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(projectID, 10))
- } else {
- ctx.JSONRedirect(issue.Link())
+ project, err := project_model.GetProjectByID(ctx, projectID)
+ if err == nil {
+ if project.Type == project_model.TypeOrganization {
+ ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.Repo.Owner, project.ID))
+ } else {
+ ctx.JSONRedirect(project_model.ProjectLinkForRepo(repo, project.ID))
+ }
+ return
+ }
}
+ ctx.JSONRedirect(issue.Link())
}
// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue
func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) {
roleDescriptor := issues_model.RoleDescriptor{}
+ // Migrated comment with no associated local user
if hasOriginalAuthor {
return roleDescriptor, nil
}
+ // Special user that can't have associated contributions and permissions in the repo.
+ if poster.IsSystem() || poster.IsAPServerActor() {
+ return roleDescriptor, nil
+ }
+
+ // If the poster is the actual poster of the issue, enable Poster role.
+ roleDescriptor.IsPoster = issue.IsPoster(poster.ID)
+
perm, err := access_model.GetUserRepoPermission(ctx, repo, poster)
if err != nil {
return roleDescriptor, err
}
- // If the poster is the actual poster of the issue, enable Poster role.
- roleDescriptor.IsPoster = issue.IsPoster(poster.ID)
-
// Check if the poster is owner of the repo.
if perm.IsOwner() {
// If the poster isn't an admin, enable the owner role.
@@ -1465,6 +1472,7 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IssueType"] = "all"
}
+ ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
@@ -1685,7 +1693,7 @@ func ViewIssue(ctx *context.Context) {
return
}
ghostMilestone := &issues_model.Milestone{
- ID: -1,
+ ID: issues_model.GhostMilestoneID,
Name: ctx.Locale.TrString("repo.issues.deleted_milestone"),
}
if comment.OldMilestoneID > 0 && comment.OldMilestone == nil {
@@ -1786,15 +1794,6 @@ func ViewIssue(ctx *context.Context) {
ctx.ServerError("LoadPushCommits", err)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- for _, commit := range comment.Commits {
- if commit.Status == nil {
- continue
- }
- commit.Status.HideActionsURL(ctx)
- git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
- }
- }
} else if comment.Type == issues_model.CommentTypeAddTimeManual ||
comment.Type == issues_model.CommentTypeStopTracking ||
comment.Type == issues_model.CommentTypeDeleteTimeManual {
@@ -1824,8 +1823,7 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["LatestCloseCommentID"] = latestCloseCommentID
// Combine multiple label assignments into a single comment
- combineLabelComments(issue)
- combineRequestReviewComments(issue)
+ issues_model.CombineCommentsHistory(issue, time.Now().Unix())
getBranchData(ctx, issue)
if issue.IsPull {
@@ -2070,8 +2068,12 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["RefEndName"] = git.RefName(issue.Ref).ShortName()
ctx.Data["NewPinAllowed"] = pinAllowed
ctx.Data["PinEnabled"] = setting.Repository.Issue.MaxPinned != 0
+ ctx.Data["OpenGraphTitle"] = issue.Title
+ ctx.Data["OpenGraphURL"] = issue.HTMLURL()
+ ctx.Data["OpenGraphDescription"] = issue.Content
ctx.Data["OpenGraphImageURL"] = issue.SummaryCardURL()
ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.issues.summary_card_alt", issue.Title, issue.Repo.FullName())
+ ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlockedMultiple(ctx, []int64{issue.PosterID, issue.Repo.OwnerID}, ctx.Doer.ID)
prepareHiddenCommentType(ctx)
if ctx.Written() {
@@ -2115,7 +2117,7 @@ func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.Depende
}
repoPerms[blocker.RepoID] = perm
}
- if perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) {
+ if perm.CanReadIssuesOrPulls(blocker.IsPull) {
canRead = append(canRead, blocker)
} else {
notPermitted = append(notPermitted, blocker)
@@ -2773,7 +2775,7 @@ func SearchIssues(ctx *context.Context) {
IncludedAnyLabelIDs: includedAnyLabels,
MilestoneIDs: includedMilestones,
ProjectID: projectID,
- SortBy: issue_indexer.SortByCreatedDesc,
+ SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc),
}
if since != 0 {
@@ -2802,9 +2804,10 @@ func SearchIssues(ctx *context.Context) {
}
}
- // FIXME: It's unsupported to sort by priority repo when searching by indexer,
- // it's indeed an regression, but I think it is worth to support filtering by indexer first.
- _ = ctx.FormInt64("priority_repo_id")
+ priorityRepoID := ctx.FormInt64("priority_repo_id")
+ if priorityRepoID > 0 {
+ searchOpt.PriorityRepoID = optional.Some(priorityRepoID)
+ }
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
@@ -2942,7 +2945,7 @@ func ListIssues(ctx *context.Context) {
IsPull: isPull,
IsClosed: isClosed,
ProjectID: projectID,
- SortBy: issue_indexer.SortByCreatedDesc,
+ SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc),
}
if since != 0 {
searchOpt.UpdatedAfterUnix = optional.Some(since)
@@ -3104,7 +3107,7 @@ func NewComment(ctx *context.Context) {
// Check if issue admin/poster changes the status of issue.
if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) &&
(form.Status == "reopen" || form.Status == "close") &&
- !(issue.IsPull && issue.PullRequest.HasMerged) {
+ (!issue.IsPull || !issue.PullRequest.HasMerged) {
// Duplication and conflict check should apply to reopen pull request.
var pr *issues_model.PullRequest
@@ -3190,6 +3193,15 @@ func NewComment(ctx *context.Context) {
} else {
isClosed := form.Status == "close"
if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", isClosed); err != nil {
+ if errors.Is(err, user_model.ErrBlockedByUser) {
+ if issue.IsPull {
+ ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user"))
+ } else {
+ ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user"))
+ }
+ return
+ }
+
log.Error("ChangeStatus: %v", err)
if issues_model.IsErrDependenciesLeft(err) {
@@ -3231,7 +3243,7 @@ func NewComment(ctx *context.Context) {
comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments)
if err != nil {
if errors.Is(err, user_model.ErrBlockedByUser) {
- ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user"))
+ ctx.JSONError(ctx.Tr("repo.comment.blocked_by_user"))
} else {
ctx.ServerError("CreateIssueComment", err)
}
@@ -3581,9 +3593,9 @@ func GetIssueAttachments(ctx *context.Context) {
if ctx.Written() {
return
}
- attachments := make([]*api.Attachment, len(issue.Attachments))
+ attachments := make([]*api.WebAttachment, len(issue.Attachments))
for i := 0; i < len(issue.Attachments); i++ {
- attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i])
+ attachments[i] = convert.ToWebAttachment(ctx.Repo.Repository, issue.Attachments[i])
}
ctx.JSON(http.StatusOK, attachments)
}
@@ -3606,7 +3618,7 @@ func GetCommentAttachments(ctx *context.Context) {
return
}
- if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) {
+ if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{})
return
}
@@ -3616,13 +3628,13 @@ func GetCommentAttachments(ctx *context.Context) {
return
}
- attachments := make([]*api.Attachment, 0)
if err := comment.LoadAttachments(ctx); err != nil {
ctx.ServerError("LoadAttachments", err)
return
}
+ attachments := make([]*api.WebAttachment, len(comment.Attachments))
for i := 0; i < len(comment.Attachments); i++ {
- attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i]))
+ attachments[i] = convert.ToWebAttachment(ctx.Repo.Repository, comment.Attachments[i])
}
ctx.JSON(http.StatusOK, attachments)
}
@@ -3683,194 +3695,6 @@ func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment,
return attachHTML
}
-type RequestReviewTarget struct {
- user *user_model.User
- team *organization.Team
-}
-
-func (t *RequestReviewTarget) ID() int64 {
- if t.user != nil {
- return t.user.ID
- }
- return t.team.ID
-}
-
-func (t *RequestReviewTarget) Name() string {
- if t.user != nil {
- return t.user.GetDisplayName()
- }
- return t.team.Name
-}
-
-func (t *RequestReviewTarget) Type() string {
- if t.user != nil {
- return "user"
- }
- return "team"
-}
-
-// combineRequestReviewComments combine the nearby request review comments as one.
-func combineRequestReviewComments(issue *issues_model.Issue) {
- var prev, cur *issues_model.Comment
- for i := 0; i < len(issue.Comments); i++ {
- cur = issue.Comments[i]
- if i > 0 {
- prev = issue.Comments[i-1]
- }
- if i == 0 || cur.Type != issues_model.CommentTypeReviewRequest ||
- (prev != nil && prev.PosterID != cur.PosterID) ||
- (prev != nil && cur.CreatedUnix-prev.CreatedUnix >= 60) {
- if cur.Type == issues_model.CommentTypeReviewRequest && (cur.Assignee != nil || cur.AssigneeTeam != nil) {
- if cur.RemovedAssignee {
- if cur.AssigneeTeam != nil {
- cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- } else {
- if cur.AssigneeTeam != nil {
- cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- }
- }
- continue
- }
-
- // Previous comment is not a review request, so cannot group. Start a new group.
- if prev.Type != issues_model.CommentTypeReviewRequest {
- if cur.RemovedAssignee {
- if cur.AssigneeTeam != nil {
- cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- } else {
- if cur.AssigneeTeam != nil {
- cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- }
- continue
- }
-
- // Start grouping.
- if cur.RemovedAssignee {
- addedIndex := slices.IndexFunc(prev.AddedRequestReview, func(t issues_model.RequestReviewTarget) bool {
- if cur.AssigneeTeam != nil {
- return cur.AssigneeTeam.ID == t.ID() && t.Type() == "team"
- }
- return cur.Assignee.ID == t.ID() && t.Type() == "user"
- })
-
- // If for this target a AddedRequestReview, then we remove that entry. If it's not found, then add it to the RemovedRequestReview.
- if addedIndex == -1 {
- if cur.AssigneeTeam != nil {
- prev.RemovedRequestReview = append(prev.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- prev.RemovedRequestReview = append(prev.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- } else {
- prev.AddedRequestReview = slices.Delete(prev.AddedRequestReview, addedIndex, addedIndex+1)
- }
- } else {
- removedIndex := slices.IndexFunc(prev.RemovedRequestReview, func(t issues_model.RequestReviewTarget) bool {
- if cur.AssigneeTeam != nil {
- return cur.AssigneeTeam.ID == t.ID() && t.Type() == "team"
- }
- return cur.Assignee.ID == t.ID() && t.Type() == "user"
- })
-
- // If for this target a RemovedRequestReview, then we remove that entry. If it's not found, then add it to the AddedRequestReview.
- if removedIndex == -1 {
- if cur.AssigneeTeam != nil {
- prev.AddedRequestReview = append(prev.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam})
- } else {
- prev.AddedRequestReview = append(prev.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee})
- }
- } else {
- prev.RemovedRequestReview = slices.Delete(prev.RemovedRequestReview, removedIndex, removedIndex+1)
- }
- }
-
- // Propagate creation time.
- prev.CreatedUnix = cur.CreatedUnix
-
- // Remove the current comment since it has been combined to prev comment
- issue.Comments = append(issue.Comments[:i], issue.Comments[i+1:]...)
- i--
- }
-}
-
-// combineLabelComments combine the nearby label comments as one.
-func combineLabelComments(issue *issues_model.Issue) {
- var prev, cur *issues_model.Comment
- for i := 0; i < len(issue.Comments); i++ {
- cur = issue.Comments[i]
- if i > 0 {
- prev = issue.Comments[i-1]
- }
- if i == 0 || cur.Type != issues_model.CommentTypeLabel ||
- (prev != nil && prev.PosterID != cur.PosterID) ||
- (prev != nil && cur.CreatedUnix-prev.CreatedUnix >= 60) {
- if cur.Type == issues_model.CommentTypeLabel && cur.Label != nil {
- if cur.Content != "1" {
- cur.RemovedLabels = append(cur.RemovedLabels, cur.Label)
- } else {
- cur.AddedLabels = append(cur.AddedLabels, cur.Label)
- }
- }
- continue
- }
-
- if cur.Label != nil { // now cur MUST be label comment
- if prev.Type == issues_model.CommentTypeLabel { // we can combine them only prev is a label comment
- if cur.Content != "1" {
- // remove labels from the AddedLabels list if the label that was removed is already
- // in this list, and if it's not in this list, add the label to RemovedLabels
- addedAndRemoved := false
- for i, label := range prev.AddedLabels {
- if cur.Label.ID == label.ID {
- prev.AddedLabels = append(prev.AddedLabels[:i], prev.AddedLabels[i+1:]...)
- addedAndRemoved = true
- break
- }
- }
- if !addedAndRemoved {
- prev.RemovedLabels = append(prev.RemovedLabels, cur.Label)
- }
- } else {
- // remove labels from the RemovedLabels list if the label that was added is already
- // in this list, and if it's not in this list, add the label to AddedLabels
- removedAndAdded := false
- for i, label := range prev.RemovedLabels {
- if cur.Label.ID == label.ID {
- prev.RemovedLabels = append(prev.RemovedLabels[:i], prev.RemovedLabels[i+1:]...)
- removedAndAdded = true
- break
- }
- }
- if !removedAndAdded {
- prev.AddedLabels = append(prev.AddedLabels, cur.Label)
- }
- }
- prev.CreatedUnix = cur.CreatedUnix
- // remove the current comment since it has been combined to prev comment
- issue.Comments = append(issue.Comments[:i], issue.Comments[i+1:]...)
- i--
- } else { // if prev is not a label comment, start a new group
- if cur.Content != "1" {
- cur.RemovedLabels = append(cur.RemovedLabels, cur.Label)
- } else {
- cur.AddedLabels = append(cur.AddedLabels, cur.Label)
- }
- }
- }
- }
-}
-
// get all teams that current user can mention
func handleTeamMentions(ctx *context.Context) {
if ctx.Doer == nil || !ctx.Repo.Owner.IsOrganization() {
diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go
index 4ce76b2bb9..11d0de90de 100644
--- a/routers/web/repo/issue_content_history.go
+++ b/routers/web/repo/issue_content_history.go
@@ -9,12 +9,12 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/avatars"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/avatars"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/services/context"
"github.com/sergi/go-diff/diffmatchpatch"
)
@@ -160,15 +160,16 @@ func GetContentHistoryDetail(ctx *context.Context) {
diffHTMLBuf := bytes.Buffer{}
diffHTMLBuf.WriteString("")
for _, it := range diff {
- if it.Type == diffmatchpatch.DiffInsert {
+ switch it.Type {
+ case diffmatchpatch.DiffInsert:
diffHTMLBuf.WriteString("")
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
diffHTMLBuf.WriteString("")
- } else if it.Type == diffmatchpatch.DiffDelete {
+ case diffmatchpatch.DiffDelete:
diffHTMLBuf.WriteString("")
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
diffHTMLBuf.WriteString("")
- } else {
+ default:
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
}
}
diff --git a/routers/web/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go
index 66b38688ec..3764a6bd7e 100644
--- a/routers/web/repo/issue_dependency.go
+++ b/routers/web/repo/issue_dependency.go
@@ -6,10 +6,10 @@ package repo
import (
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// AddDependency adds new dependencies
diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go
index 81bee4dbb5..74674e9550 100644
--- a/routers/web/repo/issue_label.go
+++ b/routers/web/repo/issue_label.go
@@ -6,17 +6,17 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- issue_service "code.gitea.io/gitea/services/issue"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/label"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ issue_service "forgejo.org/services/issue"
)
const (
diff --git a/routers/web/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go
index 2b4915e855..0adcc39499 100644
--- a/routers/web/repo/issue_label_test.go
+++ b/routers/web/repo/issue_label_test.go
@@ -8,13 +8,13 @@ import (
"strconv"
"testing"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -39,7 +39,7 @@ func TestInitializeLabels(t *testing.T) {
contexttest.LoadRepo(t, ctx, 2)
web.SetForm(ctx, &forms.InitializeLabelsForm{TemplateName: "Default"})
InitializeLabels(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
RepoID: 2,
Name: "enhancement",
@@ -69,7 +69,7 @@ func TestRetrieveLabels(t *testing.T) {
assert.True(t, ok)
if assert.Len(t, labels, len(testCase.ExpectedLabelIDs)) {
for i, label := range labels {
- assert.EqualValues(t, testCase.ExpectedLabelIDs[i], label.ID)
+ assert.Equal(t, testCase.ExpectedLabelIDs[i], label.ID)
}
}
}
@@ -85,7 +85,7 @@ func TestNewLabel(t *testing.T) {
Color: "#abcdef",
})
NewLabel(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
Name: "newlabel",
Color: "#abcdef",
@@ -105,7 +105,7 @@ func TestUpdateLabel(t *testing.T) {
IsArchived: true,
})
UpdateLabel(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
ID: 2,
Name: "newnameforlabel",
@@ -121,7 +121,7 @@ func TestDeleteLabel(t *testing.T) {
contexttest.LoadRepo(t, ctx, 1)
ctx.Req.Form.Set("id", "2")
DeleteLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: 2})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{LabelID: 2})
assert.EqualValues(t, ctx.Tr("repo.issues.label_deletion_success"), ctx.Flash.SuccessMsg)
@@ -135,7 +135,7 @@ func TestUpdateIssueLabel_Clear(t *testing.T) {
ctx.Req.Form.Set("issue_ids", "1,3")
ctx.Req.Form.Set("action", "clear")
UpdateIssueLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 1})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 3})
unittest.CheckConsistencyFor(t, &issues_model.Label{})
@@ -161,7 +161,7 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) {
ctx.Req.Form.Set("action", testCase.Action)
ctx.Req.Form.Set("id", strconv.Itoa(int(testCase.LabelID)))
UpdateIssueLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
for _, issueID := range testCase.IssueIDs {
unittest.AssertExistsIf(t, testCase.ExpectedAdd, &issues_model.IssueLabel{
IssueID: issueID,
diff --git a/routers/web/repo/issue_lock.go b/routers/web/repo/issue_lock.go
index 1d5fc8a5f3..dea67ab996 100644
--- a/routers/web/repo/issue_lock.go
+++ b/routers/web/repo/issue_lock.go
@@ -4,10 +4,10 @@
package repo
import (
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// LockIssue locks an issue. This would limit commenting abilities to
diff --git a/routers/web/repo/issue_pin.go b/routers/web/repo/issue_pin.go
index 365c812681..5e2075a17f 100644
--- a/routers/web/repo/issue_pin.go
+++ b/routers/web/repo/issue_pin.go
@@ -6,10 +6,10 @@ package repo
import (
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
// IssuePinOrUnpin pin or unpin a Issue
diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go
index 70d42b27c0..5bc49464dd 100644
--- a/routers/web/repo/issue_stopwatch.go
+++ b/routers/web/repo/issue_stopwatch.go
@@ -7,10 +7,10 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/eventsource"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/eventsource"
+ "forgejo.org/services/context"
)
// IssueStopwatch creates or stops a stopwatch for the given issue.
diff --git a/routers/web/repo/issue_test.go b/routers/web/repo/issue_test.go
deleted file mode 100644
index d642c14b5f..0000000000
--- a/routers/web/repo/issue_test.go
+++ /dev/null
@@ -1,806 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repo
-
-import (
- "testing"
-
- issues_model "code.gitea.io/gitea/models/issues"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestCombineLabelComments(t *testing.T) {
- kases := []struct {
- name string
- beforeCombined []*issues_model.Comment
- afterCombined []*issues_model.Comment
- }{
- {
- name: "kase 1",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- CreatedUnix: 0,
- AddedLabels: []*issues_model.Label{},
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- },
- {
- name: "kase 2",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 70,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- CreatedUnix: 0,
- AddedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- CreatedUnix: 70,
- RemovedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- },
- {
- name: "kase 3",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 2,
- Content: "",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- CreatedUnix: 0,
- AddedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 2,
- Content: "",
- CreatedUnix: 0,
- RemovedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- },
- {
- name: "kase 4",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/backport",
- },
- CreatedUnix: 10,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- CreatedUnix: 10,
- AddedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- {
- Name: "kind/backport",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- },
- },
- },
- {
- name: "kase 5",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 2,
- Content: "testtest",
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- AddedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 2,
- Content: "testtest",
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- RemovedLabels: []*issues_model.Label{
- {
- Name: "kind/bug",
- },
- },
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- },
- },
- {
- name: "kase 6",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "reviewed/confirmed",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/feature",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeLabel,
- PosterID: 1,
- Content: "1",
- Label: &issues_model.Label{
- Name: "kind/bug",
- },
- AddedLabels: []*issues_model.Label{
- {
- Name: "reviewed/confirmed",
- },
- {
- Name: "kind/feature",
- },
- },
- CreatedUnix: 0,
- },
- },
- },
- }
-
- for _, kase := range kases {
- t.Run(kase.name, func(t *testing.T) {
- issue := issues_model.Issue{
- Comments: kase.beforeCombined,
- }
- combineLabelComments(&issue)
- assert.EqualValues(t, kase.afterCombined, issue.Comments)
- })
- }
-}
-
-func TestCombineReviewRequests(t *testing.T) {
- testCases := []struct {
- name string
- beforeCombined []*issues_model.Comment
- afterCombined []*issues_model.Comment
- }{
- {
- name: "case 1",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{},
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- },
- },
- {
- name: "case 2",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 2,
- Name: "Ghost 2",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{
- &RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- &RequestReviewTarget{
- user: &user_model.User{
- ID: 2,
- Name: "Ghost 2",
- },
- },
- },
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- },
- {
- name: "case 3",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{
- &RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- RemovedRequestReview: []issues_model.RequestReviewTarget{
- &RequestReviewTarget{
- team: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- },
- },
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- },
- {
- name: "case 4",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{
- &RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- RemovedRequestReview: []issues_model.RequestReviewTarget{},
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- },
- {
- name: "case 5",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{},
- RemovedRequestReview: []issues_model.RequestReviewTarget{},
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- },
- },
- {
- name: "case 6",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- RemovedAssignee: true,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- team: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- }},
- AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- }},
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- {
- Type: issues_model.CommentTypeComment,
- PosterID: 1,
- Content: "test",
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- team: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- }},
- RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- }},
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- },
- },
- },
- {
- name: "case 7",
- beforeCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- CreatedUnix: 0,
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- CreatedUnix: 61,
- },
- },
- afterCombined: []*issues_model.Comment{
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- user: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- }},
- Assignee: &user_model.User{
- ID: 1,
- Name: "Ghost",
- },
- },
- {
- Type: issues_model.CommentTypeReviewRequest,
- PosterID: 1,
- CreatedUnix: 0,
- RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{
- team: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- }},
- AssigneeTeam: &org_model.Team{
- ID: 1,
- Name: "Team 1",
- },
- },
- },
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- issue := issues_model.Issue{
- Comments: testCase.beforeCombined,
- }
- combineRequestReviewComments(&issue)
- assert.EqualValues(t, testCase.afterCombined[0], issue.Comments[0])
- })
- }
-}
diff --git a/routers/web/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go
index 241e434049..e63f7e2dc2 100644
--- a/routers/web/repo/issue_timetrack.go
+++ b/routers/web/repo/issue_timetrack.go
@@ -7,12 +7,12 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// AddTimeManually tracks time manually
diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go
index 5cff9f4ddd..5af223f865 100644
--- a/routers/web/repo/issue_watch.go
+++ b/routers/web/repo/issue_watch.go
@@ -7,10 +7,10 @@ import (
"net/http"
"strconv"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/main_test.go b/routers/web/repo/main_test.go
index 6e469cf2ed..8b30ad41ed 100644
--- a/routers/web/repo/main_test.go
+++ b/routers/web/repo/main_test.go
@@ -6,7 +6,7 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go
index ddda9f3ff2..9aba447433 100644
--- a/routers/web/repo/middlewares.go
+++ b/routers/web/repo/middlewares.go
@@ -7,12 +7,12 @@ import (
"fmt"
"strconv"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/optional"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
// SetEditorconfigIfExists set editor config as render variable
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index 0acf966bca..86d2461e94 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -9,23 +9,23 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
- "code.gitea.io/gitea/models/db"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/migrations"
- "code.gitea.io/gitea/services/task"
+ "forgejo.org/models"
+ admin_model "forgejo.org/models/admin"
+ "forgejo.org/models/db"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/migrations"
+ "forgejo.org/services/task"
)
const (
diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go
index 1c53f73fdb..920a9ee12a 100644
--- a/routers/web/repo/milestone.go
+++ b/routers/web/repo/milestone.go
@@ -9,18 +9,18 @@ import (
"net/url"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/issue"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/issue"
"xorm.io/builder"
)
diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go
index 11874ab0d0..c947fb99bf 100644
--- a/routers/web/repo/packages.go
+++ b/routers/web/repo/packages.go
@@ -6,13 +6,13 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/models/packages"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go
index d234f6c964..688ef19375 100644
--- a/routers/web/repo/patch.go
+++ b/routers/web/repo/patch.go
@@ -6,16 +6,16 @@ package repo
import (
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/repository/files"
)
const (
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 5826418b63..e5bd06e987 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -9,22 +9,22 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/perm"
- project_model "code.gitea.io/gitea/models/project"
- attachment_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/perm"
+ project_model "forgejo.org/models/project"
+ attachment_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -120,7 +120,7 @@ func Projects(ctx *context.Context) {
pager.AddParam(ctx, "state", "State")
ctx.Data["Page"] = pager
- ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
+ ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["IsProjectsPage"] = true
ctx.Data["SortType"] = sortType
@@ -146,7 +146,7 @@ func RenderNewProject(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs()
ctx.Data["CardTypes"] = project_model.GetCardConfig()
- ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
+ ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects"
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@@ -196,7 +196,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
- ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
+ ctx.JSONRedirect(project_model.ProjectLinkForRepo(ctx.Repo.Repository, id))
}
// DeleteProject delete a project
@@ -228,7 +228,7 @@ func DeleteProject(ctx *context.Context) {
func RenderEditProject(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
ctx.Data["PageIsEditProjects"] = true
- ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
+ ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
@@ -250,7 +250,7 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["content"] = p.Description
ctx.Data["card_type"] = p.CardType
ctx.Data["redirect"] = ctx.FormString("redirect")
- ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), p.ID)
+ ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, p.ID)
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@@ -262,9 +262,9 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
ctx.Data["PageIsEditProjects"] = true
- ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
+ ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
- ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), projectID)
+ ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, projectID)
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplProjectsNew)
@@ -378,7 +378,7 @@ func ViewProject(ctx *context.Context) {
ctx.Data["Title"] = project.Title
ctx.Data["IsProjectsPage"] = true
- ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
+ ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects)
ctx.Data["Project"] = project
ctx.Data["IssuesMap"] = issuesMap
ctx.Data["Columns"] = columns
diff --git a/routers/web/repo/projects_test.go b/routers/web/repo/projects_test.go
index d61230a57e..bc8b747980 100644
--- a/routers/web/repo/projects_test.go
+++ b/routers/web/repo/projects_test.go
@@ -6,8 +6,8 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/unittest"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
)
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 98dacc1a0d..4e365f24ea 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -10,45 +10,49 @@ import (
"errors"
"fmt"
"html"
+ "html/template"
"net/http"
"net/url"
+ "path"
"strconv"
"strings"
- "time"
- "code.gitea.io/gitea/models"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- pull_model "code.gitea.io/gitea/models/pull"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/emoji"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- issue_template "code.gitea.io/gitea/modules/issue/template"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/utils"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/automerge"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/gitdiff"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ activities_model "forgejo.org/models/activities"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ pull_model "forgejo.org/models/pull"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/emoji"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ issue_template "forgejo.org/modules/issue/template"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/utils"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/automerge"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/gitdiff"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
"github.com/gobwas/glob"
)
@@ -401,6 +405,7 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) {
// GetPullDiffStats get Pull Requests diff stats
func GetPullDiffStats(ctx *context.Context) {
+ // FIXME: this getPullInfo seems to be a duplicate call with other route handlers
issue, ok := getPullInfo(ctx)
if !ok {
return
@@ -408,15 +413,15 @@ func GetPullDiffStats(ctx *context.Context) {
pull := issue.PullRequest
mergeBaseCommitID := GetMergedBaseCommitID(ctx, issue)
-
if mergeBaseCommitID == "" {
ctx.NotFound("PullFiles", nil)
return
}
+ // do not report 500 server error to end users if error occurs, otherwise a PR missing ref won't be able to view.
headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
- ctx.ServerError("GetRefCommitID", err)
+ log.Error("Failed to GetRefCommitID: %v, repo: %v", err, ctx.Repo.Repository.FullName())
return
}
@@ -498,6 +503,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
ctx.Data["IsPullRequestBroken"] = true
ctx.Data["BaseTarget"] = pull.BaseBranch
ctx.Data["NumCommits"] = 0
+ ctx.Data["CommitIDs"] = map[string]bool{}
ctx.Data["NumFiles"] = 0
return nil
}
@@ -508,6 +514,12 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
ctx.Data["NumCommits"] = len(compareInfo.Commits)
ctx.Data["NumFiles"] = compareInfo.NumFiles
+ commitIDs := map[string]bool{}
+ for _, commit := range compareInfo.Commits {
+ commitIDs[commit.ID.String()] = true
+ }
+ ctx.Data["CommitIDs"] = commitIDs
+
if len(compareInfo.Commits) != 0 {
sha := compareInfo.Commits[0].ID.String()
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
@@ -515,9 +527,6 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
- }
if len(commitStatuses) != 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
@@ -581,9 +590,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
- }
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
@@ -597,6 +603,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["IsPullRequestBroken"] = true
ctx.Data["BaseTarget"] = pull.BaseBranch
ctx.Data["NumCommits"] = 0
+ ctx.Data["CommitIDs"] = map[string]bool{}
ctx.Data["NumFiles"] = 0
return nil
}
@@ -607,6 +614,13 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["NumCommits"] = len(compareInfo.Commits)
ctx.Data["NumFiles"] = compareInfo.NumFiles
+
+ commitIDs := map[string]bool{}
+ for _, commit := range compareInfo.Commits {
+ commitIDs[commit.ID.String()] = true
+ }
+ ctx.Data["CommitIDs"] = commitIDs
+
return compareInfo
}
@@ -665,6 +679,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
}
ctx.Data["BaseTarget"] = pull.BaseBranch
ctx.Data["NumCommits"] = 0
+ ctx.Data["CommitIDs"] = map[string]bool{}
ctx.Data["NumFiles"] = 0
return nil
}
@@ -677,9 +692,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
- }
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
@@ -727,7 +739,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["HeadBranchCommitID"] = headBranchSha
ctx.Data["PullHeadCommitID"] = sha
- if pull.HeadRepo == nil || !headBranchExist || (!pull.Issue.IsClosed && (headBranchSha != sha)) {
+ if pull.HeadRepo == nil || !headBranchExist || (!pull.Issue.IsClosed && !pull.IsChecking() && (headBranchSha != sha)) {
ctx.Data["IsPullRequestBroken"] = true
if pull.IsSameRepo() {
ctx.Data["HeadTarget"] = pull.HeadBranch
@@ -745,6 +757,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["IsPullRequestBroken"] = true
ctx.Data["BaseTarget"] = pull.BaseBranch
ctx.Data["NumCommits"] = 0
+ ctx.Data["CommitIDs"] = map[string]bool{}
ctx.Data["NumFiles"] = 0
return nil
}
@@ -769,6 +782,13 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["NumCommits"] = len(compareInfo.Commits)
ctx.Data["NumFiles"] = compareInfo.NumFiles
+
+ commitIDs := map[string]bool{}
+ for _, commit := range compareInfo.Commits {
+ commitIDs[commit.ID.String()] = true
+ }
+ ctx.Data["CommitIDs"] = commitIDs
+
return compareInfo
}
@@ -847,7 +867,7 @@ func ViewPullCommits(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
- commits := processGitCommits(ctx, prInfo.Commits)
+ commits := git_model.ParseCommitsWithStatus(ctx, prInfo.Commits, ctx.Repo.Repository)
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)
@@ -892,7 +912,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
foundStartCommit := len(specifiedStartCommit) == 0
foundEndCommit := len(specifiedEndCommit) == 0
- if !(foundStartCommit && foundEndCommit) {
+ if !foundStartCommit || !foundEndCommit {
for _, commit := range prInfo.Commits {
if commit.ID.String() == specifiedStartCommit {
foundStartCommit = true
@@ -907,7 +927,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}
}
- if !(foundStartCommit && foundEndCommit) {
+ if !foundStartCommit || !foundEndCommit {
ctx.NotFound("Given SHA1 not found for this PR", nil)
return
}
@@ -928,7 +948,85 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.Data["IsShowingOnlySingleCommit"] = willShowSpecifiedCommit
- if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
+ if willShowSpecifiedCommit {
+ commitID := specifiedEndCommit
+
+ ctx.Data["CommitID"] = commitID
+
+ var prevCommit, curCommit, nextCommit *git.Commit
+
+ // Iterate in reverse to properly map "previous" and "next" buttons
+ for i := len(prInfo.Commits) - 1; i >= 0; i-- {
+ commit := prInfo.Commits[i]
+
+ if curCommit != nil {
+ nextCommit = commit
+ break
+ }
+
+ if commit.ID.String() == commitID {
+ curCommit = commit
+ } else {
+ prevCommit = commit
+ }
+ }
+
+ if curCommit == nil {
+ ctx.ServerError("Repo.GitRepo.viewPullFiles", git.ErrNotExist{ID: commitID})
+ return
+ }
+
+ ctx.Data["Commit"] = curCommit
+ if prevCommit != nil {
+ ctx.Data["PrevCommitLink"] = path.Join(ctx.Repo.RepoLink, "pulls", strconv.FormatInt(issue.Index, 10), "commits", prevCommit.ID.String())
+ }
+ if nextCommit != nil {
+ ctx.Data["NextCommitLink"] = path.Join(ctx.Repo.RepoLink, "pulls", strconv.FormatInt(issue.Index, 10), "commits", nextCommit.ID.String())
+ }
+
+ statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
+ if err != nil {
+ log.Error("GetLatestCommitStatus: %v", err)
+ }
+
+ ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
+ ctx.Data["CommitStatuses"] = statuses
+
+ verification := asymkey_model.ParseCommitWithSignature(ctx, curCommit)
+ ctx.Data["Verification"] = verification
+ ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, curCommit)
+
+ if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
+ return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID)
+ }, nil); err != nil {
+ ctx.ServerError("CalculateTrustStatus", err)
+ return
+ }
+
+ note := &git.Note{}
+ err = git.GetNote(ctx, ctx.Repo.GitRepo, specifiedEndCommit, note)
+ if err == nil {
+ ctx.Data["NoteCommit"] = note.Commit
+ ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
+ ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{
+ Links: markup.Links{
+ Base: ctx.Repo.RepoLink,
+ BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
+ },
+ Metas: ctx.Repo.Repository.ComposeMetas(ctx),
+ GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
+ }, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{}))))
+ if err != nil {
+ ctx.ServerError("RenderCommitMessage", err)
+ return
+ }
+ }
+
+ endCommitID = commitID
+ startCommitID = prInfo.MergeBase
+ ctx.Data["IsShowingAllCommits"] = false
+ } else if willShowSpecifiedCommitRange {
if len(specifiedEndCommit) > 0 {
endCommitID = specifiedEndCommit
} else {
@@ -939,6 +1037,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
} else {
startCommitID = prInfo.MergeBase
}
+
ctx.Data["IsShowingAllCommits"] = false
} else {
endCommitID = headCommitID
@@ -946,10 +1045,10 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.Data["IsShowingAllCommits"] = true
}
- ctx.Data["Username"] = ctx.Repo.Owner.Name
- ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["AfterCommitID"] = endCommitID
ctx.Data["BeforeCommitID"] = startCommitID
+ ctx.Data["Username"] = ctx.Repo.Owner.Name
+ ctx.Data["Reponame"] = ctx.Repo.Repository.Name
fileOnly := ctx.FormBool("file-only")
@@ -981,7 +1080,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
// as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in.
if !ctx.IsSigned || willShowSpecifiedCommit || willShowSpecifiedCommitRange {
- diff, err = gitdiff.GetDiff(ctx, gitRepo, diffOptions, files...)
+ diff, err = gitdiff.GetDiffFull(ctx, gitRepo, diffOptions, files...)
methodWithError = "GetDiff"
} else {
diff, err = gitdiff.SyncAndGetUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diffOptions, files...)
@@ -1207,8 +1306,6 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
- time.Sleep(1 * time.Second)
-
ctx.Flash.Success(ctx.Tr("repo.pulls.update_branch_success"))
ctx.Redirect(issue.Link())
}
@@ -1321,8 +1418,8 @@ func MergePullRequest(ctx *context.Context) {
} else if models.IsErrMergeConflicts(err) {
conflictError := err.(models.ErrMergeConflicts)
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
- "Message": ctx.Tr("repo.editor.merge_conflict"),
- "Summary": ctx.Tr("repo.editor.merge_conflict_summary"),
+ "Message": ctx.Tr("repo.pulls.merge_conflict"),
+ "Summary": ctx.Tr("repo.pulls.merge_conflict_summary"),
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut),
})
if err != nil {
diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go
index eb8dd83d9c..941e428039 100644
--- a/routers/web/repo/pull_review.go
+++ b/routers/web/repo/pull_review.go
@@ -8,17 +8,17 @@ import (
"fmt"
"net/http"
- issues_model "code.gitea.io/gitea/models/issues"
- pull_model "code.gitea.io/gitea/models/pull"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/forms"
- pull_service "code.gitea.io/gitea/services/pull"
+ issues_model "forgejo.org/models/issues"
+ pull_model "forgejo.org/models/pull"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/forms"
+ pull_service "forgejo.org/services/pull"
)
const (
@@ -211,9 +211,10 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori
return
}
ctx.Data["AfterCommitID"] = pullHeadCommitID
- if origin == "diff" {
+ switch origin {
+ case "diff":
ctx.HTML(http.StatusOK, tplDiffConversation)
- } else if origin == "timeline" {
+ case "timeline":
ctx.HTML(http.StatusOK, tplTimelineConversation)
}
}
diff --git a/routers/web/repo/pull_review_test.go b/routers/web/repo/pull_review_test.go
index 329e83fe4b..14e6714a63 100644
--- a/routers/web/repo/pull_review_test.go
+++ b/routers/web/repo/pull_review_test.go
@@ -8,13 +8,13 @@ import (
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/templates"
+ "forgejo.org/services/context"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/pull"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go
index c158fb30b6..211b1b2b12 100644
--- a/routers/web/repo/recent_commits.go
+++ b/routers/web/repo/recent_commits.go
@@ -4,12 +4,10 @@
package repo
import (
- "errors"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/services/context"
- contributors_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
)
const (
@@ -26,16 +24,3 @@ func RecentCommits(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplRecentCommits)
}
-
-// RecentCommitsData returns JSON of recent commits data
-func RecentCommitsData(ctx *context.Context) {
- if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil {
- if errors.Is(err, contributors_service.ErrAwaitGeneration) {
- ctx.Status(http.StatusAccepted)
- return
- }
- ctx.ServerError("RecentCommitsData", err)
- } else {
- ctx.JSON(http.StatusOK, contributorStats["total"].Weeks)
- }
-}
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 1791788743..a6de337192 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -1,5 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
@@ -10,29 +11,29 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/web/feed"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/context/upload"
- "code.gitea.io/gitea/services/forms"
- releaseservice "code.gitea.io/gitea/services/release"
+ "forgejo.org/models"
+ "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/web/feed"
+ "forgejo.org/services/context"
+ "forgejo.org/services/context/upload"
+ "forgejo.org/services/forms"
+ releaseservice "forgejo.org/services/release"
)
const (
@@ -249,7 +250,7 @@ func addVerifyTagToContext(ctx *context.Context) {
if verification == nil {
return false
}
- return verification.Reason != "gpg.error.not_signed_commit"
+ return verification.Reason != asymkey.NotSigned
}
}
diff --git a/routers/web/repo/release_test.go b/routers/web/repo/release_test.go
index 5c7b6e2e8f..785b1fdf69 100644
--- a/routers/web/repo/release_test.go
+++ b/routers/web/repo/release_test.go
@@ -6,13 +6,13 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go
index e64db03e20..b31e2e203a 100644
--- a/routers/web/repo/render.go
+++ b/routers/web/repo/render.go
@@ -9,13 +9,13 @@ import (
"net/http"
"path"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
// RenderFile renders a file by repos path
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 1c4fb39546..493787ad8b 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -12,32 +12,32 @@ import (
"slices"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- repo_service "code.gitea.io/gitea/services/repository"
- archiver_service "code.gitea.io/gitea/services/repository/archiver"
- commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ repo_service "forgejo.org/services/repository"
+ archiver_service "forgejo.org/services/repository/archiver"
+ commitstatus_service "forgejo.org/services/repository/commitstatus"
)
const (
@@ -474,7 +474,7 @@ func Download(ctx *context.Context) {
uri := ctx.Params("*")
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil {
- ctx.ServerError("ParseFileName", err)
+ ctx.NotFound("ParseFileName", err)
return
}
aReq, err := archiver_service.NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
@@ -554,7 +554,7 @@ func InitiateDownload(ctx *context.Context) {
uri := ctx.Params("*")
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil {
- ctx.ServerError("ParseFileName", err)
+ ctx.NotFound("ParseFileName", err)
return
}
aReq, err := archiver_service.NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
@@ -693,9 +693,6 @@ func SearchRepo(ctx *context.Context) {
ctx.JSON(http.StatusInternalServerError, nil)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, latestCommitStatuses)
- }
results := make([]*repo_service.WebSearchRepository, len(repos))
for i, repo := range repos {
@@ -782,3 +779,27 @@ func PrepareBranchList(ctx *context.Context) {
}
ctx.Data["Branches"] = brs
}
+
+func SyncFork(ctx *context.Context) {
+ redirectURL := fmt.Sprintf("%s/src/branch/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(ctx.Repo.BranchName))
+ branch := ctx.FormString("branch")
+
+ syncForkInfo, err := repo_service.GetSyncForkInfo(ctx, ctx.Repo.Repository, branch)
+ if err != nil {
+ ctx.ServerError("GetSyncForkInfo", err)
+ return
+ }
+
+ if !syncForkInfo.Allowed {
+ ctx.Redirect(redirectURL)
+ return
+ }
+
+ err = repo_service.SyncFork(ctx, ctx.Doer, ctx.Repo.Repository, branch)
+ if err != nil {
+ ctx.ServerError("SyncFork", err)
+ return
+ }
+
+ ctx.Redirect(redirectURL)
+}
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index 442034b287..1671378a3b 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -7,12 +7,12 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ code_indexer "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const tplSearch base.TplName = "repo/search"
@@ -21,14 +21,14 @@ type searchMode int
const (
ExactSearchMode searchMode = iota
- FuzzySearchMode
+ UnionSearchMode
RegExpSearchMode
)
func searchModeFromString(s string) searchMode {
switch s {
case "fuzzy", "union":
- return FuzzySearchMode
+ return UnionSearchMode
case "regexp":
return RegExpSearchMode
default:
@@ -40,8 +40,8 @@ func (m searchMode) String() string {
switch m {
case ExactSearchMode:
return "exact"
- case FuzzySearchMode:
- return "fuzzy"
+ case UnionSearchMode:
+ return "union"
case RegExpSearchMode:
return "regexp"
default:
@@ -49,6 +49,24 @@ func (m searchMode) String() string {
}
}
+func (m searchMode) ToIndexer() code_indexer.SearchMode {
+ if m == ExactSearchMode {
+ return code_indexer.SearchModeExact
+ }
+ return code_indexer.SearchModeUnion
+}
+
+func (m searchMode) ToGitGrep() git.GrepMode {
+ switch m {
+ case RegExpSearchMode:
+ return git.RegExpGrepMode
+ case UnionSearchMode:
+ return git.FixedAnyGrepMode
+ default:
+ return git.FixedGrepMode
+ }
+}
+
// Search render repository search page
func Search(ctx *context.Context) {
language := ctx.FormTrim("l")
@@ -59,7 +77,7 @@ func Search(ctx *context.Context) {
if modeStr := ctx.FormString("mode"); len(modeStr) > 0 {
mode = searchModeFromString(modeStr)
} else if ctx.FormOptionalBool("fuzzy").ValueOrDefault(true) { // for backward compatibility in links
- mode = FuzzySearchMode
+ mode = UnionSearchMode
}
ctx.Data["Keyword"] = keyword
@@ -90,11 +108,11 @@ func Search(ctx *context.Context) {
if setting.Indexer.RepoIndexerEnabled {
var err error
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: []int64{ctx.Repo.Repository.ID},
- Keyword: keyword,
- IsKeywordFuzzy: mode == FuzzySearchMode,
- Language: language,
- Filename: path,
+ RepoIDs: []int64{ctx.Repo.Repository.ID},
+ Keyword: keyword,
+ Mode: mode.ToIndexer(),
+ Language: language,
+ Filename: path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@@ -110,19 +128,12 @@ func Search(ctx *context.Context) {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
}
} else {
- grepOpt := git.GrepOptions{
+ res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{
ContextLineNumber: 1,
RefName: ctx.Repo.RefName,
Filename: path,
- }
- switch mode {
- case FuzzySearchMode:
- grepOpt.Mode = git.FixedAnyGrepMode
- ctx.Data["CodeSearchMode"] = "union"
- case RegExpSearchMode:
- grepOpt.Mode = git.RegExpGrepMode
- }
- res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, grepOpt)
+ Mode: mode.ToGitGrep(),
+ })
if err != nil {
ctx.ServerError("GrepSearch", err)
return
diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go
index 504f57cfc2..84d7cccdb8 100644
--- a/routers/web/repo/setting/avatar.go
+++ b/routers/web/repo/setting/avatar.go
@@ -8,13 +8,13 @@ import (
"fmt"
"io"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ repo_service "forgejo.org/services/repository"
)
// UpdateAvatarSetting update repo's avatar
@@ -46,7 +46,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
return fmt.Errorf("io.ReadAll: %w", err)
}
st := typesniffer.DetectContentType(data)
- if !(st.IsImage() && !st.IsSvgImage()) {
+ if !st.IsImage() || st.IsSvgImage() {
return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image"))
}
if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil {
diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go
index 75b55151e7..a816a16bc8 100644
--- a/routers/web/repo/setting/collaboration.go
+++ b/routers/web/repo/setting/collaboration.go
@@ -8,19 +8,19 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/mailer"
- org_service "code.gitea.io/gitea/services/org"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/mailer"
+ org_service "forgejo.org/services/org"
+ repo_service "forgejo.org/services/repository"
)
// Collaboration render a repository's collaboration page
diff --git a/routers/web/repo/setting/default_branch.go b/routers/web/repo/setting/default_branch.go
index 881d148afc..1c6033f1e4 100644
--- a/routers/web/repo/setting/default_branch.go
+++ b/routers/web/repo/setting/default_branch.go
@@ -6,12 +6,12 @@ package setting
import (
"net/http"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/web/repo"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/routers/web/repo"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
// SetDefaultBranchPost set default branch
diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go
index abc3eb4af1..c59f0e90c2 100644
--- a/routers/web/repo/setting/deploy_key.go
+++ b/routers/web/repo/setting/deploy_key.go
@@ -6,14 +6,14 @@ package setting
import (
"net/http"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// DeployKeys render the deploy keys list of a repository page
diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go
index 217a01c90c..a50bce2a27 100644
--- a/routers/web/repo/setting/git_hooks.go
+++ b/routers/web/repo/setting/git_hooks.go
@@ -6,8 +6,8 @@ package setting
import (
"net/http"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/git"
+ "forgejo.org/services/context"
)
// GitHooks hooks of a repository
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index 7e3634375a..b9cb86bd08 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -14,20 +14,20 @@ import (
"strconv"
"strings"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/git/pipeline"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/git/pipeline"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
)
const (
@@ -342,6 +342,20 @@ func LFSFileGet(ctx *context.Context) {
ctx.Data["IsVideoFile"] = true
case st.IsAudio():
ctx.Data["IsAudioFile"] = true
+ case st.Is3DModel():
+ ctx.Data["Is3DModelFile"] = true
+ switch {
+ case st.IsGLB():
+ ctx.Data["IsGLBFile"] = true
+ case st.IsSTL():
+ ctx.Data["IsSTLFile"] = true
+ case st.IsGLTF():
+ ctx.Data["IsGLTFFile"] = true
+ case st.IsOBJ():
+ ctx.Data["IsOBJFile"] = true
+ case st.Is3MF():
+ ctx.Data["Is3MFFile"] = true
+ }
case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()):
ctx.Data["IsImageFile"] = true
}
diff --git a/routers/web/repo/setting/main_test.go b/routers/web/repo/setting/main_test.go
index c414b853e5..6b5a70ba08 100644
--- a/routers/web/repo/setting/main_test.go
+++ b/routers/web/repo/setting/main_test.go
@@ -6,7 +6,7 @@ package setting
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go
index b2f5798a26..18efbc37c4 100644
--- a/routers/web/repo/setting/protected_branch.go
+++ b/routers/web/repo/setting/protected_branch.go
@@ -11,17 +11,17 @@ import (
"strings"
"time"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/web/repo"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- pull_service "code.gitea.io/gitea/services/pull"
- "code.gitea.io/gitea/services/repository"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/web/repo"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ pull_service "forgejo.org/services/pull"
+ "forgejo.org/services/repository"
"github.com/gobwas/glob"
)
diff --git a/routers/web/repo/setting/protected_tag.go b/routers/web/repo/setting/protected_tag.go
index 2c25b650b9..5735149dfd 100644
--- a/routers/web/repo/setting/protected_tag.go
+++ b/routers/web/repo/setting/protected_tag.go
@@ -8,15 +8,15 @@ import (
"net/http"
"strings"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
diff --git a/routers/web/repo/setting/runners.go b/routers/web/repo/setting/runners.go
index 9dce5d13b7..32c8667825 100644
--- a/routers/web/repo/setting/runners.go
+++ b/routers/web/repo/setting/runners.go
@@ -8,13 +8,13 @@ import (
"net/http"
"net/url"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- actions_shared "code.gitea.io/gitea/routers/web/shared/actions"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ actions_shared "forgejo.org/routers/web/shared/actions"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/setting/secrets.go b/routers/web/repo/setting/secrets.go
index d4d56bfc57..11c83e8bd6 100644
--- a/routers/web/repo/setting/secrets.go
+++ b/routers/web/repo/setting/secrets.go
@@ -7,11 +7,11 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- shared "code.gitea.io/gitea/routers/web/shared/secrets"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ shared "forgejo.org/routers/web/shared/secrets"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index df7e388680..6f35e19880 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -13,34 +13,34 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/indexer/stats"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- actions_service "code.gitea.io/gitea/services/actions"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/federation"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/migrations"
- mirror_service "code.gitea.io/gitea/services/mirror"
- repo_service "code.gitea.io/gitea/services/repository"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/indexer/issues"
+ "forgejo.org/modules/indexer/stats"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ actions_service "forgejo.org/services/actions"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/federation"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/migrations"
+ mirror_service "forgejo.org/services/mirror"
+ repo_service "forgejo.org/services/repository"
+ wiki_service "forgejo.org/services/wiki"
)
const (
@@ -64,6 +64,9 @@ func SettingsCtxData(ctx *context.Context) {
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
+ ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
+ ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
+ ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
ctx.Data["SigningKeyAvailable"] = len(signing) > 0
@@ -105,6 +108,10 @@ func Units(ctx *context.Context) {
func UnitsPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.RepoUnitSettingForm)
+ if ctx.HasError() {
+ ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/units")
+ return
+ }
repo := ctx.Repo.Repository
@@ -146,11 +153,9 @@ func UnitsPost(ctx *context.Context) {
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
- var wikiPermissions repo_model.UnitAccessMode
+ wikiPermissions := repo_model.UnitAccessModeUnset
if form.GloballyWriteableWiki {
wikiPermissions = repo_model.UnitAccessModeWrite
- } else {
- wikiPermissions = repo_model.UnitAccessModeRead
}
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
@@ -772,6 +777,8 @@ func SettingsPost(ctx *context.Context) {
return
}
code.UpdateRepoIndexer(ctx.Repo.Repository)
+ case "issues":
+ issues.UpdateRepoIndexer(ctx, ctx.Repo.Repository.ID)
default:
ctx.NotFound("", nil)
return
@@ -1030,7 +1037,7 @@ func SettingsPost(ctx *context.Context) {
return
}
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
}
diff --git a/routers/web/repo/setting/settings_test.go b/routers/web/repo/setting/settings_test.go
index 0c8553faea..3a81b85e4c 100644
--- a/routers/web/repo/setting/settings_test.go
+++ b/routers/web/repo/setting/settings_test.go
@@ -7,41 +7,27 @@ import (
"net/http"
"testing"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
- repo_service "code.gitea.io/gitea/services/repository"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
+ repo_service "forgejo.org/services/repository"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func createSSHAuthorizedKeysTmpPath(t *testing.T) func() {
- tmpDir := t.TempDir()
-
- oldPath := setting.SSH.RootPath
- setting.SSH.RootPath = tmpDir
-
- return func() {
- setting.SSH.RootPath = oldPath
- }
-}
-
func TestAddReadOnlyDeployKey(t *testing.T) {
- if deferable := createSSHAuthorizedKeysTmpPath(t); deferable != nil {
- defer deferable()
- } else {
- return
- }
+ defer test.MockVariableValue(&setting.SSH.RootPath, t.TempDir())()
unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "user2/repo1/settings/keys")
@@ -55,7 +41,7 @@ func TestAddReadOnlyDeployKey(t *testing.T) {
}
web.SetForm(ctx, &addKeyForm)
DeployKeysPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{
Name: addKeyForm.Title,
@@ -65,11 +51,7 @@ func TestAddReadOnlyDeployKey(t *testing.T) {
}
func TestAddReadWriteOnlyDeployKey(t *testing.T) {
- if deferable := createSSHAuthorizedKeysTmpPath(t); deferable != nil {
- defer deferable()
- } else {
- return
- }
+ defer test.MockVariableValue(&setting.SSH.RootPath, t.TempDir())()
unittest.PrepareTestEnv(t)
@@ -85,7 +67,7 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) {
}
web.SetForm(ctx, &addKeyForm)
DeployKeysPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{
Name: addKeyForm.Title,
@@ -124,7 +106,7 @@ func TestCollaborationPost(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
exists, err := repo_model.IsCollaborator(ctx, re.ID, 4)
require.NoError(t, err)
@@ -150,7 +132,7 @@ func TestCollaborationPost_InactiveUser(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -184,7 +166,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
exists, err := repo_model.IsCollaborator(ctx, re.ID, 4)
require.NoError(t, err)
@@ -193,7 +175,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
// Try adding the same collaborator again
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -215,7 +197,7 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -255,7 +237,7 @@ func TestAddTeamPost(t *testing.T) {
AddTeamPost(ctx)
assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.Empty(t, ctx.Flash.ErrorMsg)
}
@@ -295,7 +277,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
AddTeamPost(ctx)
assert.False(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -336,7 +318,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
AddTeamPost(ctx)
assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -369,7 +351,7 @@ func TestAddTeamPost_NonExistentTeam(t *testing.T) {
ctx.Repo = repo
AddTeamPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
diff --git a/routers/web/repo/setting/variables.go b/routers/web/repo/setting/variables.go
index 4fb8c06e84..a83d2dea6f 100644
--- a/routers/web/repo/setting/variables.go
+++ b/routers/web/repo/setting/variables.go
@@ -7,11 +7,11 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- shared "code.gitea.io/gitea/routers/web/shared/actions"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ shared "forgejo.org/routers/web/shared/actions"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go
index af54997794..0caa196e25 100644
--- a/routers/web/repo/setting/webhook.go
+++ b/routers/web/repo/setting/webhook.go
@@ -11,22 +11,22 @@ import (
"net/url"
"path"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web/middleware"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
- "code.gitea.io/gitea/services/forms"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/web/middleware"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/forms"
+ webhook_service "forgejo.org/services/webhook"
"code.forgejo.org/go-chi/binding"
)
@@ -175,6 +175,9 @@ func ParseHookEvent(form forms.WebhookCoreForm) *webhook_module.HookEvent {
Wiki: form.Wiki,
Repository: form.Repository,
Package: form.Package,
+ ActionRunFailure: form.ActionFailure,
+ ActionRunRecover: form.ActionRecover,
+ ActionRunSuccess: form.ActionSuccess,
},
BranchFilter: form.BranchFilter,
}
diff --git a/routers/web/repo/topic.go b/routers/web/repo/topic.go
index d81a695df9..a028afb042 100644
--- a/routers/web/repo/topic.go
+++ b/routers/web/repo/topic.go
@@ -7,9 +7,9 @@ import (
"net/http"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
// TopicsPost response for creating repository
diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go
index d11af4669f..5c37f2ebca 100644
--- a/routers/web/repo/treelist.go
+++ b/routers/web/repo/treelist.go
@@ -6,9 +6,9 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/services/context"
"github.com/go-enry/go-enry/v2"
)
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index fd8c1da058..bb3e1388a8 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -1,5 +1,6 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
@@ -24,35 +25,36 @@ import (
_ "image/jpeg" // for processing jpeg images
_ "image/png" // for processing png images
- activities_model "code.gitea.io/gitea/models/activities"
- admin_model "code.gitea.io/gitea/models/admin"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issue_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/highlight"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/svg"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/web/feed"
- "code.gitea.io/gitea/services/context"
- issue_service "code.gitea.io/gitea/services/issue"
- files_service "code.gitea.io/gitea/services/repository/files"
+ activities_model "forgejo.org/models/activities"
+ admin_model "forgejo.org/models/admin"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issue_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/highlight"
+ code_indexer "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/svg"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/web/feed"
+ "forgejo.org/services/context"
+ issue_service "forgejo.org/services/issue"
+ repo_service "forgejo.org/services/repository"
+ files_service "forgejo.org/services/repository/files"
"github.com/nektos/act/pkg/model"
@@ -367,9 +369,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
- if !ctx.Repo.CanRead(unit_model.TypeActions) {
- git_model.CommitStatusesHideActionsURL(ctx, statuses)
- }
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["LatestCommitStatuses"] = statuses
@@ -394,6 +393,10 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
ctx.Data["FileName"] = blob.Name()
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ ctx.Data["OpenGraphTitle"] = ctx.Data["Title"]
+ ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s%s", setting.AppURL, ctx.Data["Link"])
+ ctx.Data["OpenGraphNoDescription"] = true
+
if entry.IsLink() {
_, link, err := entry.FollowLinks()
// Errors should be allowed, because this shouldn't
@@ -436,8 +439,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error())
}
} else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) {
- if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil {
- _, warnings := issue_model.GetCodeOwnersFromContent(ctx, data)
+ if rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err == nil {
+ _, warnings := issue_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize)
if len(warnings) > 0 {
ctx.Data["FileWarning"] = strings.Join(warnings, "\n")
}
@@ -621,6 +624,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
ctx.Data["IsVideoFile"] = true
case fInfo.st.IsAudio():
ctx.Data["IsAudioFile"] = true
+ case fInfo.st.Is3DModel():
+ ctx.Data["Is3DModelFile"] = true
+ switch {
+ case fInfo.st.IsGLB():
+ ctx.Data["IsGLBFile"] = true
+ case fInfo.st.IsSTL():
+ ctx.Data["IsSTLFile"] = true
+ case fInfo.st.IsGLTF():
+ ctx.Data["IsGLTFFile"] = true
+ case fInfo.st.IsOBJ():
+ ctx.Data["IsOBJFile"] = true
+ case fInfo.st.Is3MF():
+ ctx.Data["Is3MFFile"] = true
+ }
case fInfo.st.IsImage() && (setting.UI.SVG.Enabled || !fInfo.st.IsSvgImage()):
ctx.Data["IsImageFile"] = true
ctx.Data["CanCopyContent"] = true
@@ -1040,7 +1057,15 @@ func renderHomeCode(ctx *context.Context) {
return
}
- if entry.IsDir() {
+ if entry.IsSubModule() {
+ subModuleURL, err := ctx.Repo.Commit.GetSubModule(entry.Name())
+ if err != nil {
+ HandleGitError(ctx, "Repo.Commit.GetSubModule", err)
+ return
+ }
+ subModuleFile := git.NewSubModuleFile(ctx.Repo.Commit, subModuleURL, entry.ID.String())
+ ctx.Redirect(subModuleFile.RefURL(setting.AppURL, ctx.Repo.Repository.FullName(), setting.SSH.Domain))
+ } else if entry.IsDir() {
renderDirectory(ctx)
} else {
renderFile(ctx, entry)
@@ -1142,6 +1167,20 @@ PostRecentBranchCheck:
}
}
+ if ctx.Repo.Repository.IsFork && ctx.Repo.IsViewBranch && len(ctx.Repo.TreePath) == 0 && ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
+ syncForkInfo, err := repo_service.GetSyncForkInfo(ctx, ctx.Repo.Repository, ctx.Repo.BranchName)
+ if err != nil {
+ ctx.ServerError("CanSync", err)
+ return
+ }
+
+ if syncForkInfo.Allowed {
+ ctx.Data["CanSyncFork"] = true
+ ctx.Data["ForkCommitsBehind"] = syncForkInfo.CommitsBehind
+ ctx.Data["BaseBranchLink"] = fmt.Sprintf("%s/src/branch/%s", ctx.Repo.Repository.BaseRepo.HTMLURL(), util.PathEscapeSegments(ctx.Repo.BranchName))
+ }
+ }
+
ctx.Data["Paths"] = paths
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
@@ -1213,6 +1252,7 @@ func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOp
func Watchers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.watchers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("watch.list.none")
ctx.Data["PageIsWatchers"] = true
RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, func(opts db.ListOptions) ([]*user_model.User, error) {
@@ -1224,6 +1264,7 @@ func Watchers(ctx *context.Context) {
func Stars(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.stargazers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("stars.list.none")
ctx.Data["PageIsStargazers"] = true
RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) {
return repo_model.GetStargazers(ctx, ctx.Repo.Repository, opts)
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 3d55afe294..1b5265978a 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -14,25 +14,25 @@ import (
"path/filepath"
"strings"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- notify_service "code.gitea.io/gitea/services/notify"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ notify_service "forgejo.org/services/notify"
+ wiki_service "forgejo.org/services/wiki"
)
const (
@@ -393,7 +393,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
ctx.ServerError("CommitsByFileAndRange", err)
return nil, nil
}
- ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
+ ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commitsHistory, ctx.Repo.Repository)
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx)
@@ -535,6 +535,9 @@ func Wiki(ctx *context.Context) {
}
ctx.Data["Author"] = lastCommit.Author
+ ctx.Data["OpenGraphTitle"] = ctx.Data["Title"]
+ ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s%s", setting.AppURL, ctx.Data["Link"])
+
ctx.HTML(http.StatusOK, tplWikiView)
}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 0c49e7d902..5709b32257 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -9,14 +9,14 @@ import (
"net/url"
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
- wiki_service "code.gitea.io/gitea/services/wiki"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
+ wiki_service "forgejo.org/services/wiki"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -73,7 +73,7 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas any) {
return
}
for i, pageMeta := range pageMetas {
- assert.EqualValues(t, expectedNames[i], pageMeta.Name)
+ assert.Equal(t, expectedNames[i], pageMeta.Name)
}
}
@@ -84,7 +84,7 @@ func TestWiki(t *testing.T) {
ctx.SetParams("*", "Home")
contexttest.LoadRepo(t, ctx, 1)
Wiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, "Home", ctx.Data["Title"])
assertPagesMetas(t, []string{"Home", "Long Page", "Page With Image", "Page With Spaced Name", "Unescaped File", "XSS"}, ctx.Data["Pages"])
}
@@ -95,7 +95,7 @@ func TestWikiPages(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki/?action=_pages")
contexttest.LoadRepo(t, ctx, 1)
WikiPages(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assertPagesMetas(t, []string{"Home", "Long Page", "Page With Image", "Page With Spaced Name", "Unescaped File", "XSS"}, ctx.Data["Pages"])
}
@@ -106,7 +106,7 @@ func TestNewWiki(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
NewWiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, ctx.Tr("repo.wiki.new_page"), ctx.Data["Title"])
}
@@ -126,7 +126,7 @@ func TestNewWikiPost(t *testing.T) {
Message: message,
})
NewWikiPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)))
}
@@ -144,7 +144,7 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
Message: message,
})
NewWikiPost(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg)
assertWikiNotExists(t, ctx.Repo.Repository, "_edit")
}
@@ -157,7 +157,7 @@ func TestEditWiki(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
EditWiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, "Home", ctx.Data["Title"])
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"])
}
@@ -178,7 +178,7 @@ func TestEditWikiPost(t *testing.T) {
Message: message,
})
EditWikiPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)))
if title != "Home" {
@@ -194,7 +194,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
DeleteWikiPagePost(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assertWikiNotExists(t, ctx.Repo.Repository, "Home")
}
@@ -215,10 +215,10 @@ func TestWikiRaw(t *testing.T) {
contexttest.LoadRepo(t, ctx, 1)
WikiRaw(ctx)
if filetype == "" {
- assert.EqualValues(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath)
+ assert.Equal(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath)
} else {
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath)
- assert.EqualValues(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath)
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath)
+ assert.Equal(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath)
}
}
}
diff --git a/routers/web/shared/actions/fixtures/TestRunnerDetails/action_runner.yml b/routers/web/shared/actions/fixtures/TestRunnerDetails/action_runner.yml
new file mode 100644
index 0000000000..d783f83110
--- /dev/null
+++ b/routers/web/shared/actions/fixtures/TestRunnerDetails/action_runner.yml
@@ -0,0 +1,7 @@
+-
+ id: 1004
+ uuid: "fb857e63-c0ce-4571-a6c9-fde26c128073"
+ name: "Global runner"
+ owner_id: 0
+ repo_id: 0
+ deleted: 0
diff --git a/routers/web/shared/actions/fixtures/TestRunnerDetails/action_task.yml b/routers/web/shared/actions/fixtures/TestRunnerDetails/action_task.yml
new file mode 100644
index 0000000000..63a2d30deb
--- /dev/null
+++ b/routers/web/shared/actions/fixtures/TestRunnerDetails/action_task.yml
@@ -0,0 +1,160 @@
+-
+ id: 1
+ runner_id: 1004
+ token_hash: a1
+-
+ id: 2
+ runner_id: 1004
+ token_hash: a2
+-
+ id: 3
+ runner_id: 1004
+ token_hash: a3
+-
+ id: 4
+ runner_id: 1004
+ token_hash: a4
+-
+ id: 5
+ runner_id: 1004
+ token_hash: a5
+-
+ id: 6
+ runner_id: 1004
+ token_hash: a6
+-
+ id: 7
+ runner_id: 1004
+ token_hash: a7
+-
+ id: 8
+ runner_id: 1004
+ token_hash: a8
+-
+ id: 9
+ runner_id: 1004
+ token_hash: a9
+-
+ id: 10
+ runner_id: 1004
+ token_hash: a10
+-
+ id: 11
+ runner_id: 1004
+ token_hash: a11
+-
+ id: 12
+ runner_id: 1004
+ token_hash: a12
+-
+ id: 13
+ runner_id: 1004
+ token_hash: a13
+-
+ id: 14
+ runner_id: 1004
+ token_hash: a14
+-
+ id: 15
+ runner_id: 1004
+ token_hash: a15
+-
+ id: 16
+ runner_id: 1004
+ token_hash: a16
+-
+ id: 17
+ runner_id: 1004
+ token_hash: a17
+-
+ id: 18
+ runner_id: 1004
+ token_hash: a18
+-
+ id: 19
+ runner_id: 1004
+ token_hash: a19
+-
+ id: 20
+ runner_id: 1004
+ token_hash: a20
+-
+ id: 21
+ runner_id: 1004
+ token_hash: a21
+-
+ id: 22
+ runner_id: 1004
+ token_hash: a22
+-
+ id: 23
+ runner_id: 1004
+ token_hash: a23
+-
+ id: 24
+ runner_id: 1004
+ token_hash: a24
+-
+ id: 25
+ runner_id: 1004
+ token_hash: a25
+-
+ id: 26
+ runner_id: 1004
+ token_hash: a26
+-
+ id: 27
+ runner_id: 1004
+ token_hash: a27
+-
+ id: 28
+ runner_id: 1004
+ token_hash: a28
+-
+ id: 29
+ runner_id: 1004
+ token_hash: a29
+-
+ id: 30
+ runner_id: 1004
+ token_hash: a30
+-
+ id: 31
+ runner_id: 1004
+ token_hash: a31
+-
+ id: 32
+ runner_id: 1004
+ token_hash: a32
+-
+ id: 33
+ runner_id: 1004
+ token_hash: a33
+-
+ id: 34
+ runner_id: 1004
+ token_hash: a34
+-
+ id: 35
+ runner_id: 1004
+ token_hash: a35
+-
+ id: 36
+ runner_id: 1004
+ token_hash: a36
+-
+ id: 37
+ runner_id: 1004
+ token_hash: a37
+-
+ id: 38
+ runner_id: 1004
+ token_hash: a38
+-
+ id: 39
+ runner_id: 1004
+ token_hash: a39
+-
+ id: 40
+ runner_id: 1004
+ token_hash: a40
diff --git a/routers/web/shared/actions/main_test.go b/routers/web/shared/actions/main_test.go
new file mode 100644
index 0000000000..056f48b98d
--- /dev/null
+++ b/routers/web/shared/actions/main_test.go
@@ -0,0 +1,17 @@
+// Copyright 2025 The Forgejo Authors.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "testing"
+
+ "forgejo.org/models/unittest"
+
+ _ "forgejo.org/models"
+ _ "forgejo.org/models/forgefed"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m)
+}
diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go
index 739d2d246c..2ab6b2dadd 100644
--- a/routers/web/shared/actions/runners.go
+++ b/routers/web/shared/actions/runners.go
@@ -6,13 +6,13 @@ package actions
import (
"errors"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// RunnersList prepares data for runners list
@@ -79,7 +79,6 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int
Page: page,
PageSize: 30,
},
- Status: actions_model.StatusUnknown, // Unknown means all
RunnerID: runner.ID,
}
diff --git a/routers/web/shared/actions/runners_test.go b/routers/web/shared/actions/runners_test.go
new file mode 100644
index 0000000000..ad75d34ee6
--- /dev/null
+++ b/routers/web/shared/actions/runners_test.go
@@ -0,0 +1,47 @@
+// Copyright 2025 The Forgejo Authors.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "net/http"
+ "testing"
+
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRunnerDetails(t *testing.T) {
+ defer unittest.OverrideFixtures("routers/web/shared/actions/fixtures/TestRunnerDetails")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: 1004})
+
+ t.Run("permission denied", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "/admin/actions/runners")
+ RunnerDetails(ctx, 1, runner.ID, user.ID, 0)
+ assert.Equal(t, http.StatusNotFound, resp.Code)
+ })
+
+ t.Run("first page", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "/admin/actions/runners")
+ page := 1
+ RunnerDetails(ctx, page, runner.ID, 0, 0)
+ require.Equal(t, http.StatusOK, resp.Code)
+ assert.Len(t, ctx.GetData()["Tasks"], 30)
+ })
+
+ t.Run("second and last page", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "/admin/actions/runners")
+ page := 2
+ RunnerDetails(ctx, page, runner.ID, 0, 0)
+ require.Equal(t, http.StatusOK, resp.Code)
+ assert.Len(t, ctx.GetData()["Tasks"], 10)
+ })
+}
diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go
index 47f1176f46..13dff2f11a 100644
--- a/routers/web/shared/actions/variables.go
+++ b/routers/web/shared/actions/variables.go
@@ -4,13 +4,13 @@
package actions
import (
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/web"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/web"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) {
diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go
index af960f1c0c..1d4fb1588d 100644
--- a/routers/web/shared/packages/packages.go
+++ b/routers/web/shared/packages/packages.go
@@ -9,19 +9,19 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- cargo_service "code.gitea.io/gitea/services/packages/cargo"
- container_service "code.gitea.io/gitea/services/packages/container"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ cargo_service "forgejo.org/services/packages/cargo"
+ container_service "forgejo.org/services/packages/container"
)
func SetPackagesContext(ctx *context.Context, owner *user_model.User) {
diff --git a/routers/web/shared/project/column.go b/routers/web/shared/project/column.go
index 599842ea9e..40bb439452 100644
--- a/routers/web/shared/project/column.go
+++ b/routers/web/shared/project/column.go
@@ -4,9 +4,9 @@
package project
import (
- project_model "code.gitea.io/gitea/models/project"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/services/context"
+ project_model "forgejo.org/models/project"
+ "forgejo.org/modules/json"
+ "forgejo.org/services/context"
)
// MoveColumns moves or keeps columns in a project and sorts them inside that project
diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go
index 3bd421f86a..a853598939 100644
--- a/routers/web/shared/secrets/secrets.go
+++ b/routers/web/shared/secrets/secrets.go
@@ -4,14 +4,14 @@
package secrets
import (
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- secret_service "code.gitea.io/gitea/services/secrets"
+ "forgejo.org/models/db"
+ secret_model "forgejo.org/models/secret"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ secret_service "forgejo.org/services/secrets"
)
func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
diff --git a/routers/web/shared/storage_overview.go b/routers/web/shared/storage_overview.go
new file mode 100644
index 0000000000..fac4aa99e5
--- /dev/null
+++ b/routers/web/shared/storage_overview.go
@@ -0,0 +1,90 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package shared
+
+import (
+ "html/template"
+ "net/http"
+
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+)
+
+// StorageOverview render a size overview of the user, as well as relevant
+// quota limits of the instance.
+func StorageOverview(ctx *context.Context, userID int64, tpl base.TplName) {
+ if !setting.Quota.Enabled {
+ ctx.NotFound("MustEnableQuota", nil)
+ }
+ ctx.Data["Title"] = ctx.Tr("settings.storage_overview")
+ ctx.Data["PageIsStorageOverview"] = true
+
+ ctx.Data["Color"] = func(subject quota_model.LimitSubject) float64 {
+ return float64(subject) * 137.50776405003785 // Golden angle.
+ }
+
+ ctx.Data["PrettySubject"] = func(subject quota_model.LimitSubject) template.HTML {
+ switch subject {
+ case quota_model.LimitSubjectSizeAll:
+ return ctx.Locale.Tr("settings.quota.sizes.all")
+ case quota_model.LimitSubjectSizeReposAll:
+ return ctx.Locale.Tr("settings.quota.sizes.repos.all")
+ case quota_model.LimitSubjectSizeReposPublic:
+ return ctx.Locale.Tr("settings.quota.sizes.repos.public")
+ case quota_model.LimitSubjectSizeReposPrivate:
+ return ctx.Locale.Tr("settings.quota.sizes.repos.private")
+ case quota_model.LimitSubjectSizeGitAll:
+ return ctx.Locale.Tr("settings.quota.sizes.git.all")
+ case quota_model.LimitSubjectSizeGitLFS:
+ return ctx.Locale.Tr("settings.quota.sizes.git.lfs")
+ case quota_model.LimitSubjectSizeAssetsAll:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.all")
+ case quota_model.LimitSubjectSizeAssetsAttachmentsAll:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.attachments.all")
+ case quota_model.LimitSubjectSizeAssetsAttachmentsIssues:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.attachments.issues")
+ case quota_model.LimitSubjectSizeAssetsAttachmentsReleases:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.attachments.releases")
+ case quota_model.LimitSubjectSizeAssetsArtifacts:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.artifacts")
+ case quota_model.LimitSubjectSizeAssetsPackagesAll:
+ return ctx.Locale.Tr("settings.quota.sizes.assets.packages.all")
+ case quota_model.LimitSubjectSizeWiki:
+ return ctx.Locale.Tr("settings.quota.sizes.wiki")
+ default:
+ panic("unrecognized subject: " + subject.String())
+ }
+ }
+
+ sizeUsed, err := quota_model.GetUsedForUser(ctx, userID)
+ if err != nil {
+ ctx.ServerError("GetUsedForUser", err)
+ return
+ }
+ ctx.Data["SizeUsed"] = sizeUsed
+
+ quotaGroups, err := quota_model.GetGroupsForUser(ctx, userID)
+ if err != nil {
+ ctx.ServerError("GetGroupsForUser", err)
+ return
+ }
+ if len(quotaGroups) == 0 {
+ quotaGroups = append(quotaGroups, "a_model.Group{
+ Name: "Global quota",
+ Rules: []quota_model.Rule{
+ {
+ Name: "Default",
+ Limit: setting.Quota.Default.Total,
+ Subjects: quota_model.LimitSubjects{quota_model.LimitSubjectSizeAll},
+ },
+ },
+ },
+ )
+ }
+ ctx.Data["QuotaGroups"] = quotaGroups
+
+ ctx.HTML(http.StatusOK, tpl)
+}
diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go
index fd7605c33b..379e23cce4 100644
--- a/routers/web/shared/user/header.go
+++ b/routers/web/shared/user/header.go
@@ -7,22 +7,22 @@ package user
import (
"net/url"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ project_model "forgejo.org/models/project"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
@@ -38,6 +38,7 @@ func prepareContextForCommonProfile(ctx *context.Context) {
func PrepareContextForProfileBigAvatar(ctx *context.Context) {
prepareContextForCommonProfile(ctx)
+ ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
@@ -66,6 +67,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
UserID: ctx.ContextUser.ID,
+ IncludeLimited: ctx.IsSigned,
IncludePrivate: showPrivate,
})
if err != nil {
diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go
index fc39b504a9..1569600734 100644
--- a/routers/web/swagger_json.go
+++ b/routers/web/swagger_json.go
@@ -4,7 +4,7 @@
package web
import (
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/services/context"
)
// SwaggerV1Json render swagger v1 json
diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go
index 04f510161d..76cc342770 100644
--- a/routers/web/user/avatar.go
+++ b/routers/web/user/avatar.go
@@ -7,10 +7,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/avatars"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/avatars"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/services/context"
)
func cacheableRedirect(ctx *context.Context, location string) {
diff --git a/routers/web/user/code.go b/routers/web/user/code.go
index 3e044d7876..ac1852e410 100644
--- a/routers/web/user/code.go
+++ b/routers/web/user/code.go
@@ -6,13 +6,13 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/base"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/setting"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ code_indexer "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/setting"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
)
const (
@@ -41,19 +41,17 @@ func CodeSearch(ctx *context.Context) {
keyword := ctx.FormTrim("q")
path := ctx.FormTrim("path")
- isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
- if mode := ctx.FormTrim("mode"); len(mode) > 0 {
- isFuzzy = mode == "fuzzy"
+ mode := code_indexer.SearchModeExact
+ if m := ctx.FormTrim("mode"); m == "union" ||
+ m == "fuzzy" ||
+ ctx.FormBool("fuzzy") {
+ mode = code_indexer.SearchModeUnion
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
- ctx.Data["CodeSearchOptions"] = []string{"exact", "fuzzy"}
- if isFuzzy {
- ctx.Data["CodeSearchMode"] = "fuzzy"
- } else {
- ctx.Data["CodeSearchMode"] = "exact"
- }
+ ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions
+ ctx.Data["CodeSearchMode"] = mode.String()
ctx.Data["IsCodePage"] = true
if keyword == "" {
@@ -85,11 +83,11 @@ func CodeSearch(ctx *context.Context) {
if len(repoIDs) > 0 {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: repoIDs,
- Keyword: keyword,
- IsKeywordFuzzy: isFuzzy,
- Language: language,
- Filename: path,
+ RepoIDs: repoIDs,
+ Keyword: keyword,
+ Mode: mode,
+ Language: language,
+ Filename: path,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index c59dcf5c25..d980fa393a 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -10,31 +10,29 @@ import (
"net/http"
"regexp"
"slices"
- "sort"
"strconv"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/web/feed"
- "code.gitea.io/gitea/services/context"
- issue_service "code.gitea.io/gitea/services/issue"
- pull_service "code.gitea.io/gitea/services/pull"
+ activities_model "forgejo.org/models/activities"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/routers/web/feed"
+ "forgejo.org/services/context"
+ issue_service "forgejo.org/services/issue"
+ pull_service "forgejo.org/services/pull"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
@@ -91,6 +89,8 @@ func Dashboard(ctx *context.Context) {
cnt, _ := organization.GetOrganizationCount(ctx, ctxUser)
ctx.Data["UserOrgsCount"] = cnt
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
+ ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage
+ ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
ctx.Data["Date"] = date
var uid int64
@@ -242,7 +242,9 @@ func Milestones(ctx *context.Context) {
ctx.ServerError("SearchRepositoryByCondition", err)
return
}
- sort.Sort(showRepos)
+ slices.SortFunc(showRepos, func(a, b *repo_model.Repository) int {
+ return strings.Compare(a.FullName(), b.FullName())
+ })
for i := 0; i < len(milestones); {
for _, repo := range showRepos {
@@ -462,8 +464,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
User: ctx.Doer,
}
- isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
-
// Search all repositories which
//
// As user:
@@ -593,9 +593,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// USING FINAL STATE OF opts FOR A QUERY.
var issues issues_model.IssueList
{
- issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
- func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
- ))
+ issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
if err != nil {
ctx.ServerError("issueIDsFromSearch", err)
return
@@ -612,18 +610,11 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.ServerError("GetIssuesLastCommitStatus", err)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- for key := range commitStatuses {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
- }
- }
// -------------------------------
// Fill stats to post to ctx.Data.
// -------------------------------
- issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
- func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
- ))
+ issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
if err != nil {
ctx.ServerError("getUserIssueStats", err)
return
@@ -658,9 +649,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
@@ -678,7 +670,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["SelectLabels"] = selectedLabels
ctx.Data["PageIsOrgIssues"] = org != nil
- ctx.Data["IsFuzzy"] = isFuzzy
if isShowClosed {
ctx.Data["State"] = "closed"
@@ -694,7 +685,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
pager.AddParam(ctx, "labels", "SelectLabels")
pager.AddParam(ctx, "milestone", "MilestoneID")
pager.AddParam(ctx, "assignee", "AssigneeID")
- pager.AddParam(ctx, "fuzzy", "IsFuzzy")
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplIssues)
diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go
index e1c8ca9a79..f3a2f12ae6 100644
--- a/routers/web/user/home_test.go
+++ b/routers/web/user/home_test.go
@@ -7,14 +7,14 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/services/context"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -40,15 +40,15 @@ func TestArchivedIssues(t *testing.T) {
NumIssues[repo.ID] = repo.NumIssues
}
assert.False(t, IsArchived[50])
- assert.EqualValues(t, 1, NumIssues[50])
+ assert.Equal(t, 1, NumIssues[50])
assert.True(t, IsArchived[51])
- assert.EqualValues(t, 1, NumIssues[51])
+ assert.Equal(t, 1, NumIssues[51])
// Act
Issues(ctx)
// Assert: One Issue (ID 30) from one Repo (ID 50) is retrieved, while nothing from archived Repo 51 is retrieved
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.Len(t, ctx.Data["Issues"], 1)
}
@@ -61,7 +61,7 @@ func TestIssues(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
ctx.Req.Form.Set("state", "closed")
Issues(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.Len(t, ctx.Data["Issues"], 1)
@@ -76,7 +76,7 @@ func TestPulls(t *testing.T) {
ctx.Req.Form.Set("state", "open")
ctx.Req.Form.Set("type", "your_repositories")
Pulls(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.Len(t, ctx.Data["Issues"], 5)
}
@@ -91,13 +91,15 @@ func TestMilestones(t *testing.T) {
ctx.Req.Form.Set("state", "closed")
ctx.Req.Form.Set("sort", "furthestduedate")
Milestones(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"])
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"])
assert.EqualValues(t, 1, ctx.Data["Total"])
assert.Len(t, ctx.Data["Milestones"], 1)
assert.Len(t, ctx.Data["Repos"], 2) // both repo 42 and 1 have milestones and both are owned by user 2
+ assert.Equal(t, "user2/glob", ctx.Data["Repos"].(repo_model.RepositoryList)[0].FullName())
+ assert.Equal(t, "user2/repo1", ctx.Data["Repos"].(repo_model.RepositoryList)[1].FullName())
}
func TestMilestonesForSpecificRepo(t *testing.T) {
@@ -111,7 +113,7 @@ func TestMilestonesForSpecificRepo(t *testing.T) {
ctx.Req.Form.Set("state", "closed")
ctx.Req.Form.Set("sort", "furthestduedate")
Milestones(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"])
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"])
@@ -142,7 +144,7 @@ func TestOrgLabels(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadOrganization(t, ctx, 3)
Issues(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.Status())
assert.True(t, ctx.Data["PageIsOrgIssues"].(bool))
@@ -161,9 +163,9 @@ func TestOrgLabels(t *testing.T) {
if assert.Len(t, labels, len(orgLabels)) {
for i, label := range labels {
- assert.EqualValues(t, orgLabels[i].OrgID, label.OrgID)
- assert.EqualValues(t, orgLabels[i].ID, label.ID)
- assert.EqualValues(t, orgLabels[i].Name, label.Name)
+ assert.Equal(t, orgLabels[i].OrgID, label.OrgID)
+ assert.Equal(t, orgLabels[i].ID, label.ID)
+ assert.Equal(t, orgLabels[i].Name, label.Name)
}
}
}
diff --git a/routers/web/user/main_test.go b/routers/web/user/main_test.go
index 8b6ae69296..080e3fdcfe 100644
--- a/routers/web/user/main_test.go
+++ b/routers/web/user/main_test.go
@@ -6,7 +6,7 @@ package user
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index c3358dbf62..fdca1a2fdd 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -11,21 +11,19 @@ import (
"net/url"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- issue_service "code.gitea.io/gitea/services/issue"
- pull_service "code.gitea.io/gitea/services/pull"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ issue_service "forgejo.org/services/issue"
+ pull_service "forgejo.org/services/pull"
)
const (
@@ -311,11 +309,6 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.ServerError("GetIssuesAllCommitStatus", err)
return
}
- if !ctx.Repo.CanRead(unit.TypeActions) {
- for key := range commitStatuses {
- git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
- }
- }
ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["Issues"] = issues
@@ -340,9 +333,10 @@ func NotificationSubscriptions(ctx *context.Context) {
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index cb4735da7e..2862c6684b 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -6,30 +6,30 @@ package user
import (
"fmt"
"net/http"
+ "slices"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- alpine_module "code.gitea.io/gitea/modules/packages/alpine"
- arch_model "code.gitea.io/gitea/modules/packages/arch"
- debian_module "code.gitea.io/gitea/modules/packages/debian"
- rpm_module "code.gitea.io/gitea/modules/packages/rpm"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- packages_helper "code.gitea.io/gitea/routers/api/packages/helper"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ container_model "forgejo.org/models/packages/container"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ alpine_module "forgejo.org/modules/packages/alpine"
+ arch_model "forgejo.org/modules/packages/arch"
+ debian_module "forgejo.org/modules/packages/debian"
+ rpm_module "forgejo.org/modules/packages/rpm"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ packages_helper "forgejo.org/routers/api/packages/helper"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ packages_service "forgejo.org/services/packages"
)
const (
@@ -200,9 +200,9 @@ func ViewPackageVersion(ctx *context.Context) {
}
}
- ctx.Data["Branches"] = util.Sorted(branches.Values())
- ctx.Data["Repositories"] = util.Sorted(repositories.Values())
- ctx.Data["Architectures"] = util.Sorted(architectures.Values())
+ ctx.Data["Branches"] = slices.Sorted(branches.Seq())
+ ctx.Data["Repositories"] = slices.Sorted(repositories.Seq())
+ ctx.Data["Architectures"] = slices.Sorted(architectures.Seq())
case packages_model.TypeArch:
ctx.Data["SignMail"] = fmt.Sprintf("%s@noreply.%s", ctx.Package.Owner.Name, setting.Packages.RegistryHost)
groups := make(container.Set[string])
@@ -213,7 +213,7 @@ func ViewPackageVersion(ctx *context.Context) {
}
}
}
- ctx.Data["Groups"] = util.Sorted(groups.Values())
+ ctx.Data["Groups"] = slices.Sorted(groups.Seq())
case packages_model.TypeDebian:
distributions := make(container.Set[string])
components := make(container.Set[string])
@@ -232,10 +232,10 @@ func ViewPackageVersion(ctx *context.Context) {
}
}
- ctx.Data["Distributions"] = util.Sorted(distributions.Values())
- ctx.Data["Components"] = util.Sorted(components.Values())
- ctx.Data["Architectures"] = util.Sorted(architectures.Values())
- case packages_model.TypeRpm:
+ ctx.Data["Distributions"] = slices.Sorted(distributions.Seq())
+ ctx.Data["Components"] = slices.Sorted(components.Seq())
+ ctx.Data["Architectures"] = slices.Sorted(architectures.Seq())
+ case packages_model.TypeRpm, packages_model.TypeAlt:
groups := make(container.Set[string])
architectures := make(container.Set[string])
@@ -250,8 +250,8 @@ func ViewPackageVersion(ctx *context.Context) {
}
}
- ctx.Data["Groups"] = util.Sorted(groups.Values())
- ctx.Data["Architectures"] = util.Sorted(architectures.Values())
+ ctx.Data["Groups"] = slices.Sorted(groups.Seq())
+ ctx.Data["Architectures"] = slices.Sorted(architectures.Seq())
}
var (
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 9cb392d878..78dd6c5e7c 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -1,5 +1,6 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@@ -11,23 +12,23 @@ import (
"path"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/web/feed"
- "code.gitea.io/gitea/routers/web/org"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/routers/web/feed"
+ "forgejo.org/routers/web/org"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
const (
@@ -63,16 +64,11 @@ func userProfile(ctx *context.Context) {
ctx.Data["Title"] = ctx.ContextUser.DisplayName()
ctx.Data["PageIsUserProfile"] = true
- // prepare heatmap data
- if setting.Service.EnableUserHeatmap {
- data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
- if err != nil {
- ctx.ServerError("GetUserHeatmapDataByUser", err)
- return
- }
- ctx.Data["HeatmapData"] = data
- ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
- }
+ ctx.Data["OpenGraphTitle"] = ctx.ContextUser.DisplayName()
+ ctx.Data["OpenGraphType"] = "profile"
+ ctx.Data["OpenGraphImageURL"] = ctx.ContextUser.AvatarLink(ctx)
+ ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL()
+ ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description
profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
@@ -164,11 +160,32 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Cards"] = followers
total = int(numFollowers)
ctx.Data["CardsTitle"] = ctx.TrN(total, "user.followers.title.one", "user.followers.title.few")
+ if ctx.IsSigned && ctx.ContextUser.ID == ctx.Doer.ID {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.incoming.list.self.none")
+ } else {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.incoming.list.none")
+ }
case "following":
ctx.Data["Cards"] = following
total = int(numFollowing)
ctx.Data["CardsTitle"] = ctx.TrN(total, "user.following.title.one", "user.following.title.few")
+ if ctx.IsSigned && ctx.ContextUser.ID == ctx.Doer.ID {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.outgoing.list.self.none")
+ } else {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.outgoing.list.none", ctx.ContextUser.Name)
+ }
case "activity":
+ // prepare heatmap data
+ if setting.Service.EnableUserHeatmap {
+ data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserHeatmapDataByUser", err)
+ return
+ }
+ ctx.Data["HeatmapData"] = data
+ ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
+ }
+
date := ctx.FormString("date")
pagingNum = setting.UI.FeedPagingNum
items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
@@ -247,10 +264,12 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
total = int(count)
case "overview":
- if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
- log.Error("failed to GetBlobContent: %v", err)
+ if rc, _, err := profileReadme.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err != nil {
+ log.Error("failed to NewTruncatedReader: %v", err)
} else {
- if profileContent, err := markdown.RenderString(&markup.RenderContext{
+ defer rc.Close()
+
+ if profileContent, err := markdown.RenderReader(&markup.RenderContext{
Ctx: ctx,
GitRepo: profileGitRepo,
Links: markup.Links{
@@ -263,7 +282,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
},
Metas: map[string]string{"mode": "document"},
- }, bytes); err != nil {
+ }, rc); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
diff --git a/routers/web/user/search.go b/routers/web/user/search.go
index be5eee90a9..411a356d9b 100644
--- a/routers/web/user/search.go
+++ b/routers/web/user/search.go
@@ -6,12 +6,12 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// SearchCandidates searches candidate users for dropdown list
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index 6f40e39c8d..1dfcc90e35 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -9,23 +9,23 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/db"
- "code.gitea.io/gitea/services/auth/source/smtp"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
- "code.gitea.io/gitea/services/user"
+ "forgejo.org/models"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/db"
+ "forgejo.org/services/auth/source/smtp"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
+ "forgejo.org/services/user"
)
const (
@@ -57,7 +57,7 @@ func AccountPost(ctx *context.Context) {
return
}
- if ctx.Doer.IsPasswordSet() && !ctx.Doer.ValidatePassword(form.OldPassword) {
+ if ctx.Doer.IsPasswordSet() && !ctx.Doer.ValidatePassword(ctx, form.OldPassword) {
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
} else if form.Password != form.Retype {
ctx.Flash.Error(ctx.Tr("form.password_not_match"))
@@ -178,10 +178,10 @@ func EmailPost(ctx *context.Context) {
// Set Email Notification Preference
if ctx.FormString("_method") == "NOTIFICATION" {
preference := ctx.FormString("preference")
- if !(preference == user_model.EmailNotificationsEnabled ||
- preference == user_model.EmailNotificationsOnMention ||
- preference == user_model.EmailNotificationsDisabled ||
- preference == user_model.EmailNotificationsAndYourOwn) {
+ if preference != user_model.EmailNotificationsEnabled &&
+ preference != user_model.EmailNotificationsOnMention &&
+ preference != user_model.EmailNotificationsDisabled &&
+ preference != user_model.EmailNotificationsAndYourOwn {
log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
return
@@ -212,7 +212,7 @@ func EmailPost(ctx *context.Context) {
loadAccountData(ctx)
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form)
- } else if validation.IsErrEmailCharIsNotSupported(err) || validation.IsErrEmailInvalid(err) {
+ } else if validation.IsErrEmailInvalid(err) {
loadAccountData(ctx)
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form)
diff --git a/routers/web/user/setting/account_test.go b/routers/web/user/setting/account_test.go
index 9fdc5e4d53..3f7e1c13bc 100644
--- a/routers/web/user/setting/account_test.go
+++ b/routers/web/user/setting/account_test.go
@@ -7,11 +7,11 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/forms"
"github.com/stretchr/testify/assert"
)
@@ -95,7 +95,7 @@ func TestChangePassword(t *testing.T) {
AccountPost(ctx)
assert.Contains(t, ctx.Flash.ErrorMsg, req.Message)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status())
})
}
}
diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go
index 171c1933d4..59ff31162b 100644
--- a/routers/web/user/setting/adopt.go
+++ b/routers/web/user/setting/adopt.go
@@ -6,22 +6,18 @@ package setting
import (
"path/filepath"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/context"
- repo_service "code.gitea.io/gitea/services/repository"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/context"
+ repo_service "forgejo.org/services/repository"
)
// AdoptOrDeleteRepository adopts or deletes a repository
func AdoptOrDeleteRepository(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("settings.adopt")
- ctx.Data["PageIsSettingsRepos"] = true
allowAdopt := ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
- ctx.Data["allowAdopt"] = allowAdopt
allowDelete := ctx.IsUserSiteAdmin() || setting.Repository.AllowDeleteOfUnadoptedRepositories
- ctx.Data["allowDelete"] = allowDelete
dir := ctx.FormString("id")
action := ctx.FormString("action")
diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go
index 24ebf9b922..e73239b79b 100644
--- a/routers/web/user/setting/applications.go
+++ b/routers/web/user/setting/applications.go
@@ -7,13 +7,14 @@ package setting
import (
"net/http"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -48,6 +49,9 @@ func ApplicationsPost(ctx *context.Context) {
ctx.ServerError("GetScope", err)
return
}
+ if !scope.HasPermissionScope() {
+ ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true)
+ }
t := &auth_model.AccessToken{
UID: ctx.Doer.ID,
Name: form.Name,
@@ -87,6 +91,23 @@ func DeleteApplication(ctx *context.Context) {
ctx.JSONRedirect(setting.AppSubURL + "/user/settings/applications")
}
+// RegenerateApplication response for regenerating user access token
+func RegenerateApplication(ctx *context.Context) {
+ if t, err := auth_model.RegenerateAccessTokenByID(ctx, ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
+ if auth_model.IsErrAccessTokenNotExist(err) {
+ ctx.Flash.Error(ctx.Tr("error.not_found"))
+ } else {
+ ctx.Flash.Error(ctx.Tr("error.server_internal"))
+ log.Error("DeleteAccessTokenByID", err)
+ }
+ } else {
+ ctx.Flash.Success(ctx.Tr("settings.regenerate_token_success"))
+ ctx.Flash.Info(t.Token)
+ }
+
+ ctx.JSONRedirect(setting.AppSubURL + "/user/settings/applications")
+}
+
func loadApplicationsData(ctx *context.Context) {
ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly
tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
diff --git a/routers/web/user/setting/blocked_users.go b/routers/web/user/setting/blocked_users.go
index 3f35b2eadf..1448dc9a3c 100644
--- a/routers/web/user/setting/blocked_users.go
+++ b/routers/web/user/setting/blocked_users.go
@@ -6,11 +6,11 @@ package setting
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go
index 9462be71c2..935efd7ba7 100644
--- a/routers/web/user/setting/keys.go
+++ b/routers/web/user/setting/keys.go
@@ -5,18 +5,18 @@
package setting
import (
- "fmt"
+ "errors"
"net/http"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
const (
@@ -80,7 +80,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "gpg":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
@@ -161,7 +161,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -205,7 +205,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "verify_ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -242,7 +242,7 @@ func DeleteKey(ctx *context.Context) {
switch ctx.FormString("type") {
case "gpg":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil {
@@ -252,7 +252,7 @@ func DeleteKey(ctx *context.Context) {
}
case "ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
diff --git a/routers/web/user/setting/main_test.go b/routers/web/user/setting/main_test.go
index e398208d0d..38ac2842dd 100644
--- a/routers/web/user/setting/main_test.go
+++ b/routers/web/user/setting/main_test.go
@@ -6,7 +6,7 @@ package setting
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/routers/web/user/setting/oauth2.go b/routers/web/user/setting/oauth2.go
index 1f485e06c8..64b252e97f 100644
--- a/routers/web/user/setting/oauth2.go
+++ b/routers/web/user/setting/oauth2.go
@@ -4,9 +4,9 @@
package setting
import (
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/user/setting/oauth2_common.go b/routers/web/user/setting/oauth2_common.go
index 2132d127b8..7449e45216 100644
--- a/routers/web/user/setting/oauth2_common.go
+++ b/routers/web/user/setting/oauth2_common.go
@@ -7,13 +7,13 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ shared_user "forgejo.org/routers/web/shared/user"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
type OAuth2CommonHandlers struct {
diff --git a/routers/web/user/setting/packages.go b/routers/web/user/setting/packages.go
index 4132659495..ba739a03fc 100644
--- a/routers/web/user/setting/packages.go
+++ b/routers/web/user/setting/packages.go
@@ -7,13 +7,13 @@ import (
"net/http"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- chef_module "code.gitea.io/gitea/modules/packages/chef"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- shared "code.gitea.io/gitea/routers/web/shared/packages"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ chef_module "forgejo.org/modules/packages/chef"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ shared "forgejo.org/routers/web/shared/packages"
+ "forgejo.org/services/context"
)
const (
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index 907f0f5061..400ee71f08 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -12,26 +12,26 @@ import (
"net/http"
"os"
"path/filepath"
- "slices"
"strings"
+ "time"
- "code.gitea.io/gitea/models/avatars"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/avatars"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/typesniffer"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ user_service "forgejo.org/services/user"
)
const (
@@ -41,8 +41,7 @@ const (
tplSettingsRepositories base.TplName = "user/settings/repos"
)
-// must be kept in sync with `web_src/js/features/user-settings.js`
-var recognisedPronouns = []string{"", "he/him", "she/her", "they/them", "it/its", "any pronouns"}
+var commonPronouns = []string{"he/him", "she/her", "they/them", "it/its", "any pronouns"}
// Profile render user's profile page
func Profile(ctx *context.Context) {
@@ -50,7 +49,11 @@ func Profile(ctx *context.Context) {
ctx.Data["PageIsSettingsProfile"] = true
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
- ctx.Data["PronounsAreCustom"] = !slices.Contains(recognisedPronouns, ctx.Doer.Pronouns)
+ ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
+ ctx.Data["CommonPronouns"] = commonPronouns
+ ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
+ ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
+ ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
ctx.HTML(http.StatusOK, tplSettingsProfile)
}
@@ -61,7 +64,11 @@ func ProfilePost(ctx *context.Context) {
ctx.Data["PageIsSettingsProfile"] = true
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
- ctx.Data["PronounsAreCustom"] = !slices.Contains(recognisedPronouns, ctx.Doer.Pronouns)
+ ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
+ ctx.Data["CommonPronouns"] = commonPronouns
+ ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
+ ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
+ ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplSettingsProfile)
@@ -77,6 +84,8 @@ func ProfilePost(ctx *context.Context) {
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
case user_model.IsErrUserAlreadyExist(err):
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
+ case user_model.IsErrCooldownPeriod(err):
+ ctx.Flash.Error(ctx.Tr("form.username_claiming_cooldown", err.(user_model.ErrCooldownPeriod).ExpireTime.Format(time.RFC1123Z)))
case db.IsErrNameReserved(err):
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", form.Name))
case db.IsErrNamePatternNotAllowed(err):
@@ -101,6 +110,7 @@ func ProfilePost(ctx *context.Context) {
Location: optional.Some(form.Location),
Visibility: optional.Some(form.Visibility),
KeepActivityPrivate: optional.Some(form.KeepActivityPrivate),
+ KeepPronounsPrivate: optional.Some(form.KeepPronounsPrivate),
}
if err := user_service.UpdateUser(ctx, ctx.Doer, opts); err != nil {
ctx.ServerError("UpdateUser", err)
@@ -142,7 +152,7 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *
}
st := typesniffer.DetectContentType(data)
- if !(st.IsImage() && !st.IsSvgImage()) {
+ if !st.IsImage() || st.IsSvgImage() {
return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image"))
}
if err = user_service.UploadAvatar(ctx, ctxUser, data); err != nil {
@@ -325,6 +335,14 @@ func Repos(ctx *context.Context) {
func Appearance(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings.appearance")
ctx.Data["PageIsSettingsAppearance"] = true
+ ctx.Data["AllThemes"] = setting.UI.Themes
+ ctx.Data["ThemeName"] = func(themeName string) string {
+ fullThemeName := "themes.names." + themeName
+ if ctx.Locale.HasKey(fullThemeName) {
+ return ctx.Locale.TrString(fullThemeName)
+ }
+ return themeName
+ }
var hiddenCommentTypes *big.Int
val, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyHiddenCommentTypes)
diff --git a/routers/web/user/setting/runner.go b/routers/web/user/setting/runner.go
index 2bb10cceb9..5c8bba82a1 100644
--- a/routers/web/user/setting/runner.go
+++ b/routers/web/user/setting/runner.go
@@ -4,8 +4,8 @@
package setting
import (
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go
index 37ccb5e5c4..8b362c4f08 100644
--- a/routers/web/user/setting/security/2fa.go
+++ b/routers/web/user/setting/security/2fa.go
@@ -12,13 +12,13 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
@@ -40,11 +40,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
return
}
- token, err := t.GenerateScratchToken()
- if err != nil {
- ctx.ServerError("SettingsTwoFactor: Failed to GenerateScratchToken", err)
- return
- }
+ token := t.GenerateScratchToken()
if err = auth.UpdateTwoFactor(ctx, t); err != nil {
ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err)
@@ -220,11 +216,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
t = &auth.TwoFactor{
UID: ctx.Doer.ID,
}
- token, err := t.GenerateScratchToken()
- if err != nil {
- ctx.ServerError("SettingsTwoFactor: Failed to generate scratch token", err)
- return
- }
+ token := t.GenerateScratchToken()
// Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used
// If we can detect the unique constraint failure below we can move this to after the NewTwoFactor
diff --git a/routers/web/user/setting/security/openid.go b/routers/web/user/setting/security/openid.go
index 8f788e1735..14660e1646 100644
--- a/routers/web/user/setting/security/openid.go
+++ b/routers/web/user/setting/security/openid.go
@@ -6,13 +6,13 @@ package security
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/openid"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/openid"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
)
// OpenIDPost response for change user's openid
diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go
index 8d6859ab87..8b801cfebd 100644
--- a/routers/web/user/setting/security/security.go
+++ b/routers/web/user/setting/security/security.go
@@ -8,14 +8,14 @@ import (
"net/http"
"sort"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/context"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/context"
)
const (
@@ -55,7 +55,7 @@ func DeleteAccountLink(ctx *context.Context) {
}
func loadSecurityData(ctx *context.Context) {
- enrolled, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ enrolled, err := auth_model.HasTOTPByUID(ctx, ctx.Doer.ID)
if err != nil {
ctx.ServerError("SettingsTwoFactor", err)
return
diff --git a/routers/web/user/setting/security/webauthn.go b/routers/web/user/setting/security/webauthn.go
index bfbc06c701..a909d479c9 100644
--- a/routers/web/user/setting/security/webauthn.go
+++ b/routers/web/user/setting/security/webauthn.go
@@ -9,14 +9,14 @@ import (
"strconv"
"time"
- "code.gitea.io/gitea/models/auth"
- wa "code.gitea.io/gitea/modules/auth/webauthn"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
+ "forgejo.org/models/auth"
+ wa "forgejo.org/modules/auth/webauthn"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/mailer"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
diff --git a/routers/web/user/setting/storage_overview.go b/routers/web/user/setting/storage_overview.go
new file mode 100644
index 0000000000..4586600572
--- /dev/null
+++ b/routers/web/user/setting/storage_overview.go
@@ -0,0 +1,20 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package setting
+
+import (
+ "forgejo.org/modules/base"
+ "forgejo.org/routers/web/shared"
+ "forgejo.org/services/context"
+)
+
+const (
+ tplSettingsStorageOverview base.TplName = "user/settings/storage_overview"
+)
+
+// StorageOverview render a size overview of the user, as well as relevant
+// quota limits of the instance.
+func StorageOverview(ctx *context.Context) {
+ shared.StorageOverview(ctx, ctx.Doer.ID, tplSettingsStorageOverview)
+}
diff --git a/routers/web/user/setting/webhooks.go b/routers/web/user/setting/webhooks.go
index 3cc67d9def..bc07accad4 100644
--- a/routers/web/user/setting/webhooks.go
+++ b/routers/web/user/setting/webhooks.go
@@ -6,12 +6,12 @@ package setting
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
- webhook_service "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/db"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
+ webhook_service "forgejo.org/services/webhook"
)
const (
diff --git a/routers/web/user/stop_watch.go b/routers/web/user/stop_watch.go
index 38f74ea455..210b32d205 100644
--- a/routers/web/user/stop_watch.go
+++ b/routers/web/user/stop_watch.go
@@ -6,10 +6,10 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
// GetStopwatches get all stopwatches
diff --git a/routers/web/user/task.go b/routers/web/user/task.go
index 8476767e9e..296c44f809 100644
--- a/routers/web/user/task.go
+++ b/routers/web/user/task.go
@@ -7,9 +7,9 @@ import (
"net/http"
"strconv"
- admin_model "code.gitea.io/gitea/models/admin"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/services/context"
+ admin_model "forgejo.org/models/admin"
+ "forgejo.org/modules/json"
+ "forgejo.org/services/context"
)
// TaskStatus returns task's status
diff --git a/routers/web/web.go b/routers/web/web.go
index 4d8d280c89..4b39f22f7d 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1,4 +1,5 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package web
@@ -8,48 +9,47 @@ import (
"net/http"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/metrics"
- "code.gitea.io/gitea/modules/public"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/modules/web/routing"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/routers/web/admin"
- "code.gitea.io/gitea/routers/web/auth"
- "code.gitea.io/gitea/routers/web/devtest"
- "code.gitea.io/gitea/routers/web/events"
- "code.gitea.io/gitea/routers/web/explore"
- "code.gitea.io/gitea/routers/web/feed"
- "code.gitea.io/gitea/routers/web/healthcheck"
- "code.gitea.io/gitea/routers/web/misc"
- "code.gitea.io/gitea/routers/web/org"
- org_setting "code.gitea.io/gitea/routers/web/org/setting"
- "code.gitea.io/gitea/routers/web/repo"
- "code.gitea.io/gitea/routers/web/repo/actions"
- "code.gitea.io/gitea/routers/web/repo/badges"
- repo_flags "code.gitea.io/gitea/routers/web/repo/flags"
- repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
- "code.gitea.io/gitea/routers/web/shared/project"
- "code.gitea.io/gitea/routers/web/user"
- user_setting "code.gitea.io/gitea/routers/web/user/setting"
- "code.gitea.io/gitea/routers/web/user/setting/security"
- auth_service "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/lfs"
+ "forgejo.org/models/perm"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/metrics"
+ "forgejo.org/modules/public"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/modules/web/routing"
+ "forgejo.org/routers/common"
+ "forgejo.org/routers/web/admin"
+ "forgejo.org/routers/web/auth"
+ "forgejo.org/routers/web/devtest"
+ "forgejo.org/routers/web/events"
+ "forgejo.org/routers/web/explore"
+ "forgejo.org/routers/web/feed"
+ "forgejo.org/routers/web/healthcheck"
+ "forgejo.org/routers/web/misc"
+ "forgejo.org/routers/web/moderation"
+ "forgejo.org/routers/web/org"
+ org_setting "forgejo.org/routers/web/org/setting"
+ "forgejo.org/routers/web/repo"
+ "forgejo.org/routers/web/repo/actions"
+ "forgejo.org/routers/web/repo/badges"
+ repo_flags "forgejo.org/routers/web/repo/flags"
+ repo_setting "forgejo.org/routers/web/repo/setting"
+ "forgejo.org/routers/web/shared/project"
+ "forgejo.org/routers/web/user"
+ user_setting "forgejo.org/routers/web/user/setting"
+ "forgejo.org/routers/web/user/setting/security"
+ auth_service "forgejo.org/services/auth"
+ "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/lfs"
- _ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
+ _ "forgejo.org/modules/session" // to registers all internal adapters
"code.forgejo.org/go-chi/captcha"
chi_middleware "github.com/go-chi/chi/v5/middleware"
@@ -110,10 +110,6 @@ func buildAuthGroup() *auth_service.Group {
}
group.Add(&auth_service.Session{})
- if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) {
- group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI
- }
-
return group
}
@@ -479,6 +475,11 @@ func registerRoutes(m *web.Route) {
m.Get("/search", repo.SearchIssues)
}, reqSignIn)
+ if setting.Moderation.Enabled {
+ m.Get("/report_abuse", reqSignIn, moderation.NewReport)
+ m.Post("/report_abuse", reqSignIn, web.Bind(forms.ReportAbuseForm{}), moderation.CreatePost)
+ }
+
m.Get("/pulls", reqSignIn, user.Pulls)
m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones)
@@ -592,6 +593,7 @@ func registerRoutes(m *web.Route) {
m.Combo("").Get(user_setting.Applications).
Post(web.Bind(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost)
m.Post("/delete", user_setting.DeleteApplication)
+ m.Post("/regenerate", user_setting.RegenerateApplication)
})
m.Combo("/keys").Get(user_setting.Keys).
@@ -644,7 +646,8 @@ func registerRoutes(m *web.Route) {
m.Get("", user_setting.BlockedUsers)
m.Post("/unblock", user_setting.UnblockUser)
})
- }, reqSignIn, ctxDataSet("PageIsUserSettings", true, "AllThemes", setting.UI.Themes, "EnablePackages", setting.Packages.Enabled))
+ m.Get("/storage_overview", user_setting.StorageOverview)
+ }, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled, "EnableQuota", setting.Quota.Enabled))
m.Group("/user", func() {
m.Get("/activate", auth.Activate)
@@ -815,13 +818,13 @@ func registerRoutes(m *web.Route) {
individualPermsChecker := func(ctx *context.Context) {
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
if ctx.ContextUser.IsIndividual() {
- switch {
- case ctx.ContextUser.Visibility == structs.VisibleTypePrivate:
+ switch ctx.ContextUser.Visibility {
+ case structs.VisibleTypePrivate:
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
ctx.NotFound("Visit Project", nil)
return
}
- case ctx.ContextUser.Visibility == structs.VisibleTypeLimited:
+ case structs.VisibleTypeLimited:
if ctx.Doer == nil {
ctx.NotFound("Visit Project", nil)
return
@@ -930,6 +933,7 @@ func registerRoutes(m *web.Route) {
m.Post("/block", org_setting.BlockedUsersBlock)
m.Post("/unblock", org_setting.BlockedUsersUnblock)
})
+ m.Get("/storage_overview", org_setting.StorageOverview)
m.Group("/packages", func() {
m.Get("", org.Packages)
@@ -949,7 +953,7 @@ func registerRoutes(m *web.Route) {
m.Post("/rebuild", org.RebuildCargoIndex)
})
}, packagesEnabled)
- }, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
+ }, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "EnableQuota", setting.Quota.Enabled, "PageIsOrgSettings", true))
}, context.OrgAssignment(true, true))
}, reqSignIn)
// ***** END: Organization *****
@@ -1395,7 +1399,7 @@ func registerRoutes(m *web.Route) {
m.Get("", actions.List)
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
- m.Post("/manual", reqRepoAdmin, actions.ManualRunWorkflow)
+ m.Post("/manual", reqRepoActionsWriter, actions.ManualRunWorkflow)
m.Group("/runs", func() {
m.Get("/latest", actions.ViewLatest)
@@ -1457,7 +1461,7 @@ func registerRoutes(m *web.Route) {
}, repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode))
m.Group("/recent-commits", func() {
m.Get("", repo.RecentCommits)
- m.Get("/data", repo.RecentCommitsData)
+ m.Get("/data", repo.CodeFrequencyData)
}, repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode))
}, context.RepoRef(), context.RequireRepoReaderOr(unit.TypeCode, unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases))
@@ -1506,7 +1510,10 @@ func registerRoutes(m *web.Route) {
m.Group("/commits", func() {
m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
m.Get("/list", context.RepoRef(), repo.GetPullCommits)
- m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
+ m.Group("/{sha:[a-f0-9]{4,40}}", func() {
+ m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
+ m.Post("/reviews/submit", context.RepoMustNotBeArchived(), web.Bind(forms.SubmitReviewForm{}), repo.SubmitReview)
+ })
})
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), context.EnforceQuotaWeb(quota_model.LimitSubjectSizeGitAll, context.QuotaTargetRepo), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
@@ -1594,6 +1601,8 @@ func registerRoutes(m *web.Route) {
}, context.RepoRef(), reqRepoCodeReader)
}
m.Get("/commit/{sha:([a-f0-9]{4,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
+
+ m.Post("/sync_fork", context.RepoMustNotBeArchived(), repo.MustBeNotEmpty, reqRepoCodeWriter, repo.SyncFork)
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)
@@ -1664,6 +1673,7 @@ func registerRoutes(m *web.Route) {
m.Any("/devtest", devtest.List)
m.Any("/devtest/fetch-action-test", devtest.FetchActionTest)
m.Any("/devtest/{sub}", devtest.Tmpl)
+ m.Get("/devtest/error/{errcode}", devtest.ErrorPage)
}
m.NotFound(func(w http.ResponseWriter, req *http.Request) {
diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go
index 1f3de70db0..be3c2925fe 100644
--- a/routers/web/webfinger.go
+++ b/routers/web/webfinger.go
@@ -9,10 +9,10 @@ import (
"net/url"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4
diff --git a/services/actions/auth.go b/services/actions/auth.go
index 1ef21f6e0e..98b618aeba 100644
--- a/services/actions/auth.go
+++ b/services/actions/auth.go
@@ -4,14 +4,15 @@
package actions
import (
+ "errors"
"fmt"
"net/http"
"strings"
"time"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
"github.com/golang-jwt/jwt/v5"
)
@@ -80,7 +81,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) {
parts := strings.SplitN(h, " ", 2)
if len(parts) != 2 {
log.Error("split token failed: %s", h)
- return 0, fmt.Errorf("split token failed")
+ return 0, errors.New("split token failed")
}
return TokenToTaskID(parts[1])
@@ -100,7 +101,7 @@ func TokenToTaskID(token string) (int64, error) {
c, ok := parsedToken.Claims.(*actionsClaims)
if !parsedToken.Valid || !ok {
- return 0, fmt.Errorf("invalid token claim")
+ return 0, errors.New("invalid token claim")
}
return c.TaskID, nil
diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go
index 1400e61f47..d9f0437e1b 100644
--- a/services/actions/auth_test.go
+++ b/services/actions/auth_test.go
@@ -7,8 +7,8 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
@@ -19,7 +19,7 @@ func TestCreateAuthorizationToken(t *testing.T) {
var taskID int64 = 23
token, err := CreateAuthorizationToken(taskID, 1, 2)
require.NoError(t, err)
- assert.NotEqual(t, "", token)
+ assert.NotEmpty(t, token)
claims := jwt.MapClaims{}
_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
return setting.GetGeneralTokenSigningSecret(), nil
@@ -45,7 +45,7 @@ func TestParseAuthorizationToken(t *testing.T) {
var taskID int64 = 23
token, err := CreateAuthorizationToken(taskID, 1, 2)
require.NoError(t, err)
- assert.NotEqual(t, "", token)
+ assert.NotEmpty(t, token)
headers := http.Header{}
headers.Set("Authorization", "Bearer "+token)
rTaskID, err := ParseAuthorizationToken(&http.Request{
diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go
index 34fa2688e7..918be0f185 100644
--- a/services/actions/cleanup.go
+++ b/services/actions/cleanup.go
@@ -10,12 +10,12 @@ import (
"os"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/timeutil"
+ actions_model "forgejo.org/models/actions"
+ actions_module "forgejo.org/modules/actions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/timeutil"
)
// Cleanup removes expired actions logs, data and artifacts
@@ -126,3 +126,9 @@ func CleanupLogs(ctx context.Context) error {
log.Info("Removed %d logs", count)
return nil
}
+
+// CleanupOfflineRunners removes offline runners
+func CleanupOfflineRunners(ctx context.Context, duration time.Duration, globalOnly bool) error {
+ olderThan := timeutil.TimeStampNow().AddDuration(-duration)
+ return actions_model.DeleteOfflineRunners(ctx, olderThan, globalOnly)
+}
diff --git a/services/actions/cleanup_test.go b/services/actions/cleanup_test.go
index 65fae840c1..4a847ced23 100644
--- a/services/actions/cleanup_test.go
+++ b/services/actions/cleanup_test.go
@@ -6,10 +6,10 @@ package actions
import (
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/timeutil"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -24,7 +24,7 @@ func TestCleanup(t *testing.T) {
require.NoError(t, CleanupLogs(db.DefaultContext))
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 1001})
- assert.EqualValues(t, "does-not-exist", task.LogFilename)
+ assert.Equal(t, "does-not-exist", task.LogFilename)
assert.True(t, task.LogExpired)
assert.Nil(t, task.LogIndexes)
})
diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go
index 67373782d5..c36dda55b2 100644
--- a/services/actions/clear_tasks.go
+++ b/services/actions/clear_tasks.go
@@ -8,18 +8,18 @@ import (
"fmt"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
)
// StopZombieTasks stops the task which have running status, but haven't been updated for a long time
func StopZombieTasks(ctx context.Context) error {
return stopTasks(ctx, actions_model.FindTaskOptions{
- Status: actions_model.StatusRunning,
+ Status: []actions_model.Status{actions_model.StatusRunning},
UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.ZombieTaskTimeout).Unix()),
})
}
@@ -27,7 +27,7 @@ func StopZombieTasks(ctx context.Context) error {
// StopEndlessTasks stops the tasks which have running status and continuous updates, but don't end for a long time
func StopEndlessTasks(ctx context.Context) error {
return stopTasks(ctx, actions_model.FindTaskOptions{
- Status: actions_model.StatusRunning,
+ Status: []actions_model.Status{actions_model.StatusRunning},
StartedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.EndlessTaskTimeout).Unix()),
})
}
@@ -41,7 +41,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
jobs := make([]*actions_model.ActionRunJob, 0, len(tasks))
for _, task := range tasks {
if err := db.WithTx(ctx, func(ctx context.Context) error {
- if err := actions_model.StopTask(ctx, task.ID, actions_model.StatusFailure); err != nil {
+ if err := StopTask(ctx, task.ID, actions_model.StatusFailure); err != nil {
return err
}
if err := task.LoadJob(ctx); err != nil {
@@ -88,7 +88,7 @@ func CancelAbandonedJobs(ctx context.Context) error {
job.Status = actions_model.StatusCancelled
job.Stopped = now
if err := db.WithTx(ctx, func(ctx context.Context) error {
- _, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped")
+ _, err := UpdateRunJob(ctx, job, nil, "status", "stopped")
return err
}); err != nil {
log.Warn("cancel abandoned job %v: %v", job.ID, err)
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index 04dffbac88..755fa648dc 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -5,18 +5,19 @@ package actions
import (
"context"
+ "errors"
"fmt"
"path"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- user_model "code.gitea.io/gitea/models/user"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ user_model "forgejo.org/models/user"
+ actions_module "forgejo.org/modules/actions"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ commitstatus_service "forgejo.org/services/repository/commitstatus"
"github.com/nektos/act/pkg/jobparser"
)
@@ -50,7 +51,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("GetPushEventPayload: %w", err)
}
if payload.HeadCommit == nil {
- return fmt.Errorf("head commit is missing in event payload")
+ return errors.New("head commit is missing in event payload")
}
sha = payload.HeadCommit.ID
case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestLabel, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestMilestone:
@@ -64,9 +65,9 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("GetPullRequestEventPayload: %w", err)
}
if payload.PullRequest == nil {
- return fmt.Errorf("pull request is missing in event payload")
+ return errors.New("pull request is missing in event payload")
} else if payload.PullRequest.Head == nil {
- return fmt.Errorf("head of pull request is missing in event payload")
+ return errors.New("head of pull request is missing in event payload")
}
sha = payload.PullRequest.Head.Sha
case webhook_module.HookEventRelease:
diff --git a/services/actions/context.go b/services/actions/context.go
new file mode 100644
index 0000000000..bf187c56bf
--- /dev/null
+++ b/services/actions/context.go
@@ -0,0 +1,161 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "context"
+ "fmt"
+
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ actions_module "forgejo.org/modules/actions"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+)
+
+// GenerateGiteaContext generate the gitea context without token and gitea_runtime_token
+// job can be nil when generating a context for parsing workflow-level expressions
+func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) map[string]any {
+ event := map[string]any{}
+ _ = json.Unmarshal([]byte(run.EventPayload), &event)
+
+ baseRef := ""
+ headRef := ""
+ ref := run.Ref
+ sha := run.CommitSHA
+ if pullPayload, err := run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil {
+ baseRef = pullPayload.PullRequest.Base.Ref
+ headRef = pullPayload.PullRequest.Head.Ref
+
+ // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request
+ // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target,
+ // the ref will be the base branch.
+ if run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
+ ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name
+ sha = pullPayload.PullRequest.Base.Sha
+ }
+ }
+
+ refName := git.RefName(ref)
+
+ gitContext := map[string]any{
+ // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
+ "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2.
+ "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action.
+ "action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2.
+ "action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout.
+ "action_status": "", // string, For a composite action, the current result of the composite action.
+ "actor": run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
+ "api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API.
+ "base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
+ "env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
+ "event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload.
+ "event_name": run.TriggerEvent, // string, The name of the event that triggered the workflow run.
+ "event_path": "", // string, The path to the file on the runner that contains the full event webhook payload.
+ "graphql_url": "", // string, The URL of the GitHub GraphQL API.
+ "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
+ "job": "", // string, The job_id of the current job.
+ "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/, for pull requests it is refs/pull//merge, and for tags it is refs/tags/. For example, refs/heads/feature-branch-1.
+ "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
+ "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
+ "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
+ "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
+ "repository": run.Repo.OwnerName + "/" + run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
+ "repository_owner": run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
+ "repositoryUrl": run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git.
+ "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept.
+ "run_id": "", // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run.
+ "run_number": fmt.Sprint(run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
+ "run_attempt": "", // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.
+ "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces.
+ "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com.
+ "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53.
+ "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
+ "workflow": run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
+ "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action.
+
+ // additional contexts
+ "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(),
+ }
+
+ if job != nil {
+ gitContext["job"] = job.JobID
+ gitContext["run_id"] = fmt.Sprint(job.RunID)
+ gitContext["run_attempt"] = fmt.Sprint(job.Attempt)
+ }
+
+ return gitContext
+}
+
+type TaskNeed struct {
+ Result actions_model.Status
+ Outputs map[string]string
+}
+
+// FindTaskNeeds finds the `needs` for the task by the task's job
+func FindTaskNeeds(ctx context.Context, job *actions_model.ActionRunJob) (map[string]*TaskNeed, error) {
+ if len(job.Needs) == 0 {
+ return nil, nil
+ }
+ needs := container.SetOf(job.Needs...)
+
+ jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: job.RunID})
+ if err != nil {
+ return nil, fmt.Errorf("FindRunJobs: %w", err)
+ }
+
+ jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
+ for _, job := range jobs {
+ jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
+ }
+
+ ret := make(map[string]*TaskNeed, len(needs))
+ for jobID, jobsWithSameID := range jobIDJobs {
+ if !needs.Contains(jobID) {
+ continue
+ }
+ var jobOutputs map[string]string
+ for _, job := range jobsWithSameID {
+ if job.TaskID == 0 || !job.Status.IsDone() {
+ // it shouldn't happen, or the job has been rerun
+ continue
+ }
+ got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
+ if err != nil {
+ return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
+ }
+ outputs := make(map[string]string, len(got))
+ for _, v := range got {
+ outputs[v.OutputKey] = v.OutputValue
+ }
+ if len(jobOutputs) == 0 {
+ jobOutputs = outputs
+ } else {
+ jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
+ }
+ }
+ ret[jobID] = &TaskNeed{
+ Outputs: jobOutputs,
+ Result: actions_model.AggregateJobStatus(jobsWithSameID),
+ }
+ }
+ return ret, nil
+}
+
+// mergeTwoOutputs merges two outputs from two different ActionRunJobs
+// Values with the same output name may be overridden. The user should ensure the output names are unique.
+// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
+func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
+ ret := make(map[string]string, len(o1))
+ for k1, v1 := range o1 {
+ if len(v1) > 0 {
+ ret[k1] = v1
+ } else {
+ ret[k1] = o2[k1]
+ }
+ }
+ return ret
+}
diff --git a/routers/api/actions/runner/utils_test.go b/services/actions/context_test.go
similarity index 68%
rename from routers/api/actions/runner/utils_test.go
rename to services/actions/context_test.go
index c8a0a28d65..c96094ade8 100644
--- a/routers/api/actions/runner/utils_test.go
+++ b/services/actions/context_test.go
@@ -1,25 +1,25 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package runner
+package actions
import (
- "context"
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/unittest"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func Test_findTaskNeeds(t *testing.T) {
+func TestFindTaskNeeds(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51})
+ job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: task.JobID})
- ret, err := findTaskNeeds(context.Background(), task)
+ ret, err := FindTaskNeeds(t.Context(), job)
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Contains(t, ret, "job1")
diff --git a/services/actions/init.go b/services/actions/init.go
index 0f49cb6297..8f1db64e27 100644
--- a/services/actions/init.go
+++ b/services/actions/init.go
@@ -4,11 +4,11 @@
package actions
import (
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ notify_service "forgejo.org/services/notify"
)
func Init() {
diff --git a/services/actions/interface.go b/services/actions/interface.go
index d4fa782fec..54a30061bc 100644
--- a/services/actions/interface.go
+++ b/services/actions/interface.go
@@ -3,7 +3,7 @@
package actions
-import "code.gitea.io/gitea/services/context"
+import "forgejo.org/services/context"
// API for actions of a repository or organization
type API interface {
@@ -25,4 +25,6 @@ type API interface {
UpdateVariable(*context.APIContext)
// GetRegistrationToken get registration token
GetRegistrationToken(*context.APIContext)
+ // SearchActionRunJobs get pending Action run jobs
+ SearchActionRunJobs(*context.APIContext)
}
diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go
index 1f859fcf70..942c698e73 100644
--- a/services/actions/job_emitter.go
+++ b/services/actions/job_emitter.go
@@ -8,10 +8,10 @@ import (
"errors"
"fmt"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/queue"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/queue"
"github.com/nektos/act/pkg/jobparser"
"xorm.io/builder"
@@ -59,7 +59,7 @@ func checkJobsOfRun(ctx context.Context, runID int64) error {
for _, job := range jobs {
if status, ok := updates[job.ID]; ok {
job.Status = status
- if n, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": actions_model.StatusBlocked}, "status"); err != nil {
+ if n, err := UpdateRunJob(ctx, job, builder.Eq{"status": actions_model.StatusBlocked}, "status"); err != nil {
return err
} else if n != 1 {
return fmt.Errorf("no affected for updating blocked job %v", job.ID)
diff --git a/services/actions/job_emitter_test.go b/services/actions/job_emitter_test.go
index 58c2dc3b24..a3e0e95d04 100644
--- a/services/actions/job_emitter_test.go
+++ b/services/actions/job_emitter_test.go
@@ -6,7 +6,7 @@ package actions
import (
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
+ actions_model "forgejo.org/models/actions"
"github.com/stretchr/testify/assert"
)
diff --git a/services/actions/main_test.go b/services/actions/main_test.go
index 49629ecb03..71ec1d3426 100644
--- a/services/actions/main_test.go
+++ b/services/actions/main_test.go
@@ -6,11 +6,11 @@ package actions
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/activities"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/activities"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/actions/notifier.go b/services/actions/notifier.go
index 2dd81158a7..f1e9a6d7e9 100644
--- a/services/actions/notifier.go
+++ b/services/actions/notifier.go
@@ -5,21 +5,26 @@ package actions
import (
"context"
+ "errors"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- perm_model "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/convert"
- notify_service "code.gitea.io/gitea/services/notify"
+ actions_model "forgejo.org/models/actions"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ perm_model "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/convert"
+ notify_service "forgejo.org/services/notify"
+
+ "xorm.io/builder"
)
type actionsNotifier struct {
@@ -775,3 +780,76 @@ func (n *actionsNotifier) MigrateRepository(ctx context.Context, doer, u *user_m
Sender: convert.ToUser(ctx, doer, nil),
}).Notify(ctx)
}
+
+// Call this sendActionRunNowDoneNotificationIfNeeded when there has been an update for an ActionRun.
+// priorRun and updatedRun represent the very same ActionRun, just at different times:
+// priorRun before the update and updatedRun after.
+// The parameter lastRun in the ActionRunNowDone notification represents an entirely different ActionRun:
+// the ActionRun of the same workflow that finished before priorRun/updatedRun.
+func sendActionRunNowDoneNotificationIfNeeded(ctx context.Context, priorRun, updatedRun *actions_model.ActionRun) error {
+ if !priorRun.Status.IsDone() && updatedRun.Status.IsDone() {
+ lastRun, err := actions_model.GetRunBefore(ctx, updatedRun.RepoID, updatedRun.Stopped)
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
+ return err
+ }
+ // when no last run was found lastRun is nil
+ if lastRun != nil {
+ if err = lastRun.LoadAttributes(ctx); err != nil {
+ return err
+ }
+ }
+ if err = updatedRun.LoadAttributes(ctx); err != nil {
+ return err
+ }
+ notify_service.ActionRunNowDone(ctx, updatedRun, priorRun.Status, lastRun)
+ }
+ return nil
+}
+
+// wrapper of UpdateRunWithoutNotification with a call to the ActionRunNowDone notification channel
+func UpdateRun(ctx context.Context, run *actions_model.ActionRun, cols ...string) error {
+ // run.ID is the only thing that must be given
+ priorRun, err := actions_model.GetRunByID(ctx, run.ID)
+ if err != nil {
+ return err
+ }
+
+ if err = actions_model.UpdateRunWithoutNotification(ctx, run, cols...); err != nil {
+ return err
+ }
+
+ updatedRun, err := actions_model.GetRunByID(ctx, run.ID)
+ if err != nil {
+ return err
+ }
+ return sendActionRunNowDoneNotificationIfNeeded(ctx, priorRun, updatedRun)
+}
+
+// wrapper of UpdateRunJobWithoutNotification with a call to the ActionRunNowDone notification channel
+func UpdateRunJob(ctx context.Context, job *actions_model.ActionRunJob, cond builder.Cond, cols ...string) (int64, error) {
+ runID := job.RunID
+ if runID == 0 {
+ // job.ID is the only thing that must be given
+ // Don't overwrite job here, we'd loose the change we need to make.
+ oldJob, err := actions_model.GetRunJobByID(ctx, job.ID)
+ if err != nil {
+ return 0, err
+ }
+ runID = oldJob.RunID
+ }
+ priorRun, err := actions_model.GetRunByID(ctx, runID)
+ if err != nil {
+ return 0, err
+ }
+
+ affected, err := actions_model.UpdateRunJobWithoutNotification(ctx, job, cond, cols...)
+ if err != nil {
+ return affected, err
+ }
+
+ updatedRun, err := actions_model.GetRunByID(ctx, runID)
+ if err != nil {
+ return affected, err
+ }
+ return affected, sendActionRunNowDoneNotificationIfNeeded(ctx, priorRun, updatedRun)
+}
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 0a1dbb162d..e240c996b5 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -11,24 +11,24 @@ import (
"slices"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "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"
- "code.gitea.io/gitea/services/convert"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ actions_module "forgejo.org/modules/actions"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/convert"
"github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
@@ -139,7 +139,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return nil
}
if unit_model.TypeActions.UnitGlobalDisabled() {
- if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo, true); err != nil {
+ if err := CleanRepoScheduleTasks(ctx, input.Repo, true); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
return nil
@@ -345,6 +345,14 @@ func handleWorkflows(
Status: actions_model.StatusWaiting,
}
+ if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)); err == nil {
+ notifications, err := workflow.Notifications()
+ if err != nil {
+ log.Error("Notifications: %w", err)
+ }
+ run.NotifyEmail = notifications
+ }
+
need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer)
if err != nil {
log.Error("check if need approval for repo %d with user %d: %v", input.Repo.ID, input.Doer.ID, err)
@@ -366,14 +374,17 @@ func handleWorkflows(
jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars))
if err != nil {
- log.Error("jobparser.Parse: %v", err)
- continue
+ run.Status = actions_model.StatusFailure
+ log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err)
+ jobs = []*jobparser.SingleWorkflow{{
+ Name: dwf.EntryName,
+ }}
}
// cancel running jobs if the event is push or pull_request_sync
if run.Event == webhook_module.HookEventPush ||
run.Event == webhook_module.HookEventPullRequestSync {
- if err := actions_model.CancelPreviousJobs(
+ if err := CancelPreviousJobs(
ctx,
run.RepoID,
run.Ref,
@@ -504,7 +515,7 @@ func handleSchedules(
log.Error("CountSchedules: %v", err)
return err
} else if count > 0 {
- if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo, false); err != nil {
+ if err := CleanRepoScheduleTasks(ctx, input.Repo, false); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
}
diff --git a/services/actions/notifier_helper_test.go b/services/actions/notifier_helper_test.go
index 0fa40c0168..9166dc3b95 100644
--- a/services/actions/notifier_helper_test.go
+++ b/services/actions/notifier_helper_test.go
@@ -6,10 +6,10 @@ package actions
import (
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/actions/rerun.go b/services/actions/rerun.go
index 60f6650905..f6dd4af5c7 100644
--- a/services/actions/rerun.go
+++ b/services/actions/rerun.go
@@ -4,8 +4,8 @@
package actions
import (
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/modules/container"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/modules/container"
)
// GetAllRerunJobs get all jobs that need to be rerun when job should be rerun
diff --git a/services/actions/rerun_test.go b/services/actions/rerun_test.go
index a98de7b788..4b822e8da1 100644
--- a/services/actions/rerun_test.go
+++ b/services/actions/rerun_test.go
@@ -6,7 +6,7 @@ package actions
import (
"testing"
- actions_model "code.gitea.io/gitea/models/actions"
+ actions_model "forgejo.org/models/actions"
"github.com/stretchr/testify/assert"
)
diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go
index 18f3324fd2..cf8b29ead7 100644
--- a/services/actions/schedule_tasks.go
+++ b/services/actions/schedule_tasks.go
@@ -4,19 +4,23 @@
package actions
import (
+ "bytes"
"context"
+ "errors"
"fmt"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/timeutil"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/nektos/act/pkg/jobparser"
+ act_model "github.com/nektos/act/pkg/model"
+ "xorm.io/builder"
)
// StartScheduleTasks start the task
@@ -55,7 +59,7 @@ func startTasks(ctx context.Context) error {
// cancel running jobs if the event is push
if row.Schedule.Event == webhook_module.HookEventPush {
// cancel running jobs of the same workflow
- if err := actions_model.CancelPreviousJobs(
+ if err := CancelPreviousJobs(
ctx,
row.RepoID,
row.Schedule.Ref,
@@ -138,6 +142,16 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
return err
}
+ workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content))
+ if err != nil {
+ return err
+ }
+ notifications, err := workflow.Notifications()
+ if err != nil {
+ return err
+ }
+ run.NotifyEmail = notifications
+
// Parse the workflow specification from the cron schedule
workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars))
if err != nil {
@@ -152,3 +166,93 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
// Return nil if no errors occurred
return nil
}
+
+// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
+// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
+func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
+ // Find all runs in the specified repository, reference, and workflow with non-final status
+ runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, actions_model.FindRunOptions{
+ RepoID: repoID,
+ Ref: ref,
+ WorkflowID: workflowID,
+ TriggerEvent: event,
+ Status: []actions_model.Status{actions_model.StatusRunning, actions_model.StatusWaiting, actions_model.StatusBlocked},
+ })
+ if err != nil {
+ return err
+ }
+
+ // If there are no runs found, there's no need to proceed with cancellation, so return nil.
+ if total == 0 {
+ return nil
+ }
+
+ // Iterate over each found run and cancel its associated jobs.
+ for _, run := range runs {
+ // Find all jobs associated with the current run.
+ jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
+ RunID: run.ID,
+ })
+ if err != nil {
+ return err
+ }
+
+ // Iterate over each job and attempt to cancel it.
+ for _, job := range jobs {
+ // Skip jobs that are already in a terminal state (completed, cancelled, etc.).
+ status := job.Status
+ if status.IsDone() {
+ continue
+ }
+
+ // If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
+ if job.TaskID == 0 {
+ job.Status = actions_model.StatusCancelled
+ job.Stopped = timeutil.TimeStampNow()
+
+ // Update the job's status and stopped time in the database.
+ n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
+ if err != nil {
+ return err
+ }
+
+ // If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
+ if n == 0 {
+ return errors.New("job has changed, try again")
+ }
+
+ // Continue with the next job.
+ continue
+ }
+
+ // If the job has an associated task, try to stop the task, effectively cancelling the job.
+ if err := StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Return nil to indicate successful cancellation of all running and waiting jobs.
+ return nil
+}
+
+func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository, cancelPreviousJobs bool) error {
+ // If actions disabled when there is schedule task, this will remove the outdated schedule tasks
+ // There is no other place we can do this because the app.ini will be changed manually
+ if err := actions_model.DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
+ return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
+ }
+ if cancelPreviousJobs {
+ // cancel running cron jobs of this repository and delete old schedules
+ if err := CancelPreviousJobs(
+ ctx,
+ repo.ID,
+ repo.DefaultBranch,
+ "",
+ webhook_module.HookEventSchedule,
+ ); err != nil {
+ return fmt.Errorf("CancelPreviousJobs: %v", err)
+ }
+ }
+ return nil
+}
diff --git a/services/actions/schedule_tasks_test.go b/services/actions/schedule_tasks_test.go
new file mode 100644
index 0000000000..7073985252
--- /dev/null
+++ b/services/actions/schedule_tasks_test.go
@@ -0,0 +1,121 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "testing"
+
+ actions_model "forgejo.org/models/actions"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ webhook_module "forgejo.org/modules/webhook"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCreateScheduleTask(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: 2})
+
+ assertConstant := func(t *testing.T, cron *actions_model.ActionSchedule, run *actions_model.ActionRun) {
+ t.Helper()
+ assert.Equal(t, cron.Title, run.Title)
+ assert.Equal(t, cron.RepoID, run.RepoID)
+ assert.Equal(t, cron.OwnerID, run.OwnerID)
+ assert.Equal(t, cron.WorkflowID, run.WorkflowID)
+ assert.Equal(t, cron.TriggerUserID, run.TriggerUserID)
+ assert.Equal(t, cron.Ref, run.Ref)
+ assert.Equal(t, cron.CommitSHA, run.CommitSHA)
+ assert.Equal(t, cron.Event, run.Event)
+ assert.Equal(t, cron.EventPayload, run.EventPayload)
+ assert.Equal(t, cron.ID, run.ScheduleID)
+ assert.Equal(t, actions_model.StatusWaiting, run.Status)
+ }
+
+ assertMutable := func(t *testing.T, expected, run *actions_model.ActionRun) {
+ t.Helper()
+ assert.Equal(t, expected.NotifyEmail, run.NotifyEmail)
+ }
+
+ testCases := []struct {
+ name string
+ cron actions_model.ActionSchedule
+ want []actions_model.ActionRun
+ }{
+ {
+ name: "simple",
+ cron: actions_model.ActionSchedule{
+ Title: "scheduletitle1",
+ RepoID: repo.ID,
+ OwnerID: repo.OwnerID,
+ WorkflowID: "some.yml",
+ TriggerUserID: repo.OwnerID,
+ Ref: "branch",
+ CommitSHA: "fakeSHA",
+ Event: webhook_module.HookEventSchedule,
+ EventPayload: "fakepayload",
+ Content: []byte(
+ `
+name: test
+on: push
+jobs:
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: true
+`),
+ },
+ want: []actions_model.ActionRun{
+ {
+ Title: "scheduletitle1",
+ NotifyEmail: false,
+ },
+ },
+ },
+ {
+ name: "enable-email-notifications is true",
+ cron: actions_model.ActionSchedule{
+ Title: "scheduletitle2",
+ RepoID: repo.ID,
+ OwnerID: repo.OwnerID,
+ WorkflowID: "some.yml",
+ TriggerUserID: repo.OwnerID,
+ Ref: "branch",
+ CommitSHA: "fakeSHA",
+ Event: webhook_module.HookEventSchedule,
+ EventPayload: "fakepayload",
+ Content: []byte(
+ `
+name: test
+enable-email-notifications: true
+on: push
+jobs:
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: true
+`),
+ },
+ want: []actions_model.ActionRun{
+ {
+ Title: "scheduletitle2",
+ NotifyEmail: true,
+ },
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ require.NoError(t, CreateScheduleTask(t.Context(), &testCase.cron))
+ require.Equal(t, len(testCase.want), unittest.GetCount(t, actions_model.ActionRun{RepoID: repo.ID}))
+ for _, expected := range testCase.want {
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{Title: expected.Title})
+ assertConstant(t, &testCase.cron, run)
+ assertMutable(t, &expected, run)
+ }
+ unittest.AssertSuccessfulDelete(t, actions_model.ActionRun{RepoID: repo.ID})
+ })
+ }
+}
diff --git a/services/actions/task.go b/services/actions/task.go
new file mode 100644
index 0000000000..bb319c7d05
--- /dev/null
+++ b/services/actions/task.go
@@ -0,0 +1,252 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ secret_model "forgejo.org/models/secret"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "google.golang.org/protobuf/types/known/structpb"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv1.Task, bool, error) {
+ var (
+ task *runnerv1.Task
+ job *actions_model.ActionRunJob
+ )
+
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ t, ok, err := actions_model.CreateTaskForRunner(ctx, runner)
+ if err != nil {
+ return fmt.Errorf("CreateTaskForRunner: %w", err)
+ }
+ if !ok {
+ return nil
+ }
+
+ if err := t.LoadAttributes(ctx); err != nil {
+ return fmt.Errorf("task LoadAttributes: %w", err)
+ }
+ job = t.Job
+
+ secrets, err := secret_model.GetSecretsOfTask(ctx, t)
+ if err != nil {
+ return fmt.Errorf("GetSecretsOfTask: %w", err)
+ }
+
+ vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run)
+ if err != nil {
+ return fmt.Errorf("GetVariablesOfRun: %w", err)
+ }
+
+ needs, err := findTaskNeeds(ctx, job)
+ if err != nil {
+ return fmt.Errorf("findTaskNeeds: %w", err)
+ }
+
+ taskContext, err := generateTaskContext(t)
+ if err != nil {
+ return fmt.Errorf("generateTaskContext: %w", err)
+ }
+
+ task = &runnerv1.Task{
+ Id: t.ID,
+ WorkflowPayload: t.Job.WorkflowPayload,
+ Context: taskContext,
+ Secrets: secrets,
+ Vars: vars,
+ Needs: needs,
+ }
+
+ return nil
+ }); err != nil {
+ return nil, false, err
+ }
+
+ if task == nil {
+ return nil, false, nil
+ }
+
+ CreateCommitStatus(ctx, job)
+
+ return task, true, nil
+}
+
+func generateTaskContext(t *actions_model.ActionTask) (*structpb.Struct, error) {
+ giteaRuntimeToken, err := CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID)
+ if err != nil {
+ return nil, err
+ }
+
+ gitCtx := GenerateGiteaContext(t.Job.Run, t.Job)
+ gitCtx["token"] = t.Token
+ gitCtx["gitea_runtime_token"] = giteaRuntimeToken
+
+ return structpb.NewStruct(gitCtx)
+}
+
+func findTaskNeeds(ctx context.Context, taskJob *actions_model.ActionRunJob) (map[string]*runnerv1.TaskNeed, error) {
+ taskNeeds, err := FindTaskNeeds(ctx, taskJob)
+ if err != nil {
+ return nil, err
+ }
+ ret := make(map[string]*runnerv1.TaskNeed, len(taskNeeds))
+ for jobID, taskNeed := range taskNeeds {
+ ret[jobID] = &runnerv1.TaskNeed{
+ Outputs: taskNeed.Outputs,
+ Result: runnerv1.Result(taskNeed.Result),
+ }
+ }
+ return ret, nil
+}
+
+func StopTask(ctx context.Context, taskID int64, status actions_model.Status) error {
+ if !status.IsDone() {
+ return fmt.Errorf("cannot stop task with status %v", status)
+ }
+ e := db.GetEngine(ctx)
+
+ task := &actions_model.ActionTask{}
+ if has, err := e.ID(taskID).Get(task); err != nil {
+ return err
+ } else if !has {
+ return util.ErrNotExist
+ }
+ if task.Status.IsDone() {
+ return nil
+ }
+
+ now := timeutil.TimeStampNow()
+ task.Status = status
+ task.Stopped = now
+ if _, err := UpdateRunJob(ctx, &actions_model.ActionRunJob{
+ ID: task.JobID,
+ Status: task.Status,
+ Stopped: task.Stopped,
+ }, nil); err != nil {
+ return err
+ }
+
+ if err := actions_model.UpdateTask(ctx, task, "status", "stopped"); err != nil {
+ return err
+ }
+
+ if err := task.LoadAttributes(ctx); err != nil {
+ return err
+ }
+
+ for _, step := range task.Steps {
+ if !step.Status.IsDone() {
+ step.Status = status
+ if step.Started == 0 {
+ step.Started = now
+ }
+ step.Stopped = now
+ }
+ if _, err := e.ID(step.ID).Update(step); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// UpdateTaskByState updates the task by the state.
+// It will always update the task if the state is not final, even there is no change.
+// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
+func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.TaskState) (*actions_model.ActionTask, error) {
+ stepStates := map[int64]*runnerv1.StepState{}
+ for _, v := range state.Steps {
+ stepStates[v.Id] = v
+ }
+
+ ctx, commiter, err := db.TxContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer commiter.Close()
+
+ e := db.GetEngine(ctx)
+
+ task := &actions_model.ActionTask{}
+ if has, err := e.ID(state.Id).Get(task); err != nil {
+ return nil, err
+ } else if !has {
+ return nil, util.ErrNotExist
+ } else if runnerID != task.RunnerID {
+ return nil, errors.New("invalid runner for task")
+ }
+
+ if task.Status.IsDone() {
+ // the state is final, do nothing
+ return task, nil
+ }
+
+ // state.Result is not unspecified means the task is finished
+ if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
+ task.Status = actions_model.Status(state.Result)
+ task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
+ if err := actions_model.UpdateTask(ctx, task, "status", "stopped"); err != nil {
+ return nil, err
+ }
+ if _, err := UpdateRunJob(ctx, &actions_model.ActionRunJob{
+ ID: task.JobID,
+ Status: task.Status,
+ Stopped: task.Stopped,
+ }, nil); err != nil {
+ return nil, err
+ }
+ } else {
+ // Force update ActionTask.Updated to avoid the task being judged as a zombie task
+ task.Updated = timeutil.TimeStampNow()
+ if err := actions_model.UpdateTask(ctx, task, "updated"); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := task.LoadAttributes(ctx); err != nil {
+ return nil, err
+ }
+
+ for _, step := range task.Steps {
+ var result runnerv1.Result
+ if v, ok := stepStates[step.Index]; ok {
+ result = v.Result
+ step.LogIndex = v.LogIndex
+ step.LogLength = v.LogLength
+ step.Started = convertTimestamp(v.StartedAt)
+ step.Stopped = convertTimestamp(v.StoppedAt)
+ }
+ if result != runnerv1.Result_RESULT_UNSPECIFIED {
+ step.Status = actions_model.Status(result)
+ } else if step.Started != 0 {
+ step.Status = actions_model.StatusRunning
+ }
+ if _, err := e.ID(step.ID).Update(step); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := commiter.Commit(); err != nil {
+ return nil, err
+ }
+
+ return task, nil
+}
+
+func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp {
+ if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 {
+ return timeutil.TimeStamp(0)
+ }
+ return timeutil.TimeStamp(timestamp.AsTime().Unix())
+}
diff --git a/services/actions/variables.go b/services/actions/variables.go
index a5703898ab..fed1fd0890 100644
--- a/services/actions/variables.go
+++ b/services/actions/variables.go
@@ -8,10 +8,10 @@ import (
"regexp"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
- secret_service "code.gitea.io/gitea/services/secrets"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
+ secret_service "forgejo.org/services/secrets"
)
func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*actions_model.ActionVariable, error) {
diff --git a/services/actions/workflows.go b/services/actions/workflows.go
index e2fb31622a..fbba3fd667 100644
--- a/services/actions/workflows.go
+++ b/services/actions/workflows.go
@@ -10,18 +10,19 @@ import (
"fmt"
"strconv"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/convert"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/perm"
+ "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/webhook"
+ "forgejo.org/services/convert"
"github.com/nektos/act/pkg/jobparser"
act_model "github.com/nektos/act/pkg/model"
@@ -49,15 +50,15 @@ type Workflow struct {
type InputValueGetter func(key string) string
-func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) error {
+func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) (r *actions_model.ActionRun, j []string, err error) {
content, err := actions.GetContentFromEntry(entry.GitEntry)
if err != nil {
- return err
+ return nil, nil, err
}
wf, err := act_model.ReadWorkflow(bytes.NewReader(content))
if err != nil {
- return err
+ return nil, nil, err
}
fullWorkflowID := ".forgejo/workflows/" + entry.WorkflowID
@@ -79,7 +80,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
if len(name) == 0 {
name = key
}
- return InputRequiredErr{Name: name}
+ return nil, nil, InputRequiredErr{Name: name}
}
continue
}
@@ -92,9 +93,11 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
}
if int64(len(inputs)) > setting.Actions.LimitDispatchInputs {
- return errors.New("to many inputs")
+ return nil, nil, errors.New("to many inputs")
}
+ jobNames := util.KeysOfMap(wf.Jobs)
+
payload := &structs.WorkflowDispatchPayload{
Inputs: inputs,
Ref: entry.Ref,
@@ -105,7 +108,12 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
p, err := json.Marshal(payload)
if err != nil {
- return err
+ return nil, nil, err
+ }
+
+ notifications, err := wf.Notifications()
+ if err != nil {
+ return nil, nil, err
}
run := &actions_model.ActionRun{
@@ -122,19 +130,20 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
EventPayload: string(p),
TriggerEvent: string(webhook.HookEventWorkflowDispatch),
Status: actions_model.StatusWaiting,
+ NotifyEmail: notifications,
}
vars, err := actions_model.GetVariablesOfRun(ctx, run)
if err != nil {
- return err
+ return nil, nil, err
}
jobs, err := jobparser.Parse(content, jobparser.WithVars(vars))
if err != nil {
- return err
+ return nil, nil, err
}
- return actions_model.InsertRun(ctx, run, jobs)
+ return run, jobNames, actions_model.InsertRun(ctx, run, jobs)
}
func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Workflow, error) {
diff --git a/services/agit/agit.go b/services/agit/agit.go
index a18f9ef728..20e87642c3 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.go
@@ -9,15 +9,15 @@ import (
"os"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/git/pushoptions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/private"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/git/pushoptions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/private"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
)
// ProcReceive handle proc receive work
diff --git a/services/asymkey/deploy_key.go b/services/asymkey/deploy_key.go
index e127cbfc6e..4a2cb53eec 100644
--- a/services/asymkey/deploy_key.go
+++ b/services/asymkey/deploy_key.go
@@ -6,10 +6,10 @@ package asymkey
import (
"context"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
)
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
diff --git a/services/asymkey/main_test.go b/services/asymkey/main_test.go
index 060cc78cec..8ba76668b1 100644
--- a/services/asymkey/main_test.go
+++ b/services/asymkey/main_test.go
@@ -6,11 +6,11 @@ package asymkey
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/activities"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/activities"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go
index 8fb569939c..527f6edd92 100644
--- a/services/asymkey/sign.go
+++ b/services/asymkey/sign.go
@@ -8,18 +8,17 @@ import (
"fmt"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/setting"
)
type signingMode string
@@ -90,6 +89,13 @@ func SigningKey(ctx context.Context, repoPath string) (string, *git.Signature) {
return "", nil
}
+ if setting.Repository.Signing.Format == "ssh" {
+ return setting.Repository.Signing.SigningKey, &git.Signature{
+ Name: setting.Repository.Signing.SigningName,
+ Email: setting.Repository.Signing.SigningEmail,
+ }
+ }
+
if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
// Can ignore the error here as it means that commit.gpgsign is not set
value, _, _ := git.NewCommand(ctx, "config", "--get", "commit.gpgsign").RunStdString(&git.RunOpts{Dir: repoPath})
@@ -145,22 +151,19 @@ Loop:
case always:
break Loop
case pubkey:
- keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
- OwnerID: u.ID,
- IncludeSubKeys: true,
- })
+ hasPubKey, err := asymkey_model.HasAsymKeyByUID(ctx, u.ID)
if err != nil {
return false, "", nil, err
}
- if len(keys) == 0 {
+ if !hasPubKey {
return false, "", nil, &ErrWontSign{pubkey}
}
case twofa:
- twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
- if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ if err != nil {
return false, "", nil, err
}
- if twofaModel == nil {
+ if !hasTwoFactor {
return false, "", nil, &ErrWontSign{twofa}
}
}
@@ -185,22 +188,19 @@ Loop:
case always:
break Loop
case pubkey:
- keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
- OwnerID: u.ID,
- IncludeSubKeys: true,
- })
+ hasPubKey, err := asymkey_model.HasAsymKeyByUID(ctx, u.ID)
if err != nil {
return false, "", nil, err
}
- if len(keys) == 0 {
+ if !hasPubKey {
return false, "", nil, &ErrWontSign{pubkey}
}
case twofa:
- twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
- if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ if err != nil {
return false, "", nil, err
}
- if twofaModel == nil {
+ if !hasTwoFactor {
return false, "", nil, &ErrWontSign{twofa}
}
case parentSigned:
@@ -241,22 +241,19 @@ Loop:
case always:
break Loop
case pubkey:
- keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
- OwnerID: u.ID,
- IncludeSubKeys: true,
- })
+ hasPubKey, err := asymkey_model.HasAsymKeyByUID(ctx, u.ID)
if err != nil {
return false, "", nil, err
}
- if len(keys) == 0 {
+ if !hasPubKey {
return false, "", nil, &ErrWontSign{pubkey}
}
case twofa:
- twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
- if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ if err != nil {
return false, "", nil, err
}
- if twofaModel == nil {
+ if !hasTwoFactor {
return false, "", nil, &ErrWontSign{twofa}
}
case parentSigned:
@@ -306,22 +303,19 @@ Loop:
case always:
break Loop
case pubkey:
- keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
- OwnerID: u.ID,
- IncludeSubKeys: true,
- })
+ hasPubKey, err := asymkey_model.HasAsymKeyByUID(ctx, u.ID)
if err != nil {
return false, "", nil, err
}
- if len(keys) == 0 {
+ if !hasPubKey {
return false, "", nil, &ErrWontSign{pubkey}
}
case twofa:
- twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
- if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID)
+ if err != nil {
return false, "", nil, err
}
- if twofaModel == nil {
+ if !hasTwoFactor {
return false, "", nil, &ErrWontSign{twofa}
}
case approved:
diff --git a/services/asymkey/ssh_key.go b/services/asymkey/ssh_key.go
index 83d7edafa3..f20445891d 100644
--- a/services/asymkey/ssh_key.go
+++ b/services/asymkey/ssh_key.go
@@ -6,9 +6,9 @@ package asymkey
import (
"context"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
)
// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
diff --git a/services/asymkey/ssh_key_test.go b/services/asymkey/ssh_key_test.go
index d667a02557..24b28d295e 100644
--- a/services/asymkey/ssh_key_test.go
+++ b/services/asymkey/ssh_key_test.go
@@ -6,11 +6,11 @@ package asymkey
import (
"testing"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/attachment/attachment.go b/services/attachment/attachment.go
index c911945e5d..b6f763842b 100644
--- a/services/attachment/attachment.go
+++ b/services/attachment/attachment.go
@@ -9,12 +9,12 @@ import (
"fmt"
"io"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/services/context/upload"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/validation"
+ "forgejo.org/services/context/upload"
"github.com/google/uuid"
)
@@ -51,7 +51,7 @@ func NewExternalAttachment(ctx context.Context, attach *repo_model.Attachment) (
if attach.ExternalURL == "" {
return nil, fmt.Errorf("attachment %s should have a external url", attach.Name)
}
- if !validation.IsValidExternalURL(attach.ExternalURL) {
+ if !validation.IsValidReleaseAssetURL(attach.ExternalURL) {
return nil, repo_model.ErrInvalidExternalURL{ExternalURL: attach.ExternalURL}
}
diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go
index c24b3f8006..ef002bf16c 100644
--- a/services/attachment/attachment_test.go
+++ b/services/attachment/attachment_test.go
@@ -8,13 +8,13 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -43,6 +43,6 @@ func TestUploadAttachment(t *testing.T) {
attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID)
require.NoError(t, err)
- assert.EqualValues(t, user.ID, attachment.UploaderID)
+ assert.Equal(t, user.ID, attachment.UploaderID)
assert.Equal(t, int64(0), attachment.DownloadCount)
}
diff --git a/services/auth/auth.go b/services/auth/auth.go
index c10872313f..85c9296ced 100644
--- a/services/auth/auth.go
+++ b/services/auth/auth.go
@@ -10,15 +10,15 @@ import (
"regexp"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/webauthn"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/session"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
- gitea_context "code.gitea.io/gitea/services/context"
- user_service "code.gitea.io/gitea/services/user"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/webauthn"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/session"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web/middleware"
+ gitea_context "forgejo.org/services/context"
+ user_service "forgejo.org/services/user"
)
// Init should be called exactly once when the application starts to allow plugins
diff --git a/services/auth/auth_test.go b/services/auth/auth_test.go
index 3adaa28664..a6c6c74022 100644
--- a/services/auth/auth_test.go
+++ b/services/auth/auth_test.go
@@ -8,7 +8,7 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
)
func Test_isGitRawOrLFSPath(t *testing.T) {
diff --git a/services/auth/basic.go b/services/auth/basic.go
index d489164954..f259ad5f69 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -9,15 +9,15 @@ import (
"net/http"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/middleware"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web/middleware"
)
// Ensure the struct implements the interface.
diff --git a/services/auth/group.go b/services/auth/group.go
index aecf43cb24..b713301b50 100644
--- a/services/auth/group.go
+++ b/services/auth/group.go
@@ -7,7 +7,7 @@ import (
"net/http"
"strings"
- user_model "code.gitea.io/gitea/models/user"
+ user_model "forgejo.org/models/user"
)
// Ensure the struct implements the interface.
diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go
index b604349f80..e776ccbbed 100644
--- a/services/auth/httpsign.go
+++ b/services/auth/httpsign.go
@@ -11,13 +11,13 @@ import (
"net/http"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
- "github.com/go-fed/httpsig"
+ "github.com/42wim/httpsig"
"golang.org/x/crypto/ssh"
)
@@ -134,7 +134,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
// Check if it's really a ssh certificate
cert, ok := pk.(*ssh.Certificate)
if !ok {
- return nil, fmt.Errorf("no certificate found")
+ return nil, errors.New("no certificate found")
}
c := &ssh.CertChecker{
@@ -153,7 +153,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
// check the CA of the cert
if !c.IsUserAuthority(cert.SignatureKey) {
- return nil, fmt.Errorf("CA check failed")
+ return nil, errors.New("CA check failed")
}
// Create a verifier
@@ -191,7 +191,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
}
// No public key matching a principal in the certificate is registered in gitea
- return nil, fmt.Errorf("no valid principal found")
+ return nil, errors.New("no valid principal found")
}
// doVerify iterates across the provided public keys attempting the verify the current request against each key in turn
@@ -205,7 +205,7 @@ func doVerify(verifier httpsig.Verifier, sshPublicKeys []ssh.PublicKey) error {
case strings.HasPrefix(publicKey.Type(), "ssh-ed25519"):
algos = []httpsig.Algorithm{httpsig.ED25519}
case strings.HasPrefix(publicKey.Type(), "ssh-rsa"):
- algos = []httpsig.Algorithm{httpsig.RSA_SHA1, httpsig.RSA_SHA256, httpsig.RSA_SHA512}
+ algos = []httpsig.Algorithm{httpsig.RSA_SHA256, httpsig.RSA_SHA512}
}
for _, algo := range algos {
if err := verifier.Verify(cryptoPubkey, algo); err == nil {
diff --git a/services/auth/interface.go b/services/auth/interface.go
index ece28af12d..12b04a7abf 100644
--- a/services/auth/interface.go
+++ b/services/auth/interface.go
@@ -7,9 +7,9 @@ import (
"context"
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/session"
- "code.gitea.io/gitea/modules/web/middleware"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/session"
+ "forgejo.org/modules/web/middleware"
)
// DataStore represents a data store
diff --git a/services/auth/main_test.go b/services/auth/main_test.go
index b81c39a1f2..0e6315b06e 100644
--- a/services/auth/main_test.go
+++ b/services/auth/main_test.go
@@ -6,7 +6,7 @@ package auth
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go
index b983e57ecd..093940aa18 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -11,15 +11,15 @@ import (
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/auth/source/oauth2"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/actions"
+ "forgejo.org/services/auth/source/oauth2"
)
// Ensure the struct implements the interface.
@@ -121,18 +121,6 @@ func (o *OAuth2) Name() string {
// representing whether the token exists or not
func parseToken(req *http.Request) (string, bool) {
_ = req.ParseForm()
- if !setting.DisableQueryAuthToken {
- // Check token.
- if token := req.Form.Get("token"); token != "" {
- return token, true
- }
- // Check access token.
- if token := req.Form.Get("access_token"); token != "" {
- return token, true
- }
- } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" {
- log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true")
- }
// check header token
if auHead := req.Header.Get("Authorization"); auHead != "" {
diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go
index c9b4ed06cc..d6455b33ad 100644
--- a/services/auth/oauth2_test.go
+++ b/services/auth/oauth2_test.go
@@ -4,13 +4,12 @@
package auth
import (
- "context"
"testing"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/actions"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/actions"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -27,7 +26,7 @@ func TestUserIDFromToken(t *testing.T) {
ds := make(middleware.ContextData)
o := OAuth2{}
- uid := o.userIDFromToken(context.Background(), token, ds)
+ uid := o.userIDFromToken(t.Context(), token, ds)
assert.Equal(t, int64(user_model.ActionsUserID), uid)
assert.Equal(t, true, ds["IsActionsToken"])
assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID))
@@ -48,7 +47,7 @@ func TestCheckTaskIsRunning(t *testing.T) {
for name := range cases {
c := cases[name]
t.Run(name, func(t *testing.T) {
- actual := CheckTaskIsRunning(context.Background(), c.TaskID)
+ actual := CheckTaskIsRunning(t.Context(), c.TaskID)
assert.Equal(t, c.Expected, actual)
})
}
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index 8a5a5dc992..eb9ceb8cf2 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -8,11 +8,11 @@ import (
"net/http"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web/middleware"
gouuid "github.com/google/uuid"
)
diff --git a/services/auth/reverseproxy_test.go b/services/auth/reverseproxy_test.go
index 7f1b2a7782..cdcd845148 100644
--- a/services/auth/reverseproxy_test.go
+++ b/services/auth/reverseproxy_test.go
@@ -7,11 +7,11 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/require"
)
@@ -38,10 +38,10 @@ func TestReverseProxyAuth(t *testing.T) {
require.EqualValues(t, 1, user_model.CountUsers(db.DefaultContext, nil))
unittest.AssertExistsAndLoadBean(t, &user_model.User{Email: "edgar@example.org", Name: "Edgar", LowerName: "edgar", FullName: "Edgar Allan Poe", IsAdmin: true})
- require.EqualValues(t, "edgar@example.org", user.Email)
- require.EqualValues(t, "Edgar", user.Name)
- require.EqualValues(t, "edgar", user.LowerName)
- require.EqualValues(t, "Edgar Allan Poe", user.FullName)
+ require.Equal(t, "edgar@example.org", user.Email)
+ require.Equal(t, "Edgar", user.Name)
+ require.Equal(t, "edgar", user.LowerName)
+ require.Equal(t, "Edgar Allan Poe", user.FullName)
require.True(t, user.IsAdmin)
})
@@ -58,10 +58,10 @@ func TestReverseProxyAuth(t *testing.T) {
require.EqualValues(t, 2, user_model.CountUsers(db.DefaultContext, nil))
unittest.AssertExistsAndLoadBean(t, &user_model.User{Email: "gusted@example.org", Name: "Gusted", LowerName: "gusted", FullName: "❤‿❤"}, "is_admin = false")
- require.EqualValues(t, "gusted@example.org", user.Email)
- require.EqualValues(t, "Gusted", user.Name)
- require.EqualValues(t, "gusted", user.LowerName)
- require.EqualValues(t, "❤‿❤", user.FullName)
+ require.Equal(t, "gusted@example.org", user.Email)
+ require.Equal(t, "Gusted", user.Name)
+ require.Equal(t, "gusted", user.LowerName)
+ require.Equal(t, "❤‿❤", user.FullName)
require.False(t, user.IsAdmin)
})
}
diff --git a/services/auth/session.go b/services/auth/session.go
index 35d97e42da..a15c24c940 100644
--- a/services/auth/session.go
+++ b/services/auth/session.go
@@ -6,8 +6,8 @@ package auth
import (
"net/http"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
)
// Ensure the struct implements the interface.
diff --git a/services/auth/signin.go b/services/auth/signin.go
index e116a088e0..495b3d387e 100644
--- a/services/auth/signin.go
+++ b/services/auth/signin.go
@@ -7,18 +7,17 @@ import (
"context"
"strings"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- "code.gitea.io/gitea/services/auth/source/smtp"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/services/auth/source/oauth2"
+ "forgejo.org/services/auth/source/smtp"
- _ "code.gitea.io/gitea/services/auth/source/db" // register the sources (and below)
- _ "code.gitea.io/gitea/services/auth/source/ldap" // register the ldap source
- _ "code.gitea.io/gitea/services/auth/source/pam" // register the pam source
- _ "code.gitea.io/gitea/services/auth/source/sspi" // register the sspi source
+ _ "forgejo.org/services/auth/source/db" // register the sources (and below)
+ _ "forgejo.org/services/auth/source/ldap" // register the ldap source
+ _ "forgejo.org/services/auth/source/pam" // register the pam source
)
// UserSignIn validates user name and password.
diff --git a/services/auth/source.go b/services/auth/source.go
index 69b71a6dea..b13554efde 100644
--- a/services/auth/source.go
+++ b/services/auth/source.go
@@ -6,9 +6,9 @@ package auth
import (
"context"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
)
// DeleteSource deletes a AuthSource record in DB.
diff --git a/services/auth/source/db/assert_interface_test.go b/services/auth/source/db/assert_interface_test.go
index 62387c78f0..1422e9693c 100644
--- a/services/auth/source/db/assert_interface_test.go
+++ b/services/auth/source/db/assert_interface_test.go
@@ -4,9 +4,9 @@
package db_test
import (
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/db"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/db"
)
// This test file exists to assert that our Source exposes the interfaces that we expect
diff --git a/services/auth/source/db/authenticate.go b/services/auth/source/db/authenticate.go
index 8160141863..b1d8eae6ae 100644
--- a/services/auth/source/db/authenticate.go
+++ b/services/auth/source/db/authenticate.go
@@ -7,9 +7,9 @@ import (
"context"
"fmt"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
)
// ErrUserPasswordNotSet represents a "ErrUserPasswordNotSet" kind of error.
@@ -50,7 +50,7 @@ func Authenticate(ctx context.Context, user *user_model.User, login, password st
if !user.IsPasswordSet() {
return nil, ErrUserPasswordNotSet{UID: user.ID, Name: user.Name}
- } else if !user.ValidatePassword(password) {
+ } else if !user.ValidatePassword(ctx, password) {
return nil, ErrUserPasswordInvalid{UID: user.ID, Name: user.Name}
}
diff --git a/services/auth/source/db/source.go b/services/auth/source/db/source.go
index bb2270cbd6..d158718bb2 100644
--- a/services/auth/source/db/source.go
+++ b/services/auth/source/db/source.go
@@ -6,8 +6,8 @@ package db
import (
"context"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
)
// Source is a password authentication service
diff --git a/services/auth/source/ldap/assert_interface_test.go b/services/auth/source/ldap/assert_interface_test.go
index 33347687dc..859143a3f8 100644
--- a/services/auth/source/ldap/assert_interface_test.go
+++ b/services/auth/source/ldap/assert_interface_test.go
@@ -4,9 +4,9 @@
package ldap_test
import (
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/ldap"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/ldap"
)
// This test file exists to assert that our Source exposes the interfaces that we expect
diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go
index ba407b351a..a094c1410c 100644
--- a/services/auth/source/ldap/source.go
+++ b/services/auth/source/ldap/source.go
@@ -6,10 +6,10 @@ package ldap
import (
"strings"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/secret"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/secret"
+ "forgejo.org/modules/setting"
)
// .____ ________ _____ __________
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 68ecd16342..a2ff10cd07 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -8,13 +8,13 @@ import (
"fmt"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- auth_module "code.gitea.io/gitea/modules/auth"
- "code.gitea.io/gitea/modules/optional"
- source_service "code.gitea.io/gitea/services/auth/source"
- user_service "code.gitea.io/gitea/services/user"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ auth_module "forgejo.org/modules/auth"
+ "forgejo.org/modules/optional"
+ source_service "forgejo.org/services/auth/source"
+ user_service "forgejo.org/services/user"
)
// Authenticate queries if login/password is valid against the LDAP directory pool,
diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go
index 2a61386ae1..da7e225428 100644
--- a/services/auth/source/ldap/source_search.go
+++ b/services/auth/source/ldap/source_search.go
@@ -11,8 +11,8 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
"github.com/go-ldap/ldap/v3"
)
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index 1f70edaa82..cb6172ed1d 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -8,16 +8,16 @@ import (
"fmt"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- auth_module "code.gitea.io/gitea/modules/auth"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- source_service "code.gitea.io/gitea/services/auth/source"
- user_service "code.gitea.io/gitea/services/user"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ auth_module "forgejo.org/modules/auth"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ source_service "forgejo.org/services/auth/source"
+ user_service "forgejo.org/services/user"
)
// Sync causes this ldap source to synchronize its users with the db
diff --git a/services/auth/source/oauth2/assert_interface_test.go b/services/auth/source/oauth2/assert_interface_test.go
index 56fe0e4aa8..12fce257cf 100644
--- a/services/auth/source/oauth2/assert_interface_test.go
+++ b/services/auth/source/oauth2/assert_interface_test.go
@@ -4,9 +4,9 @@
package oauth2_test
import (
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/oauth2"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/oauth2"
)
// This test file exists to assert that our Source exposes the interfaces that we expect
diff --git a/services/auth/source/oauth2/init.go b/services/auth/source/oauth2/init.go
index 5c25681548..6c78a14da4 100644
--- a/services/auth/source/oauth2/init.go
+++ b/services/auth/source/oauth2/init.go
@@ -9,11 +9,11 @@ import (
"net/http"
"sync"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
"github.com/google/uuid"
"github.com/gorilla/sessions"
diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go
index 92adfc4d84..550945a812 100644
--- a/services/auth/source/oauth2/jwtsigningkey.go
+++ b/services/auth/source/oauth2/jwtsigningkey.go
@@ -18,9 +18,9 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
"github.com/golang-jwt/jwt/v5"
)
diff --git a/services/auth/source/oauth2/jwtsigningkey_test.go b/services/auth/source/oauth2/jwtsigningkey_test.go
index 4db538b0e8..9b07b022df 100644
--- a/services/auth/source/oauth2/jwtsigningkey_test.go
+++ b/services/auth/source/oauth2/jwtsigningkey_test.go
@@ -13,8 +13,8 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -30,7 +30,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
block, _ := pem.Decode(fileContent)
assert.NotNil(t, block)
- assert.EqualValues(t, "PRIVATE KEY", block.Type)
+ assert.Equal(t, "PRIVATE KEY", block.Type)
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
require.NoError(t, err)
@@ -44,14 +44,14 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
- assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen())
+ assert.Equal(t, 2048, rsaPrivateKey.N.BitLen())
t.Run("Load key with differ specified algorithm", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")()
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
- assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen())
+ assert.Equal(t, 2048, rsaPrivateKey.N.BitLen())
})
})
@@ -62,7 +62,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
- assert.EqualValues(t, 3072, rsaPrivateKey.N.BitLen())
+ assert.Equal(t, 3072, rsaPrivateKey.N.BitLen())
})
t.Run("RSA-4096", func(t *testing.T) {
@@ -72,7 +72,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
- assert.EqualValues(t, 4096, rsaPrivateKey.N.BitLen())
+ assert.Equal(t, 4096, rsaPrivateKey.N.BitLen())
})
t.Run("ECDSA-256", func(t *testing.T) {
@@ -82,7 +82,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
- assert.EqualValues(t, 256, ecdsaPrivateKey.Params().BitSize)
+ assert.Equal(t, 256, ecdsaPrivateKey.Params().BitSize)
})
t.Run("ECDSA-384", func(t *testing.T) {
@@ -92,7 +92,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
- assert.EqualValues(t, 384, ecdsaPrivateKey.Params().BitSize)
+ assert.Equal(t, 384, ecdsaPrivateKey.Params().BitSize)
})
t.Run("ECDSA-512", func(t *testing.T) {
@@ -102,7 +102,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) {
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
- assert.EqualValues(t, 521, ecdsaPrivateKey.Params().BitSize)
+ assert.Equal(t, 521, ecdsaPrivateKey.Params().BitSize)
})
t.Run("EdDSA", func(t *testing.T) {
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go
index f2c1bb4894..773ce19c12 100644
--- a/services/auth/source/oauth2/providers.go
+++ b/services/auth/source/oauth2/providers.go
@@ -12,11 +12,11 @@ import (
"net/url"
"sort"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
"github.com/markbates/goth"
)
diff --git a/services/auth/source/oauth2/providers_base.go b/services/auth/source/oauth2/providers_base.go
index 9d4ab106e5..1ef8d0af72 100644
--- a/services/auth/source/oauth2/providers_base.go
+++ b/services/auth/source/oauth2/providers_base.go
@@ -6,8 +6,8 @@ package oauth2
import (
"html/template"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/svg"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/svg"
)
// BaseProvider represents a common base for Provider
@@ -48,4 +48,8 @@ func (b *BaseProvider) CustomURLSettings() *CustomURLSettings {
return nil
}
+func (b *BaseProvider) CanProvideSSHKeys() bool {
+ return false
+}
+
var _ Provider = &BaseProvider{}
diff --git a/services/auth/source/oauth2/providers_custom.go b/services/auth/source/oauth2/providers_custom.go
index 65cf538ad7..51a412e0be 100644
--- a/services/auth/source/oauth2/providers_custom.go
+++ b/services/auth/source/oauth2/providers_custom.go
@@ -4,7 +4,7 @@
package oauth2
import (
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
"github.com/markbates/goth"
"github.com/markbates/goth/providers/azureadv2"
diff --git a/services/auth/source/oauth2/providers_openid.go b/services/auth/source/oauth2/providers_openid.go
index 285876d5ac..7950506ab7 100644
--- a/services/auth/source/oauth2/providers_openid.go
+++ b/services/auth/source/oauth2/providers_openid.go
@@ -6,9 +6,9 @@ package oauth2
import (
"html/template"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/svg"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/svg"
"github.com/markbates/goth"
"github.com/markbates/goth/providers/openidConnect"
@@ -51,6 +51,10 @@ func (o *OpenIDProvider) CustomURLSettings() *CustomURLSettings {
return nil
}
+func (o *OpenIDProvider) CanProvideSSHKeys() bool {
+ return true
+}
+
var _ GothProvider = &OpenIDProvider{}
func init() {
diff --git a/services/auth/source/oauth2/providers_simple.go b/services/auth/source/oauth2/providers_simple.go
index e95323a62a..8e2c0a7700 100644
--- a/services/auth/source/oauth2/providers_simple.go
+++ b/services/auth/source/oauth2/providers_simple.go
@@ -4,7 +4,7 @@
package oauth2
import (
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
"github.com/markbates/goth"
"github.com/markbates/goth/providers/azuread"
diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go
index 675005e55a..5245f88270 100644
--- a/services/auth/source/oauth2/source.go
+++ b/services/auth/source/oauth2/source.go
@@ -4,8 +4,10 @@
package oauth2
import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
+ "strings"
+
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
)
// Source holds configuration for the OAuth2 login source.
@@ -17,15 +19,16 @@ type Source struct {
CustomURLMapping *CustomURLMapping
IconURL string
- Scopes []string
- RequiredClaimName string
- RequiredClaimValue string
- GroupClaimName string
- AdminGroup string
- GroupTeamMap string
- GroupTeamMapRemoval bool
- RestrictedGroup string
- SkipLocalTwoFA bool `json:",omitempty"`
+ Scopes []string
+ AttributeSSHPublicKey string
+ RequiredClaimName string
+ RequiredClaimValue string
+ GroupClaimName string
+ AdminGroup string
+ GroupTeamMap string
+ GroupTeamMapRemoval bool
+ RestrictedGroup string
+ SkipLocalTwoFA bool `json:",omitempty"`
// reference to the authSource
authSource *auth.Source
@@ -41,6 +44,11 @@ func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
+// ProvidesSSHKeys returns if this source provides SSH Keys
+func (source *Source) ProvidesSSHKeys() bool {
+ return len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0
+}
+
// SetAuthSource sets the related AuthSource
func (source *Source) SetAuthSource(authSource *auth.Source) {
source.authSource = authSource
diff --git a/services/auth/source/oauth2/source_authenticate.go b/services/auth/source/oauth2/source_authenticate.go
index bbda35dee0..1efd7be02a 100644
--- a/services/auth/source/oauth2/source_authenticate.go
+++ b/services/auth/source/oauth2/source_authenticate.go
@@ -6,8 +6,8 @@ package oauth2
import (
"context"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/services/auth/source/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/services/auth/source/db"
)
// Authenticate falls back to the db authenticator
diff --git a/services/auth/source/oauth2/store.go b/services/auth/source/oauth2/store.go
index e031653119..d52581ea2d 100644
--- a/services/auth/source/oauth2/store.go
+++ b/services/auth/source/oauth2/store.go
@@ -8,8 +8,8 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/log"
- session_module "code.gitea.io/gitea/modules/session"
+ "forgejo.org/modules/log"
+ session_module "forgejo.org/modules/session"
chiSession "code.forgejo.org/go-chi/session"
"github.com/gorilla/sessions"
diff --git a/services/auth/source/oauth2/token.go b/services/auth/source/oauth2/token.go
index 3405619d3f..b060b6b746 100644
--- a/services/auth/source/oauth2/token.go
+++ b/services/auth/source/oauth2/token.go
@@ -4,10 +4,11 @@
package oauth2
import (
+ "errors"
"fmt"
"time"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/modules/timeutil"
"github.com/golang-jwt/jwt/v5"
)
@@ -51,12 +52,12 @@ func ParseToken(jwtToken string, signingKey JWTSigningKey) (*Token, error) {
return nil, err
}
if !parsedToken.Valid {
- return nil, fmt.Errorf("invalid token")
+ return nil, errors.New("invalid token")
}
var token *Token
var ok bool
if token, ok = parsedToken.Claims.(*Token); !ok || !parsedToken.Valid {
- return nil, fmt.Errorf("invalid token")
+ return nil, errors.New("invalid token")
}
return token, nil
}
diff --git a/services/auth/source/pam/assert_interface_test.go b/services/auth/source/pam/assert_interface_test.go
index 8e7648b8d3..8c54b7e9e2 100644
--- a/services/auth/source/pam/assert_interface_test.go
+++ b/services/auth/source/pam/assert_interface_test.go
@@ -4,9 +4,9 @@
package pam_test
import (
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/pam"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/pam"
)
// This test file exists to assert that our Source exposes the interfaces that we expect
diff --git a/services/auth/source/pam/source.go b/services/auth/source/pam/source.go
index 96b182e185..e1dc83ba43 100644
--- a/services/auth/source/pam/source.go
+++ b/services/auth/source/pam/source.go
@@ -4,8 +4,8 @@
package pam
import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
)
// __________ _____ _____
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
index 0df0b2bca1..6f3ffc2d9d 100644
--- a/services/auth/source/pam/source_authenticate.go
+++ b/services/auth/source/pam/source_authenticate.go
@@ -8,12 +8,12 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/auth/pam"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/validation"
+ "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/auth/pam"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
"github.com/google/uuid"
)
diff --git a/services/auth/source/remote/source.go b/services/auth/source/remote/source.go
index 4165858a56..effbabc7d0 100644
--- a/services/auth/source/remote/source.go
+++ b/services/auth/source/remote/source.go
@@ -4,8 +4,8 @@
package remote
import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
)
type Source struct {
diff --git a/services/auth/source/smtp/assert_interface_test.go b/services/auth/source/smtp/assert_interface_test.go
index 6c9cde66e1..6826dae873 100644
--- a/services/auth/source/smtp/assert_interface_test.go
+++ b/services/auth/source/smtp/assert_interface_test.go
@@ -4,9 +4,9 @@
package smtp_test
import (
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/auth/source/smtp"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/auth/source/smtp"
)
// This test file exists to assert that our Source exposes the interfaces that we expect
diff --git a/services/auth/source/smtp/source.go b/services/auth/source/smtp/source.go
index 2a648e421e..d44971bab0 100644
--- a/services/auth/source/smtp/source.go
+++ b/services/auth/source/smtp/source.go
@@ -4,8 +4,8 @@
package smtp
import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
)
// _________ __________________________
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index 1f0a61c789..3d7ccd0669 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -10,10 +10,10 @@ import (
"net/textproto"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/util"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/util"
)
// Authenticate queries if the provided login/password is authenticates against the SMTP server
diff --git a/services/auth/source/source_group_sync.go b/services/auth/source/source_group_sync.go
index 3a2411ec55..46be6937fb 100644
--- a/services/auth/source/source_group_sync.go
+++ b/services/auth/source/source_group_sync.go
@@ -7,11 +7,11 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models"
+ "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
)
type syncType int
diff --git a/services/auth/source/sspi/assert_interface_test.go b/services/auth/source/sspi/assert_interface_test.go
deleted file mode 100644
index 03d836dd6f..0000000000
--- a/services/auth/source/sspi/assert_interface_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package sspi_test
-
-import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/services/auth/source/sspi"
-)
-
-// This test file exists to assert that our Source exposes the interfaces that we expect
-// It tightly binds the interfaces and implementation without breaking go import cycles
-
-type sourceInterface interface {
- auth.Config
-}
-
-var _ (sourceInterface) = &sspi.Source{}
diff --git a/services/auth/source/sspi/source.go b/services/auth/source/sspi/source.go
deleted file mode 100644
index bdd6ef451c..0000000000
--- a/services/auth/source/sspi/source.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package sspi
-
-import (
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
-)
-
-// _________ ___________________.___
-// / _____// _____/\______ \ |
-// \_____ \ \_____ \ | ___/ |
-// / \/ \ | | | |
-// /_______ /_______ / |____| |___|
-// \/ \/
-
-// Source holds configuration for SSPI single sign-on.
-type Source struct {
- AutoCreateUsers bool
- AutoActivateUsers bool
- StripDomainNames bool
- SeparatorReplacement string
- DefaultLanguage string
-}
-
-// FromDB fills up an SSPIConfig from serialized format.
-func (cfg *Source) FromDB(bs []byte) error {
- return json.UnmarshalHandleDoubleEncode(bs, &cfg)
-}
-
-// ToDB exports an SSPIConfig to a serialized format.
-func (cfg *Source) ToDB() ([]byte, error) {
- return json.Marshal(cfg)
-}
-
-func init() {
- auth.RegisterTypeConfig(auth.SSPI, &Source{})
-}
diff --git a/services/auth/sspi.go b/services/auth/sspi.go
deleted file mode 100644
index 64a127e97a..0000000000
--- a/services/auth/sspi.go
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package auth
-
-import (
- "context"
- "errors"
- "net/http"
- "strings"
- "sync"
-
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/auth/source/sspi"
- gitea_context "code.gitea.io/gitea/services/context"
-
- gouuid "github.com/google/uuid"
-)
-
-const (
- tplSignIn base.TplName = "user/auth/signin"
-)
-
-type SSPIAuth interface {
- AppendAuthenticateHeader(w http.ResponseWriter, data string)
- Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error)
-}
-
-var (
- sspiAuth SSPIAuth // a global instance of the websspi authenticator to avoid acquiring the server credential handle on every request
- sspiAuthOnce sync.Once
- sspiAuthErrInit error
-
- // Ensure the struct implements the interface.
- _ Method = &SSPI{}
-)
-
-// SSPI implements the SingleSignOn interface and authenticates requests
-// via the built-in SSPI module in Windows for SPNEGO authentication.
-// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation
-// fails (or if negotiation should continue), which would prevent other authentication methods
-// to execute at all.
-type SSPI struct{}
-
-// Name represents the name of auth method
-func (s *SSPI) Name() string {
- return "sspi"
-}
-
-// Verify uses SSPI (Windows implementation of SPNEGO) to authenticate the request.
-// If authentication is successful, returns the corresponding user object.
-// If negotiation should continue or authentication fails, immediately returns a 401 HTTP
-// response code, as required by the SPNEGO protocol.
-func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
- sspiAuthOnce.Do(func() { sspiAuthErrInit = sspiAuthInit() })
- if sspiAuthErrInit != nil {
- return nil, sspiAuthErrInit
- }
- if !s.shouldAuthenticate(req) {
- return nil, nil
- }
-
- cfg, err := s.getConfig(req.Context())
- if err != nil {
- log.Error("could not get SSPI config: %v", err)
- return nil, err
- }
-
- log.Trace("SSPI Authorization: Attempting to authenticate")
- userInfo, outToken, err := sspiAuth.Authenticate(req, w)
- if err != nil {
- log.Warn("Authentication failed with error: %v\n", err)
- sspiAuth.AppendAuthenticateHeader(w, outToken)
-
- // Include the user login page in the 401 response to allow the user
- // to login with another authentication method if SSPI authentication
- // fails
- store.GetData()["Flash"] = map[string]string{
- "ErrorMsg": err.Error(),
- }
- store.GetData()["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
- store.GetData()["EnableSSPI"] = true
- // in this case, the Verify function is called in Gitea's web context
- // FIXME: it doesn't look good to render the page here, why not redirect?
- gitea_context.GetWebContext(req).HTML(http.StatusUnauthorized, tplSignIn)
- return nil, err
- }
- if outToken != "" {
- sspiAuth.AppendAuthenticateHeader(w, outToken)
- }
-
- username := sanitizeUsername(userInfo.Username, cfg)
- if len(username) == 0 {
- return nil, nil
- }
- log.Info("Authenticated as %s\n", username)
-
- user, err := user_model.GetUserByName(req.Context(), username)
- if err != nil {
- if !user_model.IsErrUserNotExist(err) {
- log.Error("GetUserByName: %v", err)
- return nil, err
- }
- if !cfg.AutoCreateUsers {
- log.Error("User '%s' not found", username)
- return nil, nil
- }
- user, err = s.newUser(req.Context(), username, cfg)
- if err != nil {
- log.Error("CreateUser: %v", err)
- return nil, err
- }
- }
-
- // Make sure requests to API paths and PWA resources do not create a new session
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) {
- handleSignIn(w, req, sess, user)
- }
-
- log.Trace("SSPI Authorization: Logged in user %-v", user)
- return user, nil
-}
-
-// getConfig retrieves the SSPI configuration from login sources
-func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) {
- sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
- IsActive: optional.Some(true),
- LoginType: auth.SSPI,
- })
- if err != nil {
- return nil, err
- }
- if len(sources) == 0 {
- return nil, errors.New("no active login sources of type SSPI found")
- }
- if len(sources) > 1 {
- return nil, errors.New("more than one active login source of type SSPI found")
- }
- return sources[0].Cfg.(*sspi.Source), nil
-}
-
-func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
- shouldAuth = false
- path := strings.TrimSuffix(req.URL.Path, "/")
- if path == "/user/login" {
- if req.FormValue("user_name") != "" && req.FormValue("password") != "" {
- shouldAuth = false
- } else if req.FormValue("auth_with_sspi") == "1" {
- shouldAuth = true
- }
- } else if middleware.IsAPIPath(req) || isAttachmentDownload(req) {
- shouldAuth = true
- }
- return shouldAuth
-}
-
-// newUser creates a new user object for the purpose of automatic registration
-// and populates its name and email with the information present in request headers.
-func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (*user_model.User, error) {
- email := gouuid.New().String() + "@localhost.localdomain"
- user := &user_model.User{
- Name: username,
- Email: email,
- Language: cfg.DefaultLanguage,
- }
- emailNotificationPreference := user_model.EmailNotificationsDisabled
- overwriteDefault := &user_model.CreateUserOverwriteOptions{
- IsActive: optional.Some(cfg.AutoActivateUsers),
- KeepEmailPrivate: optional.Some(true),
- EmailNotificationsPreference: &emailNotificationPreference,
- }
- if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil {
- return nil, err
- }
-
- return user, nil
-}
-
-// stripDomainNames removes NETBIOS domain name and separator from down-level logon names
-// (eg. "DOMAIN\user" becomes "user"), and removes the UPN suffix (domain name) and separator
-// from UPNs (eg. "user@domain.local" becomes "user")
-func stripDomainNames(username string) string {
- if strings.Contains(username, "\\") {
- parts := strings.SplitN(username, "\\", 2)
- if len(parts) > 1 {
- username = parts[1]
- }
- } else if strings.Contains(username, "@") {
- parts := strings.Split(username, "@")
- if len(parts) > 1 {
- username = parts[0]
- }
- }
- return username
-}
-
-func replaceSeparators(username string, cfg *sspi.Source) string {
- newSep := cfg.SeparatorReplacement
- username = strings.ReplaceAll(username, "\\", newSep)
- username = strings.ReplaceAll(username, "/", newSep)
- username = strings.ReplaceAll(username, "@", newSep)
- return username
-}
-
-func sanitizeUsername(username string, cfg *sspi.Source) string {
- if len(username) == 0 {
- return ""
- }
- if cfg.StripDomainNames {
- username = stripDomainNames(username)
- }
- // Replace separators even if we have already stripped the domain name part,
- // as the username can contain several separators: eg. "MICROSOFT\useremail@live.com"
- username = replaceSeparators(username, cfg)
- return username
-}
diff --git a/services/auth/sspiauth_posix.go b/services/auth/sspiauth_posix.go
deleted file mode 100644
index 49b0ed4a52..0000000000
--- a/services/auth/sspiauth_posix.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build !windows
-
-package auth
-
-import (
- "errors"
- "net/http"
-)
-
-type SSPIUserInfo struct {
- Username string // Name of user, usually in the form DOMAIN\User
- Groups []string // The global groups the user is a member of
-}
-
-type sspiAuthMock struct{}
-
-func (s sspiAuthMock) AppendAuthenticateHeader(w http.ResponseWriter, data string) {
-}
-
-func (s sspiAuthMock) Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) {
- return nil, "", errors.New("not implemented")
-}
-
-func sspiAuthInit() error {
- sspiAuth = &sspiAuthMock{} // TODO: we can mock the SSPI auth in tests
- return nil
-}
diff --git a/services/auth/sync.go b/services/auth/sync.go
index 7562ac812b..c594be7a24 100644
--- a/services/auth/sync.go
+++ b/services/auth/sync.go
@@ -6,9 +6,9 @@ package auth
import (
"context"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
)
// SyncExternalUsers is used to synchronize users with external authorization source
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index d3cc4c6fb1..cbfe3bd54e 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -8,22 +8,22 @@ import (
"errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
- shared_automerge "code.gitea.io/gitea/services/shared/automerge"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
+ shared_automerge "forgejo.org/services/shared/automerge"
)
// Init runs the task queue to that handles auto merges
@@ -32,7 +32,7 @@ func Init() error {
shared_automerge.PRAutoMergeQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_auto_merge", handler)
if shared_automerge.PRAutoMergeQueue == nil {
- return fmt.Errorf("unable to create pr_auto_merge queue")
+ return errors.New("unable to create pr_auto_merge queue")
}
go graceful.GetManager().RunWithCancel(shared_automerge.PRAutoMergeQueue)
return nil
@@ -107,6 +107,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
return
}
if !exists {
+ log.Trace("GetScheduledMergeByPullID found nothing for PR %d", pullID)
return
}
@@ -204,6 +205,10 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
return
}
+ if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
+ log.Error("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
+ }
+
if err := pull_service.Merge(ctx, pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message, true); err != nil {
log.Error("pull_service.Merge: %v", err)
// FIXME: if merge failed, we should display some error message to the pull request page.
diff --git a/services/automerge/notify.go b/services/automerge/notify.go
index cb078214f6..3b5eae9d48 100644
--- a/services/automerge/notify.go
+++ b/services/automerge/notify.go
@@ -6,10 +6,10 @@ package automerge
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- notify_service "code.gitea.io/gitea/services/notify"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ notify_service "forgejo.org/services/notify"
)
type automergeNotifier struct {
diff --git a/services/context/access_log.go b/services/context/access_log.go
index 0926748ac5..7a54b746f6 100644
--- a/services/context/access_log.go
+++ b/services/context/access_log.go
@@ -12,10 +12,10 @@ import (
"text/template"
"time"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web/middleware"
)
type routerLoggerOptions struct {
diff --git a/services/context/api.go b/services/context/api.go
index 3289727425..e9f67c720d 100644
--- a/services/context/api.go
+++ b/services/context/api.go
@@ -6,23 +6,24 @@ package context
import (
"context"
+ "errors"
"fmt"
"net/http"
"net/url"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- mc "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- web_types "code.gitea.io/gitea/modules/web/types"
+ issues_model "forgejo.org/models/issues"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ mc "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/web"
+ web_types "forgejo.org/modules/web/types"
"code.forgejo.org/go-chi/cache"
)
@@ -157,6 +158,17 @@ type swaggerAPIRepoArchivedError struct {
Body APIRepoArchivedError `json:"body"`
}
+type APIInternalServerError struct {
+ APIError
+}
+
+// APIInternalServerError is an error that is raised when an internal server error occurs
+// swagger:response internalServerError
+type swaggerAPIInternalServerError struct {
+ // in:body
+ Body APIInternalServerError `json:"body"`
+}
+
// ServerError responds with error message, status is 500
func (ctx *APIContext) ServerError(title string, err error) {
ctx.Error(http.StatusInternalServerError, title, err)
@@ -175,7 +187,7 @@ func (ctx *APIContext) Error(status int, title string, obj any) {
if status == http.StatusInternalServerError {
log.ErrorWithSkip(1, "%s: %s", title, message)
- if setting.IsProd && !(ctx.Doer != nil && ctx.Doer.IsAdmin) {
+ if setting.IsProd && (ctx.Doer == nil || !ctx.Doer.IsAdmin) {
message = ""
}
}
@@ -274,8 +286,8 @@ func APIContexter() func(http.Handler) http.Handler {
}
defer baseCleanUp()
- ctx.Base.AppendContextValue(apiContextKey, ctx)
- ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
+ ctx.AppendContextValue(apiContextKey, ctx)
+ ctx.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
@@ -323,7 +335,7 @@ func (ctx *APIContext) NotFound(objs ...any) {
func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) {
return func(ctx *APIContext) (cancel context.CancelFunc) {
// Empty repository does not have reference information.
- if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) {
+ if ctx.Repo.Repository.IsEmpty && (len(allowEmpty) == 0 || !allowEmpty[0]) {
return nil
}
@@ -354,12 +366,12 @@ func RepoRefForAPI(next http.Handler) http.Handler {
ctx := GetAPIContext(req)
if ctx.Repo.Repository.IsEmpty {
- ctx.NotFound(fmt.Errorf("repository is empty"))
+ ctx.NotFound(errors.New("repository is empty"))
return
}
if ctx.Repo.GitRepo == nil {
- ctx.InternalServerError(fmt.Errorf("no open git repo"))
+ ctx.InternalServerError(errors.New("no open git repo"))
return
}
diff --git a/services/context/api_org.go b/services/context/api_org.go
index dad02b1719..acc9594e48 100644
--- a/services/context/api_org.go
+++ b/services/context/api_org.go
@@ -3,7 +3,7 @@
package context
-import "code.gitea.io/gitea/models/organization"
+import "forgejo.org/models/organization"
// APIOrganization contains organization and team
type APIOrganization struct {
diff --git a/services/context/api_test.go b/services/context/api_test.go
index 6064fee1c3..4bc89939ca 100644
--- a/services/context/api_test.go
+++ b/services/context/api_test.go
@@ -8,14 +8,15 @@ import (
"strconv"
"testing"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGenAPILinks(t *testing.T) {
- setting.AppURL = "http://localhost:3000/"
+ defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/")()
kases := map[string][]string{
"api/v1/repos/jerrykan/example-repo/issues?state=all": {
`; rel="next"`,
@@ -46,6 +47,6 @@ func TestGenAPILinks(t *testing.T) {
links := genAPILinks(u, 100, 20, curPage)
- assert.EqualValues(t, links, response)
+ assert.Equal(t, links, response)
}
}
diff --git a/services/context/base.go b/services/context/base.go
index 0259e0d806..dc3d226bb0 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -14,12 +14,12 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web/middleware"
+ "forgejo.org/modules/httplib"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/web/middleware"
"github.com/go-chi/chi/v5"
)
@@ -250,7 +250,7 @@ func (b *Base) PlainText(status int, text string) {
// Redirect redirects the request
func (b *Base) Redirect(location string, status ...int) {
code := http.StatusSeeOther
- if len(status) == 1 {
+ if len(status) == 1 && status[0] > 0 {
code = status[0]
}
diff --git a/services/context/base_test.go b/services/context/base_test.go
index 823f20e00b..9e058d8f24 100644
--- a/services/context/base_test.go
+++ b/services/context/base_test.go
@@ -8,12 +8,14 @@ import (
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
)
func TestRedirect(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/")()
req, _ := http.NewRequest("GET", "/", nil)
cases := []struct {
@@ -34,6 +36,7 @@ func TestRedirect(t *testing.T) {
cleanup()
has := resp.Header().Get("Set-Cookie") == "i_like_gitea=dummy"
assert.Equal(t, c.keep, has, "url = %q", c.url)
+ assert.Equal(t, http.StatusSeeOther, resp.Code)
}
req, _ = http.NewRequest("GET", "/", nil)
@@ -45,3 +48,24 @@ func TestRedirect(t *testing.T) {
assert.Equal(t, "/other", resp.Header().Get("HX-Redirect"))
assert.Equal(t, http.StatusNoContent, resp.Code)
}
+
+func TestRedirectOptionalStatus(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/")()
+ req, _ := http.NewRequest("GET", "/", nil)
+
+ cases := []struct {
+ expected int
+ actual int
+ }{
+ {expected: 303},
+ {http.StatusTemporaryRedirect, 307},
+ {http.StatusPermanentRedirect, 308},
+ }
+ for _, c := range cases {
+ resp := httptest.NewRecorder()
+ b, cleanup := NewBaseContext(resp, req)
+ b.Redirect("/", c.actual)
+ cleanup()
+ assert.Equal(t, c.expected, resp.Code)
+ }
+}
diff --git a/services/context/captcha.go b/services/context/captcha.go
index da837acb00..8ae8bdcae3 100644
--- a/services/context/captcha.go
+++ b/services/context/captcha.go
@@ -7,14 +7,14 @@ import (
"fmt"
"sync"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/hcaptcha"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/mcaptcha"
- "code.gitea.io/gitea/modules/recaptcha"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/turnstile"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/hcaptcha"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/mcaptcha"
+ "forgejo.org/modules/recaptcha"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/turnstile"
mc "code.forgejo.org/go-chi/cache"
"code.forgejo.org/go-chi/captcha"
diff --git a/services/context/context.go b/services/context/context.go
index 91e7b1849d..1a839773a8 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -15,17 +15,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- mc "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- web_types "code.gitea.io/gitea/modules/web/types"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ mc "forgejo.org/modules/cache"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/httpcache"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/web"
+ "forgejo.org/modules/web/middleware"
+ web_types "forgejo.org/modules/web/types"
"code.forgejo.org/go-chi/cache"
"code.forgejo.org/go-chi/session"
@@ -100,7 +100,7 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
func NewTemplateContextForWeb(ctx *Context) TemplateContext {
tmplCtx := NewTemplateContext(ctx)
- tmplCtx["Locale"] = ctx.Base.Locale
+ tmplCtx["Locale"] = ctx.Locale
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
return tmplCtx
}
@@ -121,6 +121,18 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
return ctx
}
+func (ctx *Context) AddPluralStringsToPageData(keys []string) {
+ for _, key := range keys {
+ array, fallback := ctx.Locale.TrPluralStringAllForms(key)
+
+ ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)[key] = array
+
+ if fallback != nil {
+ ctx.PageData["PLURALSTRINGS_FALLBACK"].(map[string][]string)[key] = fallback
+ }
+ }
+}
+
// Contexter initializes a classic context for a request.
func Contexter() func(next http.Handler) http.Handler {
rnd := templates.HTMLRenderer()
@@ -151,8 +163,8 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.PageData = map[string]any{}
ctx.Data["PageData"] = ctx.PageData
- ctx.Base.AppendContextValue(WebContextKey, ctx)
- ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
+ ctx.AppendContextValue(WebContextKey, ctx)
+ ctx.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
ctx.Csrf = NewCSRFProtector(csrfOpts)
@@ -208,6 +220,25 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Data["AllLangs"] = translation.AllLangs()
+ ctx.PageData["PLURAL_RULE_LANG"] = translation.GetPluralRule(ctx.Locale)
+ ctx.PageData["PLURAL_RULE_FALLBACK"] = translation.GetDefaultPluralRule()
+ ctx.PageData["PLURALSTRINGS_LANG"] = map[string][]string{}
+ ctx.PageData["PLURALSTRINGS_FALLBACK"] = map[string][]string{}
+
+ ctx.AddPluralStringsToPageData([]string{"relativetime.mins", "relativetime.hours", "relativetime.days", "relativetime.weeks", "relativetime.months", "relativetime.years"})
+
+ ctx.PageData["DATETIMESTRINGS"] = map[string]string{
+ "FUTURE": ctx.Locale.TrString("relativetime.future"),
+ "NOW": ctx.Locale.TrString("relativetime.now"),
+ }
+ for _, key := range []string{"relativetime.1day", "relativetime.1week", "relativetime.1month", "relativetime.1year", "relativetime.2days", "relativetime.2weeks", "relativetime.2months", "relativetime.2years"} {
+ // These keys are used for special-casing some time words. We only add keys that are actually translated, so that we
+ // can fall back to the generic pluralized time word in the correct language if the special case is untranslated.
+ if ctx.Locale.HasKey(key) {
+ ctx.PageData["DATETIMESTRINGS"].(map[string]string)[key] = ctx.Locale.TrString(key)
+ }
+ }
+
next.ServeHTTP(ctx.Resp, ctx.Req)
})
}
diff --git a/services/context/context_cookie.go b/services/context/context_cookie.go
index 3699f81071..08ef84b5eb 100644
--- a/services/context/context_cookie.go
+++ b/services/context/context_cookie.go
@@ -7,11 +7,11 @@ import (
"net/http"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web/middleware"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/web/middleware"
)
const CookieNameFlash = "gitea_flash"
diff --git a/services/context/context_model.go b/services/context/context_model.go
index 4f70aac516..1a8751ee63 100644
--- a/services/context/context_model.go
+++ b/services/context/context_model.go
@@ -4,7 +4,7 @@
package context
import (
- "code.gitea.io/gitea/models/unit"
+ "forgejo.org/models/unit"
)
// IsUserSiteAdmin returns true if current user is a site admin
diff --git a/services/context/context_response.go b/services/context/context_response.go
index f36b834a44..e64f478420 100644
--- a/services/context/context_response.go
+++ b/services/context/context_response.go
@@ -1,4 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package context
@@ -16,13 +17,13 @@ import (
"syscall"
"time"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web/middleware"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/httplib"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/web/middleware"
)
// RedirectToUser redirect to a differently-named user
@@ -66,7 +67,10 @@ func (ctx *Context) RedirectToFirst(location ...string) string {
return setting.AppSubURL + "/"
}
-const tplStatus500 base.TplName = "status/500"
+const (
+ tplStatus404 base.TplName = "status/404"
+ tplStatus500 base.TplName = "status/500"
+)
// HTML calls Context.HTML and renders the template to HTTP response
func (ctx *Context) HTML(status int, name base.TplName) {
@@ -153,8 +157,8 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
}
ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
- ctx.Data["Title"] = "Page Not Found"
- ctx.HTML(http.StatusNotFound, base.TplName("status/404"))
+ ctx.Data["Title"] = ctx.Locale.TrString("error.not_found.title")
+ ctx.HTML(http.StatusNotFound, tplStatus404)
}
// ServerError displays a 500 (Internal Server Error) page and prints the given error, if any.
@@ -177,7 +181,6 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
}
}
- ctx.Data["Title"] = "Internal Server Error"
ctx.HTML(http.StatusInternalServerError, tplStatus500)
}
diff --git a/services/context/context_test.go b/services/context/context_test.go
index 033ce2ef0a..c2a271d2b7 100644
--- a/services/context/context_test.go
+++ b/services/context/context_test.go
@@ -8,7 +8,7 @@ import (
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
)
diff --git a/services/context/csrf.go b/services/context/csrf.go
index 51127c6eb0..82dd9283ff 100644
--- a/services/context/csrf.go
+++ b/services/context/csrf.go
@@ -25,8 +25,8 @@ import (
"strconv"
"time"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
)
const (
diff --git a/services/context/org.go b/services/context/org.go
index 9673f2f5a9..3ddc40b6b3 100644
--- a/services/context/org.go
+++ b/services/context/org.go
@@ -1,5 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
-// Copyright 2020 The Gitea Authors.
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package context
@@ -7,14 +8,14 @@ package context
import (
"strings"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
)
// Organization contains organization context
@@ -165,6 +166,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+ ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
ctx.Data["IsPublicMember"] = func(uid int64) bool {
is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid)
return is
diff --git a/services/context/package.go b/services/context/package.go
index c452c657e7..b95e02a882 100644
--- a/services/context/package.go
+++ b/services/context/package.go
@@ -7,14 +7,14 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates"
+ "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/models/perm"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/templates"
)
// Package contains owner, access mode and optional the package descriptor
@@ -158,7 +158,7 @@ func PackageContexter() func(next http.Handler) http.Handler {
// it is still needed when rendering 500 page in a package handler
ctx := NewWebContext(base, renderer, nil)
- ctx.Base.AppendContextValue(WebContextKey, ctx)
+ ctx.AppendContextValue(WebContextKey, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req)
})
}
diff --git a/services/context/pagination.go b/services/context/pagination.go
index 655a278f9f..b826e59dea 100644
--- a/services/context/pagination.go
+++ b/services/context/pagination.go
@@ -9,7 +9,7 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/modules/paginator"
+ "forgejo.org/modules/paginator"
)
// Pagination provides a pagination via paginator.Paginator and additional configurations for the link params used in rendering
diff --git a/services/context/permission.go b/services/context/permission.go
index 14a9801dcc..b6af87f912 100644
--- a/services/context/permission.go
+++ b/services/context/permission.go
@@ -6,10 +6,10 @@ package context
import (
"net/http"
- auth_model "code.gitea.io/gitea/models/auth"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
+ auth_model "forgejo.org/models/auth"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
)
// RequireRepoAdmin returns a middleware for requiring repository admin permission
diff --git a/services/context/private.go b/services/context/private.go
index 8b41949f60..94ee31876a 100644
--- a/services/context/private.go
+++ b/services/context/private.go
@@ -9,10 +9,10 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/web"
- web_types "code.gitea.io/gitea/modules/web/types"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/web"
+ web_types "forgejo.org/modules/web/types"
)
// PrivateContext represents a context for private routes
@@ -67,7 +67,7 @@ func PrivateContexter() func(http.Handler) http.Handler {
base, baseCleanUp := NewBaseContext(w, req)
ctx := &PrivateContext{Base: base}
defer baseCleanUp()
- ctx.Base.AppendContextValue(privateContextKey, ctx)
+ ctx.AppendContextValue(privateContextKey, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req)
})
diff --git a/services/context/quota.go b/services/context/quota.go
index 94e8847696..502a316107 100644
--- a/services/context/quota.go
+++ b/services/context/quota.go
@@ -8,8 +8,8 @@ import (
"net/http"
"strings"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/modules/base"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/modules/base"
)
type QuotaTargetType int
@@ -64,7 +64,7 @@ func QuotaRuleAssignmentAPI() func(ctx *APIContext) {
// ctx.CheckQuota checks whether the user in question is within quota limits (web context)
func (ctx *Context) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool {
- ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) {
+ ok, err := checkQuota(ctx.originCtx, subject, userID, username, func(userID int64, username string) {
showHTML := false
for _, part := range ctx.Req.Header["Accept"] {
if strings.Contains(part, "text/html") {
@@ -91,7 +91,7 @@ func (ctx *Context) CheckQuota(subject quota_model.LimitSubject, userID int64, u
// ctx.CheckQuota checks whether the user in question is within quota limits (API context)
func (ctx *APIContext) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool {
- ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) {
+ ok, err := checkQuota(ctx.originCtx, subject, userID, username, func(userID int64, username string) {
ctx.JSON(http.StatusRequestEntityTooLarge, APIQuotaExceeded{
Message: "quota exceeded",
UserID: userID,
diff --git a/services/context/repo.go b/services/context/repo.go
index 71cf431273..c8876d7166 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -15,26 +15,26 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/card"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/card"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ code_indexer "forgejo.org/modules/indexer/code"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ asymkey_service "forgejo.org/services/asymkey"
"github.com/editorconfig/editorconfig-core-go/v2"
)
@@ -83,7 +83,7 @@ func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User)
// CanCreateBranch returns true if repository is editable and user has proper access level.
func (r *Repository) CanCreateBranch() bool {
- return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
+ return r.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
}
func (r *Repository) GetObjectFormat() git.ObjectFormat {
@@ -160,12 +160,12 @@ func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
isAssigned, _ := issues_model.IsUserAssignedToIssue(ctx, issue, user)
return r.Repository.IsTimetrackerEnabled(ctx) && (!r.Repository.AllowOnlyContributorsToTrackTime(ctx) ||
- r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
+ r.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
}
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_model.User, isPull bool) bool {
- return r.Repository.IsDependenciesEnabled(ctx) && r.Permission.CanWriteIssuesOrPulls(isPull)
+ return r.Repository.IsDependenciesEnabled(ctx) && r.CanWriteIssuesOrPulls(isPull)
}
// GetCommitsCount returns cached commit count for current view
@@ -361,7 +361,9 @@ func RedirectToRepo(ctx *Base, redirectRepoID int64) {
if ctx.Req.URL.RawQuery != "" {
redirectPath += "?" + ctx.Req.URL.RawQuery
}
- ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
+ // Git client needs a 301 redirect by default to follow the new location
+ // It's not documentated in git documentation, but it's the behavior of git client
+ ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusMovedPermanently)
}
func repoAssignment(ctx *Context, repo *repo_model.Repository) {
@@ -378,7 +380,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
}
// Check access.
- if !ctx.Repo.Permission.HasAccess() {
+ if !ctx.Repo.HasAccess() {
if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return
@@ -591,6 +593,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions)
+ ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
if err != nil {
@@ -634,10 +637,18 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
}
cardWidth, cardHeight := card.DefaultSize()
+ ctx.Data["OpenGraphTitle"] = repo.Name
+ ctx.Data["OpenGraphURL"] = repo.HTMLURL()
+ ctx.Data["OpenGraphType"] = "object"
+ ctx.Data["OpenGraphDescription"] = repo.Description
ctx.Data["OpenGraphImageURL"] = repo.SummaryCardURL()
ctx.Data["OpenGraphImageWidth"] = cardWidth
ctx.Data["OpenGraphImageHeight"] = cardHeight
- ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName())
+ if util.IsEmptyString(repo.Description) {
+ ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName())
+ } else {
+ ctx.Data["OpenGraphImageAltText"] = ctx.Tr("og.repo.summary_card.alt_description", repo.FullName(), repo.Description)
+ }
if repo.IsFork {
RetrieveBaseRepo(ctx, repo)
@@ -933,6 +944,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
// of repository reference
func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc {
return func(ctx *Context) (cancel context.CancelFunc) {
+ if ctx.Repo.Repository.IsBeingCreated() {
+ return nil // no git repo, so do nothing
+ }
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty {
// assume the user is viewing the (non-existent) default branch
@@ -1051,7 +1065,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
if refType == RepoRefLegacy {
// redirect from old URL scheme to new URL scheme
- prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.PathParamRaw("*"))), strings.ToLower(ctx.Repo.RepoLink))
+ prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*"))), strings.ToLower(ctx.Repo.RepoLink))
ctx.Redirect(path.Join(
ctx.Repo.RepoLink,
diff --git a/services/context/repository.go b/services/context/repository.go
index 422ac3f58d..7eef2c5068 100644
--- a/services/context/repository.go
+++ b/services/context/repository.go
@@ -6,7 +6,7 @@ package context
import (
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
+ repo_model "forgejo.org/models/repo"
)
// RepositoryIDAssignmentAPI returns a middleware to handle context-repo assignment for api routes
diff --git a/services/context/response.go b/services/context/response.go
index 2f271f211b..8fc631e671 100644
--- a/services/context/response.go
+++ b/services/context/response.go
@@ -6,7 +6,7 @@ package context
import (
"net/http"
- web_types "code.gitea.io/gitea/modules/web/types"
+ web_types "forgejo.org/modules/web/types"
)
// ResponseWriter represents a response writer for HTTP
diff --git a/services/context/upload/upload.go b/services/context/upload/upload.go
index 77a7eb9377..e71fc50c1f 100644
--- a/services/context/upload/upload.go
+++ b/services/context/upload/upload.go
@@ -11,9 +11,9 @@ import (
"regexp"
"strings"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/context"
)
// ErrFileTypeForbidden not allowed file type error
@@ -76,14 +76,15 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error {
// AddUploadContext renders template values for dropzone
func AddUploadContext(ctx *context.Context, uploadType string) {
- if uploadType == "release" {
+ switch uploadType {
+ case "release":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove"
ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",")
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "comment" {
+ case "comment":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
if len(ctx.Params(":index")) > 0 {
@@ -94,7 +95,7 @@ func AddUploadContext(ctx *context.Context, uploadType string) {
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "repo" {
+ case "repo":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
diff --git a/services/context/user.go b/services/context/user.go
index 4c9cd2928b..a82c90d7a6 100644
--- a/services/context/user.go
+++ b/services/context/user.go
@@ -8,7 +8,7 @@ import (
"net/http"
"strings"
- user_model "code.gitea.io/gitea/models/user"
+ user_model "forgejo.org/models/user"
)
// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index 7c829f3598..a4e674a896 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -15,16 +15,16 @@ import (
"testing"
"time"
- org_model "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ org_model "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
@@ -68,7 +68,7 @@ func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*cont
ctx.PageData = map[string]any{}
ctx.Data["PageStartTime"] = time.Now()
chiCtx := chi.NewRouteContext()
- ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+ ctx.AppendContextValue(chi.RouteCtxKey, chiCtx)
return ctx, resp
}
@@ -83,7 +83,7 @@ func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptes
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
chiCtx := chi.NewRouteContext()
- ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+ ctx.AppendContextValue(chi.RouteCtxKey, chiCtx)
return ctx, resp
}
@@ -96,7 +96,7 @@ func MockPrivateContext(t *testing.T, reqPath string) (*context.PrivateContext,
ctx := &context.PrivateContext{Base: base}
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
chiCtx := chi.NewRouteContext()
- ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+ ctx.AppendContextValue(chi.RouteCtxKey, chiCtx)
return ctx, resp
}
diff --git a/services/contexttest/pagedata_test.go b/services/contexttest/pagedata_test.go
new file mode 100644
index 0000000000..0c9319b6db
--- /dev/null
+++ b/services/contexttest/pagedata_test.go
@@ -0,0 +1,63 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package contexttest
+
+import (
+ "testing"
+
+ "forgejo.org/modules/translation"
+ "forgejo.org/modules/translation/i18n"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPluralStringsForClient(t *testing.T) {
+ mockLocale := translation.MockLocale{}
+ mockLocale.MockTranslations = map[string]string{
+ "relativetime.mins" + i18n.PluralFormSeparator + "one": "%d minute ago",
+ "relativetime.hours" + i18n.PluralFormSeparator + "one": "%d hour ago",
+ "relativetime.days" + i18n.PluralFormSeparator + "one": "%d day ago",
+ "relativetime.weeks" + i18n.PluralFormSeparator + "one": "%d week ago",
+ "relativetime.months" + i18n.PluralFormSeparator + "one": "%d month ago",
+ "relativetime.years" + i18n.PluralFormSeparator + "one": "%d year ago",
+ "relativetime.mins" + i18n.PluralFormSeparator + "other": "%d minutes ago",
+ "relativetime.hours" + i18n.PluralFormSeparator + "other": "%d hours ago",
+ "relativetime.days" + i18n.PluralFormSeparator + "other": "%d days ago",
+ "relativetime.weeks" + i18n.PluralFormSeparator + "other": "%d weeks ago",
+ "relativetime.months" + i18n.PluralFormSeparator + "other": "%d months ago",
+ "relativetime.years" + i18n.PluralFormSeparator + "other": "%d years ago",
+ }
+
+ ctx, _ := MockContext(t, "/")
+ ctx.Locale = mockLocale
+ assert.True(t, ctx.Locale.HasKey("relativetime.mins"))
+ assert.True(t, ctx.Locale.HasKey("relativetime.weeks"))
+ assert.Equal(t, "%d minutes ago", ctx.Locale.TrString("relativetime.mins"+i18n.PluralFormSeparator+"other"))
+ assert.Equal(t, "%d week ago", ctx.Locale.TrString("relativetime.weeks"+i18n.PluralFormSeparator+"one"))
+
+ assert.Empty(t, ctx.PageData)
+ ctx.PageData["PLURALSTRINGS_LANG"] = map[string][]string{}
+ assert.Empty(t, ctx.PageData["PLURALSTRINGS_LANG"])
+
+ ctx.AddPluralStringsToPageData([]string{"relativetime.mins", "relativetime.hours"})
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"], 2)
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"], 2)
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.hours"], 2)
+ assert.Equal(t, "%d minute ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"][0])
+ assert.Equal(t, "%d minutes ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"][1])
+ assert.Equal(t, "%d hour ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.hours"][0])
+ assert.Equal(t, "%d hours ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.hours"][1])
+
+ ctx.AddPluralStringsToPageData([]string{"relativetime.years", "relativetime.days"})
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"], 4)
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"], 2)
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.days"], 2)
+ assert.Len(t, ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.years"], 2)
+ assert.Equal(t, "%d minute ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"][0])
+ assert.Equal(t, "%d minutes ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.mins"][1])
+ assert.Equal(t, "%d day ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.days"][0])
+ assert.Equal(t, "%d days ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.days"][1])
+ assert.Equal(t, "%d year ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.years"][0])
+ assert.Equal(t, "%d years ago", ctx.PageData["PLURALSTRINGS_LANG"].(map[string][]string)["relativetime.years"][1])
+}
diff --git a/services/convert/action.go b/services/convert/action.go
new file mode 100644
index 0000000000..703c1f1261
--- /dev/null
+++ b/services/convert/action.go
@@ -0,0 +1,49 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package convert
+
+import (
+ "context"
+
+ actions_model "forgejo.org/models/actions"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
+)
+
+// ToActionRun convert actions_model.User to api.ActionRun
+// the run needs all attributes loaded
+func ToActionRun(ctx context.Context, run *actions_model.ActionRun, doer *user_model.User) *api.ActionRun {
+ if run == nil {
+ return nil
+ }
+
+ permissionInRepo, _ := access_model.GetUserRepoPermission(ctx, run.Repo, doer)
+
+ return &api.ActionRun{
+ ID: run.ID,
+ Title: run.Title,
+ Repo: ToRepo(ctx, run.Repo, permissionInRepo),
+ WorkflowID: run.WorkflowID,
+ Index: run.Index,
+ TriggerUser: ToUser(ctx, run.TriggerUser, doer),
+ ScheduleID: run.ScheduleID,
+ PrettyRef: run.PrettyRef(),
+ IsRefDeleted: run.IsRefDeleted,
+ CommitSHA: run.CommitSHA,
+ IsForkPullRequest: run.IsForkPullRequest,
+ NeedApproval: run.NeedApproval,
+ ApprovedBy: run.ApprovedBy,
+ Event: run.Event.Event(),
+ EventPayload: run.EventPayload,
+ TriggerEvent: run.TriggerEvent,
+ Status: run.Status.String(),
+ Started: run.Started.AsTime(),
+ Stopped: run.Stopped.AsTime(),
+ Created: run.Created.AsTime(),
+ Updated: run.Updated.AsTime(),
+ Duration: run.Duration(),
+ HTMLURL: run.HTMLURL(),
+ }
+}
diff --git a/services/convert/activity.go b/services/convert/activity.go
index 01fef73e58..213db13772 100644
--- a/services/convert/activity.go
+++ b/services/convert/activity.go
@@ -6,12 +6,12 @@ package convert
import (
"context"
- activities_model "code.gitea.io/gitea/models/activities"
- perm_model "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
+ activities_model "forgejo.org/models/activities"
+ perm_model "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
)
func ToActivity(ctx context.Context, ac *activities_model.Action, doer *user_model.User) *api.Activity {
diff --git a/services/convert/attachment.go b/services/convert/attachment.go
index d632c94c18..74ae7c509c 100644
--- a/services/convert/attachment.go
+++ b/services/convert/attachment.go
@@ -4,8 +4,11 @@
package convert
import (
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
+ "mime"
+ "path/filepath"
+
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
)
func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string {
@@ -20,9 +23,13 @@ func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachm
return attach.DownloadURL()
}
-// ToAttachment converts models.Attachment to api.Attachment for API usage
-func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
- return toAttachment(repo, a, WebAssetDownloadURL)
+// ToWebAttachment converts models.Attachment to api.WebAttachment for API usage
+func ToWebAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.WebAttachment {
+ attachment := toAttachment(repo, a, WebAssetDownloadURL)
+ return &api.WebAttachment{
+ Attachment: attachment,
+ MimeType: mime.TypeByExtension(filepath.Ext(attachment.Name)),
+ }
}
// ToAPIAttachment converts models.Attachment to api.Attachment for API usage
diff --git a/services/convert/attachment_test.go b/services/convert/attachment_test.go
new file mode 100644
index 0000000000..d7bf0c1ee7
--- /dev/null
+++ b/services/convert/attachment_test.go
@@ -0,0 +1,56 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package convert
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestToWebAttachment(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ attachment := &repo_model.Attachment{
+ ID: 10,
+ UUID: "uuidxxx",
+ RepoID: 1,
+ IssueID: 1,
+ ReleaseID: 0,
+ UploaderID: 0,
+ CommentID: 0,
+ Name: "test.png",
+ DownloadCount: 90,
+ Size: 30,
+ NoAutoTime: false,
+ CreatedUnix: 9342,
+ CustomDownloadURL: "",
+ ExternalURL: "",
+ }
+
+ webAttachment := ToWebAttachment(headRepo, attachment)
+
+ assert.NotNil(t, webAttachment)
+ assert.Equal(t, &api.WebAttachment{
+ Attachment: &api.Attachment{
+ ID: 10,
+ Name: "test.png",
+ Created: time.Unix(9342, 0),
+ DownloadCount: 90,
+ Size: 30,
+ UUID: "uuidxxx",
+ DownloadURL: fmt.Sprintf("%sattachments/uuidxxx", setting.AppURL),
+ Type: "attachment",
+ },
+ MimeType: "image/png",
+ }, webAttachment)
+}
diff --git a/services/convert/convert.go b/services/convert/convert.go
index 7a094494e4..2ea24a1b51 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -11,24 +11,24 @@ import (
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/gitdiff"
+ actions_model "forgejo.org/models/actions"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/gitdiff"
)
// ToEmail convert models.EmailAddress to api.Email
diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go
index e0efcddbcb..4603cfac4d 100644
--- a/services/convert/git_commit.go
+++ b/services/convert/git_commit.go
@@ -8,14 +8,14 @@ import (
"net/url"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- ctx "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/gitdiff"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ ctx "forgejo.org/services/context"
+ "forgejo.org/services/gitdiff"
)
// ToCommitUser convert a git.Signature to an api.CommitUser
@@ -210,7 +210,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
// Get diff stats for commit
if opts.Stat {
- diff, err := gitdiff.GetDiff(ctx, gitRepo, &gitdiff.DiffOptions{
+ diff, _, err := gitdiff.GetDiffSimple(ctx, gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commit.ID.String(),
})
if err != nil {
diff --git a/services/convert/git_commit_test.go b/services/convert/git_commit_test.go
index 68d1b05168..97dff365e6 100644
--- a/services/convert/git_commit_test.go
+++ b/services/convert/git_commit_test.go
@@ -7,11 +7,11 @@ import (
"testing"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -34,7 +34,7 @@ func TestToCommitMeta(t *testing.T) {
commitMeta := ToCommitMeta(headRepo, tag)
assert.NotNil(t, commitMeta)
- assert.EqualValues(t, &api.CommitMeta{
+ assert.Equal(t, &api.CommitMeta{
SHA: sha1.EmptyObjectID().String(),
URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.EmptyObjectID().String()),
Created: time.Unix(0, 0),
diff --git a/services/convert/issue.go b/services/convert/issue.go
index f514dc4313..c7803794d0 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -9,13 +9,13 @@ import (
"net/url"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/label"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
)
func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go
index 9ec9ac7684..9ea315aee6 100644
--- a/services/convert/issue_comment.go
+++ b/services/convert/issue_comment.go
@@ -6,12 +6,12 @@ package convert
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
)
// ToAPIComment converts a issues_model.Comment to the api.Comment format for API usage
diff --git a/services/convert/issue_test.go b/services/convert/issue_test.go
index 0aeb3e5612..97bacfb229 100644
--- a/services/convert/issue_test.go
+++ b/services/convert/issue_test.go
@@ -8,12 +8,12 @@ import (
"testing"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/convert/main_test.go b/services/convert/main_test.go
index b28b8f9446..5915d16be4 100644
--- a/services/convert/main_test.go
+++ b/services/convert/main_test.go
@@ -6,10 +6,10 @@ package convert
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/convert/mirror.go b/services/convert/mirror.go
index 85e0d1c856..9e7d2659ab 100644
--- a/services/convert/mirror.go
+++ b/services/convert/mirror.go
@@ -6,8 +6,8 @@ package convert
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
)
// ToPushMirror convert from repo_model.PushMirror and remoteAddress to api.TopicResponse
diff --git a/services/convert/notification.go b/services/convert/notification.go
index 41063cf399..2a69b62e4b 100644
--- a/services/convert/notification.go
+++ b/services/convert/notification.go
@@ -7,17 +7,17 @@ import (
"context"
"net/url"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- api "code.gitea.io/gitea/modules/structs"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ api "forgejo.org/modules/structs"
)
// ToNotificationThread convert a Notification to api.NotificationThread
func ToNotificationThread(ctx context.Context, n *activities_model.Notification) *api.NotificationThread {
result := &api.NotificationThread{
ID: n.ID,
- Unread: !(n.Status == activities_model.NotificationStatusRead || n.Status == activities_model.NotificationStatusPinned),
+ Unread: n.Status != activities_model.NotificationStatusRead && n.Status != activities_model.NotificationStatusPinned,
Pinned: n.Status == activities_model.NotificationStatusPinned,
UpdatedAt: n.UpdatedUnix.AsTime(),
URL: n.APIURL(),
diff --git a/services/convert/package.go b/services/convert/package.go
index b5fca21a3c..a28e60e1b1 100644
--- a/services/convert/package.go
+++ b/services/convert/package.go
@@ -6,10 +6,10 @@ package convert
import (
"context"
- "code.gitea.io/gitea/models/packages"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
)
// ToPackage convert a packages.PackageDescriptor to api.Package
diff --git a/services/convert/pull.go b/services/convert/pull.go
index 70dc22445a..ca965a0d18 100644
--- a/services/convert/pull.go
+++ b/services/convert/pull.go
@@ -7,15 +7,15 @@ import (
"context"
"fmt"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
)
// ToAPIPullRequest assumes following fields have been assigned with valid values:
@@ -66,33 +66,36 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
}
apiPullRequest := &api.PullRequest{
- ID: pr.ID,
- URL: pr.Issue.HTMLURL(),
- Index: pr.Index,
- Poster: apiIssue.Poster,
- Title: apiIssue.Title,
- Body: apiIssue.Body,
- Labels: apiIssue.Labels,
- Milestone: apiIssue.Milestone,
- Assignee: apiIssue.Assignee,
- Assignees: apiIssue.Assignees,
- State: apiIssue.State,
- Draft: pr.IsWorkInProgress(ctx),
- IsLocked: apiIssue.IsLocked,
- Comments: apiIssue.Comments,
- ReviewComments: pr.GetReviewCommentsCount(ctx),
- HTMLURL: pr.Issue.HTMLURL(),
- DiffURL: pr.Issue.DiffURL(),
- PatchURL: pr.Issue.PatchURL(),
- HasMerged: pr.HasMerged,
- MergeBase: pr.MergeBase,
- Mergeable: pr.Mergeable(ctx),
- Deadline: apiIssue.Deadline,
- Created: pr.Issue.CreatedUnix.AsTimePtr(),
- Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
- PinOrder: apiIssue.PinOrder,
+ ID: pr.ID,
+ URL: pr.Issue.HTMLURL(),
+ Index: pr.Index,
+ Poster: apiIssue.Poster,
+ Title: apiIssue.Title,
+ Body: apiIssue.Body,
+ Labels: apiIssue.Labels,
+ Milestone: apiIssue.Milestone,
+ Assignee: apiIssue.Assignee,
+ Assignees: apiIssue.Assignees,
+ State: apiIssue.State,
+ Draft: pr.IsWorkInProgress(ctx),
+ IsLocked: apiIssue.IsLocked,
+ Comments: apiIssue.Comments,
+ ReviewComments: pr.GetReviewCommentsCount(ctx),
+ HTMLURL: pr.Issue.HTMLURL(),
+ DiffURL: pr.Issue.DiffURL(),
+ PatchURL: pr.Issue.PatchURL(),
+ HasMerged: pr.HasMerged,
+ MergeBase: pr.MergeBase,
+ Mergeable: pr.Mergeable(ctx),
+ Deadline: apiIssue.Deadline,
+ Created: pr.Issue.CreatedUnix.AsTimePtr(),
+ Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
+ PinOrder: apiIssue.PinOrder,
+ RequestedReviewers: []*api.User{},
+ RequestedReviewersTeams: []*api.Team{},
AllowMaintainerEdit: pr.AllowMaintainerEdit,
+ Flow: int64(pr.Flow),
Base: &api.PRBranchInfo{
Name: pr.BaseBranch,
diff --git a/services/convert/pull_review.go b/services/convert/pull_review.go
index f7990e7a5c..97be118a83 100644
--- a/services/convert/pull_review.go
+++ b/services/convert/pull_review.go
@@ -7,9 +7,9 @@ import (
"context"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
)
// ToPullReview convert a review to api format
@@ -66,7 +66,7 @@ func ToPullReviewList(ctx context.Context, rl []*issues_model.Review, doer *user
result := make([]*api.PullReview, 0, len(rl))
for i := range rl {
// show pending reviews only for the user who created them
- if rl[i].Type == issues_model.ReviewTypePending && (doer == nil || !(doer.IsAdmin || doer.ID == rl[i].ReviewerID)) {
+ if rl[i].Type == issues_model.ReviewTypePending && (doer == nil || (!doer.IsAdmin && doer.ID != rl[i].ReviewerID)) {
continue
}
r, err := ToPullReview(ctx, rl[i], doer)
diff --git a/services/convert/pull_test.go b/services/convert/pull_test.go
index 1339ed5cc0..c0c69fd9ad 100644
--- a/services/convert/pull_test.go
+++ b/services/convert/pull_test.go
@@ -6,15 +6,15 @@ package convert
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -29,7 +29,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
require.NoError(t, pr.LoadIssue(db.DefaultContext))
apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil)
assert.NotNil(t, apiPullRequest)
- assert.EqualValues(t, &structs.PRBranchInfo{
+ assert.Equal(t, &structs.PRBranchInfo{
Name: "branch1",
Ref: "refs/pull/2/head",
Sha: "4a357436d925b5c974181ff12a994538ddc5a269",
diff --git a/services/convert/quota.go b/services/convert/quota.go
index 791cd8e038..ba729feaac 100644
--- a/services/convert/quota.go
+++ b/services/convert/quota.go
@@ -7,12 +7,12 @@ import (
"context"
"strconv"
- action_model "code.gitea.io/gitea/models/actions"
- issue_model "code.gitea.io/gitea/models/issues"
- package_model "code.gitea.io/gitea/models/packages"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
+ action_model "forgejo.org/models/actions"
+ issue_model "forgejo.org/models/issues"
+ package_model "forgejo.org/models/packages"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
)
func ToQuotaRuleInfo(rule quota_model.Rule, withName bool) api.QuotaRuleInfo {
diff --git a/services/convert/release.go b/services/convert/release.go
index 8c0f61b56c..7773cf3b19 100644
--- a/services/convert/release.go
+++ b/services/convert/release.go
@@ -6,8 +6,8 @@ package convert
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- api "code.gitea.io/gitea/modules/structs"
+ repo_model "forgejo.org/models/repo"
+ api "forgejo.org/modules/structs"
)
// ToAPIRelease convert a repo_model.Release to api.Release
diff --git a/services/convert/release_test.go b/services/convert/release_test.go
index 2e40bb9cdd..1d214f0222 100644
--- a/services/convert/release_test.go
+++ b/services/convert/release_test.go
@@ -6,9 +6,9 @@ package convert
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -24,6 +24,6 @@ func TestRelease_ToRelease(t *testing.T) {
apiRelease := ToAPIRelease(db.DefaultContext, repo1, release1)
assert.NotNil(t, apiRelease)
assert.EqualValues(t, 1, apiRelease.ID)
- assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL)
- assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL)
+ assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL)
+ assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL)
}
diff --git a/services/convert/repository.go b/services/convert/repository.go
index e4b2c7b8bc..1b0f46b3da 100644
--- a/services/convert/repository.go
+++ b/services/convert/repository.go
@@ -7,14 +7,14 @@ import (
"context"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
)
// ToRepo converts a Repository to api.Repository
diff --git a/services/convert/secret.go b/services/convert/secret.go
deleted file mode 100644
index dd7b9f0a6a..0000000000
--- a/services/convert/secret.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package convert
-
-import (
- secret_model "code.gitea.io/gitea/models/secret"
- api "code.gitea.io/gitea/modules/structs"
-)
-
-// ToSecret converts Secret to API format
-func ToSecret(secret *secret_model.Secret) *api.Secret {
- result := &api.Secret{
- Name: secret.Name,
- }
-
- return result
-}
diff --git a/services/convert/status.go b/services/convert/status.go
index 6cef63c1cd..1a71e70a52 100644
--- a/services/convert/status.go
+++ b/services/convert/status.go
@@ -6,9 +6,9 @@ package convert
import (
"context"
- git_model "code.gitea.io/gitea/models/git"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
+ git_model "forgejo.org/models/git"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
)
// ToCommitStatus converts git_model.CommitStatus to api.CommitStatus
diff --git a/services/convert/user.go b/services/convert/user.go
index 94a400de5d..444089fd83 100644
--- a/services/convert/user.go
+++ b/services/convert/user.go
@@ -6,9 +6,9 @@ package convert
import (
"context"
- "code.gitea.io/gitea/models/perm"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/perm"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
)
// ToUser convert user_model.User to api.User
@@ -57,7 +57,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
Created: user.CreatedUnix.AsTime(),
Restricted: user.IsRestricted,
Location: user.Location,
- Pronouns: user.Pronouns,
+ Pronouns: user.GetPronouns(signed),
Website: user.Website,
Description: user.Description,
// counter's
@@ -97,6 +97,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings {
Description: user.Description,
Theme: user.Theme,
HideEmail: user.KeepEmailPrivate,
+ HidePronouns: user.KeepPronounsPrivate,
HideActivity: user.KeepActivityPrivate,
DiffViewStyle: user.DiffViewStyle,
EnableRepoUnitHints: user.EnableRepoUnitHints,
diff --git a/services/convert/user_test.go b/services/convert/user_test.go
index 0f0b520c9b..8a42a9d97d 100644
--- a/services/convert/user_test.go
+++ b/services/convert/user_test.go
@@ -6,10 +6,10 @@ package convert
import (
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ api "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -31,11 +31,11 @@ func TestUser_ToUser(t *testing.T) {
apiUser = toUser(db.DefaultContext, user1, false, false)
assert.False(t, apiUser.IsAdmin)
- assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility)
+ assert.Equal(t, api.VisibleTypePublic.String(), apiUser.Visibility)
user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate})
apiUser = toUser(db.DefaultContext, user31, true, true)
assert.False(t, apiUser.IsAdmin)
- assert.EqualValues(t, api.VisibleTypePrivate.String(), apiUser.Visibility)
+ assert.Equal(t, api.VisibleTypePrivate.String(), apiUser.Visibility)
}
diff --git a/services/convert/utils.go b/services/convert/utils.go
index fe35fd2dac..3bbd4e39bd 100644
--- a/services/convert/utils.go
+++ b/services/convert/utils.go
@@ -7,8 +7,8 @@ package convert
import (
"strings"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
)
// ToCorrectPageSize makes sure page size is in allowed range.
diff --git a/services/convert/utils_test.go b/services/convert/utils_test.go
index b464d8bb68..6c3bf7d938 100644
--- a/services/convert/utils_test.go
+++ b/services/convert/utils_test.go
@@ -10,10 +10,10 @@ import (
)
func TestToCorrectPageSize(t *testing.T) {
- assert.EqualValues(t, 30, ToCorrectPageSize(0))
- assert.EqualValues(t, 30, ToCorrectPageSize(-10))
- assert.EqualValues(t, 20, ToCorrectPageSize(20))
- assert.EqualValues(t, 50, ToCorrectPageSize(100))
+ assert.Equal(t, 30, ToCorrectPageSize(0))
+ assert.Equal(t, 30, ToCorrectPageSize(-10))
+ assert.Equal(t, 20, ToCorrectPageSize(20))
+ assert.Equal(t, 50, ToCorrectPageSize(100))
}
func TestToGitServiceType(t *testing.T) {
diff --git a/services/convert/wiki.go b/services/convert/wiki.go
index 767bfdb88d..adcbd52949 100644
--- a/services/convert/wiki.go
+++ b/services/convert/wiki.go
@@ -6,8 +6,8 @@ package convert
import (
"time"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
)
// ToWikiCommit convert a git commit into a WikiCommit
diff --git a/services/cron/cron.go b/services/cron/cron.go
index 3c5737e371..d020f3fd6c 100644
--- a/services/cron/cron.go
+++ b/services/cron/cron.go
@@ -9,10 +9,10 @@ import (
"runtime/pprof"
"time"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/sync"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/sync"
+ "forgejo.org/modules/translation"
"github.com/go-co-op/gocron"
)
diff --git a/services/cron/setting.go b/services/cron/setting.go
index 6dad88830a..2db6c15370 100644
--- a/services/cron/setting.go
+++ b/services/cron/setting.go
@@ -6,7 +6,7 @@ package cron
import (
"time"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/modules/translation"
)
// Config represents a basic configuration interface that cron task
@@ -46,6 +46,13 @@ type CleanupHookTaskConfig struct {
NumberToKeep int
}
+// CleanupOfflineRunnersConfig represents a cron task with settings to clean up offline-runner
+type CleanupOfflineRunnersConfig struct {
+ BaseConfig
+ OlderThan time.Duration
+ GlobalScopeOnly bool
+}
+
// GetSchedule returns the schedule for the base config
func (b *BaseConfig) GetSchedule() string {
return b.Schedule
diff --git a/services/cron/tasks.go b/services/cron/tasks.go
index f8a7444c49..b547acdf05 100644
--- a/services/cron/tasks.go
+++ b/services/cron/tasks.go
@@ -11,14 +11,14 @@ import (
"sync"
"time"
- "code.gitea.io/gitea/models/db"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/models/db"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
)
var (
diff --git a/services/cron/tasks_actions.go b/services/cron/tasks_actions.go
index 59cfe36d14..2cd484fa69 100644
--- a/services/cron/tasks_actions.go
+++ b/services/cron/tasks_actions.go
@@ -5,10 +5,11 @@ package cron
import (
"context"
+ "time"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- actions_service "code.gitea.io/gitea/services/actions"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ actions_service "forgejo.org/services/actions"
)
func initActionsTasks() {
@@ -20,6 +21,7 @@ func initActionsTasks() {
registerCancelAbandonedJobs()
registerScheduleTasks()
registerActionsCleanup()
+ registerOfflineRunnersCleanup()
}
func registerStopZombieTasks() {
@@ -74,3 +76,22 @@ func registerActionsCleanup() {
return actions_service.Cleanup(ctx)
})
}
+
+func registerOfflineRunnersCleanup() {
+ RegisterTaskFatal("cleanup_offline_runners", &CleanupOfflineRunnersConfig{
+ BaseConfig: BaseConfig{
+ Enabled: false,
+ RunAtStart: false,
+ Schedule: "@midnight",
+ },
+ GlobalScopeOnly: true,
+ OlderThan: time.Hour * 24,
+ }, func(ctx context.Context, _ *user_model.User, cfg Config) error {
+ c := cfg.(*CleanupOfflineRunnersConfig)
+ return actions_service.CleanupOfflineRunners(
+ ctx,
+ c.OlderThan,
+ c.GlobalScopeOnly,
+ )
+ })
+}
diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go
index 2a213ae515..5ada7a8f5c 100644
--- a/services/cron/tasks_basic.go
+++ b/services/cron/tasks_basic.go
@@ -7,18 +7,18 @@ import (
"context"
"time"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/auth"
- "code.gitea.io/gitea/services/migrations"
- mirror_service "code.gitea.io/gitea/services/mirror"
- packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
- repo_service "code.gitea.io/gitea/services/repository"
- archiver_service "code.gitea.io/gitea/services/repository/archiver"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/auth"
+ "forgejo.org/services/migrations"
+ mirror_service "forgejo.org/services/mirror"
+ packages_cleanup_service "forgejo.org/services/packages/cleanup"
+ repo_service "forgejo.org/services/repository"
+ archiver_service "forgejo.org/services/repository/archiver"
)
func registerUpdateMirrorTask() {
@@ -54,7 +54,7 @@ func registerRepoHealthCheck() {
RunAtStart: false,
Schedule: "@midnight",
},
- Timeout: 60 * time.Second,
+ Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
Args: []string{},
}, func(ctx context.Context, _ *user_model.User, config Config) error {
rhcConfig := config.(*RepoHealthCheckConfig)
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index e1ba5274e6..322fe27ca0 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -7,17 +7,17 @@ import (
"context"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/updatechecker"
- repo_service "code.gitea.io/gitea/services/repository"
- archiver_service "code.gitea.io/gitea/services/repository/archiver"
- user_service "code.gitea.io/gitea/services/user"
+ activities_model "forgejo.org/models/activities"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/updatechecker"
+ repo_service "forgejo.org/services/repository"
+ archiver_service "forgejo.org/services/repository/archiver"
+ user_service "forgejo.org/services/user"
)
func registerDeleteInactiveUsers() {
diff --git a/services/doctor/actions.go b/services/doctor/actions.go
index 7c44fb8392..c382132265 100644
--- a/services/doctor/actions.go
+++ b/services/doctor/actions.go
@@ -7,12 +7,12 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ unit_model "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_service "forgejo.org/services/repository"
)
func disableMirrorActionsUnit(ctx context.Context, logger log.Logger, autofix bool) error {
diff --git a/services/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go
index 2920cf51d7..465a3fc7c0 100644
--- a/services/doctor/authorizedkeys.go
+++ b/services/doctor/authorizedkeys.go
@@ -7,15 +7,16 @@ import (
"bufio"
"bytes"
"context"
+ "errors"
"fmt"
"os"
"path/filepath"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
const tplCommentPrefix = `# gitea public key`
@@ -77,7 +78,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
fPath,
"forgejo admin regenerate keys",
"forgejo doctor check --run authorized-keys --fix")
- return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "forgejo admin regenerate keys" or "forgejo doctor check --run authorized-keys --fix"`)
+ return errors.New(`authorized_keys is out of date and should be regenerated with "forgejo admin regenerate keys" or "forgejo doctor check --run authorized-keys --fix"`)
}
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
err = asymkey_model.RewriteAllPublicKeys(ctx)
diff --git a/services/doctor/breaking.go b/services/doctor/breaking.go
index ec8433b8de..339f8e847c 100644
--- a/services/doctor/breaking.go
+++ b/services/doctor/breaking.go
@@ -7,11 +7,11 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/validation"
+ "forgejo.org/models/db"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
"xorm.io/builder"
)
diff --git a/services/doctor/checkOldArchives.go b/services/doctor/checkOldArchives.go
index 390dfb43aa..301e99391b 100644
--- a/services/doctor/checkOldArchives.go
+++ b/services/doctor/checkOldArchives.go
@@ -8,9 +8,9 @@ import (
"os"
"path/filepath"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
)
func checkOldArchives(ctx context.Context, logger log.Logger, autofix bool) error {
diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go
index 9e2fcb645f..6fe4c9c5e6 100644
--- a/services/doctor/dbconsistency.go
+++ b/services/doctor/dbconsistency.go
@@ -6,16 +6,16 @@ package doctor
import (
"context"
- actions_model "code.gitea.io/gitea/models/actions"
- activities_model "code.gitea.io/gitea/models/activities"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/migrations"
- org_model "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ actions_model "forgejo.org/models/actions"
+ activities_model "forgejo.org/models/activities"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/migrations"
+ org_model "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
type consistencyCheck struct {
@@ -78,7 +78,14 @@ func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCh
func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
// make sure DB version is up-to-date
- if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
+ ensureUpToDateWrapper := func(e db.Engine) error {
+ engine, err := db.GetMasterEngine(e)
+ if err != nil {
+ return err
+ }
+ return migrations.EnsureUpToDate(engine)
+ }
+ if err := db.InitEngineWithMigration(ctx, ensureUpToDateWrapper); err != nil {
logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
return err
}
diff --git a/services/doctor/dbversion.go b/services/doctor/dbversion.go
index 2a102b2194..c0ff22915d 100644
--- a/services/doctor/dbversion.go
+++ b/services/doctor/dbversion.go
@@ -6,14 +6,18 @@ package doctor
import (
"context"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/migrations"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/db"
+ "forgejo.org/models/migrations"
+ "forgejo.org/modules/log"
+
+ "xorm.io/xorm"
)
func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error {
logger.Info("Expected database version: %d", migrations.ExpectedDBVersion())
- if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
+ if err := db.InitEngineWithMigration(ctx, func(eng db.Engine) error {
+ return migrations.EnsureUpToDate(eng.(*xorm.Engine))
+ }); err != nil {
if !autofix {
logger.Critical("Error: %v during ensure up to date", err)
return err
@@ -21,7 +25,9 @@ func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error
logger.Warn("Got Error: %v during ensure up to date", err)
logger.Warn("Attempting to migrate to the latest DB version to fix this.")
- err = db.InitEngineWithMigration(ctx, migrations.Migrate)
+ err = db.InitEngineWithMigration(ctx, func(eng db.Engine) error {
+ return migrations.Migrate(eng.(*xorm.Engine))
+ })
if err != nil {
logger.Critical("Error: %v during migration", err)
}
diff --git a/services/doctor/doctor.go b/services/doctor/doctor.go
index a4eb5e16b9..6d8e168bf2 100644
--- a/services/doctor/doctor.go
+++ b/services/doctor/doctor.go
@@ -10,11 +10,11 @@ import (
"sort"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
)
// Check represents a Doctor check
diff --git a/services/doctor/fix16961.go b/services/doctor/fix16961.go
index 50d9ac6621..2212d9e903 100644
--- a/services/doctor/fix16961.go
+++ b/services/doctor/fix16961.go
@@ -9,12 +9,12 @@ import (
"errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/timeutil"
"xorm.io/builder"
)
diff --git a/services/doctor/fix16961_test.go b/services/doctor/fix16961_test.go
index 498ed9c8d5..75f9f206ab 100644
--- a/services/doctor/fix16961_test.go
+++ b/services/doctor/fix16961_test.go
@@ -6,7 +6,7 @@ package doctor
import (
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
+ repo_model "forgejo.org/models/repo"
"github.com/stretchr/testify/assert"
)
@@ -221,7 +221,7 @@ func Test_fixPullRequestsConfig_16961(t *testing.T) {
if gotFixed != tt.wantFixed {
t.Errorf("fixPullRequestsConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
- assert.EqualValues(t, &tt.expected, cfg)
+ assert.Equal(t, &tt.expected, cfg)
})
}
}
@@ -265,7 +265,7 @@ func Test_fixIssuesConfig_16961(t *testing.T) {
if gotFixed != tt.wantFixed {
t.Errorf("fixIssuesConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
- assert.EqualValues(t, &tt.expected, cfg)
+ assert.Equal(t, &tt.expected, cfg)
})
}
}
diff --git a/services/doctor/fix8312.go b/services/doctor/fix8312.go
index 4fc049873a..31cd6686d7 100644
--- a/services/doctor/fix8312.go
+++ b/services/doctor/fix8312.go
@@ -6,11 +6,11 @@ package doctor
import (
"context"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ "forgejo.org/modules/log"
"xorm.io/builder"
)
diff --git a/services/doctor/heads.go b/services/doctor/heads.go
index 41fca01d57..7f9d1c73e8 100644
--- a/services/doctor/heads.go
+++ b/services/doctor/heads.go
@@ -6,9 +6,9 @@ package doctor
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error {
diff --git a/services/doctor/lfs.go b/services/doctor/lfs.go
index 8531b7bbe8..fe858605f4 100644
--- a/services/doctor/lfs.go
+++ b/services/doctor/lfs.go
@@ -5,12 +5,12 @@ package doctor
import (
"context"
- "fmt"
+ "errors"
"time"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/repository"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/repository"
)
func init() {
@@ -27,7 +27,7 @@ func init() {
func garbageCollectLFSCheck(ctx context.Context, logger log.Logger, autofix bool) error {
if !setting.LFS.StartServer {
- return fmt.Errorf("LFS support is disabled")
+ return errors.New("LFS support is disabled")
}
if err := repository.GarbageCollectLFSMetaObjects(ctx, repository.GarbageCollectLFSMetaObjectsOptions{
diff --git a/services/doctor/mergebase.go b/services/doctor/mergebase.go
index de460c4190..bebde30bee 100644
--- a/services/doctor/mergebase.go
+++ b/services/doctor/mergebase.go
@@ -8,11 +8,11 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
"xorm.io/builder"
)
diff --git a/services/doctor/misc.go b/services/doctor/misc.go
index 9300c3a25c..9b9c96b52b 100644
--- a/services/doctor/misc.go
+++ b/services/doctor/misc.go
@@ -11,17 +11,17 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
lru "github.com/hashicorp/golang-lru/v2"
"xorm.io/builder"
diff --git a/services/doctor/packages_nuget.go b/services/doctor/packages_nuget.go
index 47fdb3ac12..f6a33db779 100644
--- a/services/doctor/packages_nuget.go
+++ b/services/doctor/packages_nuget.go
@@ -9,12 +9,12 @@ import (
"slices"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- nuget_module "code.gitea.io/gitea/modules/packages/nuget"
- packages_service "code.gitea.io/gitea/services/packages"
+ "forgejo.org/models/db"
+ "forgejo.org/models/packages"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ nuget_module "forgejo.org/modules/packages/nuget"
+ packages_service "forgejo.org/services/packages"
"xorm.io/builder"
)
diff --git a/services/doctor/paths.go b/services/doctor/paths.go
index 8e37f01ef5..4fbe19ea04 100644
--- a/services/doctor/paths.go
+++ b/services/doctor/paths.go
@@ -8,8 +8,8 @@ import (
"fmt"
"os"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
type configurationFile struct {
diff --git a/services/doctor/push_mirror_consistency.go b/services/doctor/push_mirror_consistency.go
index 68b96d6415..07986770b2 100644
--- a/services/doctor/push_mirror_consistency.go
+++ b/services/doctor/push_mirror_consistency.go
@@ -7,9 +7,9 @@ import (
"context"
"strings"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/log"
"xorm.io/builder"
)
diff --git a/services/doctor/repository.go b/services/doctor/repository.go
index 6c33426636..cd51483d88 100644
--- a/services/doctor/repository.go
+++ b/services/doctor/repository.go
@@ -6,11 +6,11 @@ package doctor
import (
"context"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/storage"
+ repo_service "forgejo.org/services/repository"
"xorm.io/builder"
)
diff --git a/services/doctor/storage.go b/services/doctor/storage.go
index 3f3b562c37..7dbe475d6c 100644
--- a/services/doctor/storage.go
+++ b/services/doctor/storage.go
@@ -9,16 +9,16 @@ import (
"io/fs"
"strings"
- "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/git"
+ "forgejo.org/models/packages"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
)
type commonStorageCheckOptions struct {
diff --git a/services/doctor/usertype.go b/services/doctor/usertype.go
index ab32b78e62..0a034d8f9d 100644
--- a/services/doctor/usertype.go
+++ b/services/doctor/usertype.go
@@ -6,8 +6,8 @@ package doctor
import (
"context"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
)
func checkUserType(ctx context.Context, logger log.Logger, autofix bool) error {
diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go
index d6e2ea7e94..5672313181 100644
--- a/services/externalaccount/link.go
+++ b/services/externalaccount/link.go
@@ -5,9 +5,9 @@ package externalaccount
import (
"context"
- "fmt"
+ "errors"
- user_model "code.gitea.io/gitea/models/user"
+ user_model "forgejo.org/models/user"
"github.com/markbates/goth"
)
@@ -23,7 +23,7 @@ type Store interface {
func LinkAccountFromStore(ctx context.Context, store Store, user *user_model.User) error {
gothUser := store.Get("linkAccountGothUser")
if gothUser == nil {
- return fmt.Errorf("not in LinkAccount session")
+ return errors.New("not in LinkAccount session")
}
return LinkAccountToUser(ctx, user, gothUser.(goth.User))
diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go
index 3cfd8c81f9..68d085f6d0 100644
--- a/services/externalaccount/user.go
+++ b/services/externalaccount/user.go
@@ -8,11 +8,11 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/auth"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/auth"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/structs"
"github.com/markbates/goth"
)
diff --git a/services/f3/driver/asset.go b/services/f3/driver/asset.go
deleted file mode 100644
index 6759cc645c..0000000000
--- a/services/f3/driver/asset.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright Earl Warren
-// Copyright Loïc Dachary
-// SPDX-License-Identifier: MIT
-
-package driver
-
-import (
- "context"
- "crypto/sha256"
- "encoding/hex"
- "fmt"
- "io"
- "os"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/services/attachment"
-
- "code.forgejo.org/f3/gof3/v3/f3"
- f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
- "code.forgejo.org/f3/gof3/v3/tree/generic"
- f3_util "code.forgejo.org/f3/gof3/v3/util"
- "github.com/google/uuid"
-)
-
-var _ f3_tree.ForgeDriverInterface = &issue{}
-
-type asset struct {
- common
-
- forgejoAsset *repo_model.Attachment
- sha string
- contentType string
- downloadFunc f3.DownloadFuncType
-}
-
-func (o *asset) SetNative(asset any) {
- o.forgejoAsset = asset.(*repo_model.Attachment)
-}
-
-func (o *asset) GetNativeID() string {
- return fmt.Sprintf("%d", o.forgejoAsset.ID)
-}
-
-func (o *asset) NewFormat() f3.Interface {
- node := o.GetNode()
- return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
-}
-
-func (o *asset) ToFormat() f3.Interface {
- if o.forgejoAsset == nil {
- return o.NewFormat()
- }
-
- return &f3.ReleaseAsset{
- Common: f3.NewCommon(o.GetNativeID()),
- Name: o.forgejoAsset.Name,
- ContentType: o.contentType,
- Size: o.forgejoAsset.Size,
- DownloadCount: o.forgejoAsset.DownloadCount,
- Created: o.forgejoAsset.CreatedUnix.AsTime(),
- SHA256: o.sha,
- DownloadURL: o.forgejoAsset.DownloadURL(),
- DownloadFunc: o.downloadFunc,
- }
-}
-
-func (o *asset) FromFormat(content f3.Interface) {
- asset := content.(*f3.ReleaseAsset)
- o.forgejoAsset = &repo_model.Attachment{
- ID: f3_util.ParseInt(asset.GetID()),
- Name: asset.Name,
- Size: asset.Size,
- DownloadCount: asset.DownloadCount,
- CreatedUnix: timeutil.TimeStamp(asset.Created.Unix()),
- CustomDownloadURL: asset.DownloadURL,
- }
- o.contentType = asset.ContentType
- o.sha = asset.SHA256
- o.downloadFunc = asset.DownloadFunc
-}
-
-func (o *asset) Get(ctx context.Context) bool {
- node := o.GetNode()
- o.Trace("%s", node.GetID())
-
- id := node.GetID().Int64()
-
- asset, err := repo_model.GetAttachmentByID(ctx, id)
- if repo_model.IsErrAttachmentNotExist(err) {
- return false
- }
- if err != nil {
- panic(fmt.Errorf("asset %v %w", id, err))
- }
-
- o.forgejoAsset = asset
-
- path := o.forgejoAsset.RelativePath()
-
- {
- f, err := storage.Attachments.Open(path)
- if err != nil {
- panic(err)
- }
- hasher := sha256.New()
- if _, err := io.Copy(hasher, f); err != nil {
- panic(fmt.Errorf("io.Copy to hasher: %v", err))
- }
- o.sha = hex.EncodeToString(hasher.Sum(nil))
- }
-
- o.downloadFunc = func() io.ReadCloser {
- o.Trace("download %s from copy stored in temporary file %s", o.forgejoAsset.DownloadURL, path)
- f, err := os.Open(path)
- if err != nil {
- panic(err)
- }
- return f
- }
- return true
-}
-
-func (o *asset) Patch(ctx context.Context) {
- o.Trace("%d", o.forgejoAsset.ID)
- if _, err := db.GetEngine(ctx).ID(o.forgejoAsset.ID).Cols("name").Update(o.forgejoAsset); err != nil {
- panic(fmt.Errorf("UpdateAssetCols: %v %v", o.forgejoAsset, err))
- }
-}
-
-func (o *asset) Put(ctx context.Context) generic.NodeID {
- node := o.GetNode()
- o.Trace("%s", node.GetID())
-
- uploader, err := user_model.GetAdminUser(ctx)
- if err != nil {
- panic(fmt.Errorf("GetAdminUser %w", err))
- }
-
- o.forgejoAsset.UploaderID = uploader.ID
- o.forgejoAsset.RepoID = f3_tree.GetProjectID(o.GetNode())
- o.forgejoAsset.ReleaseID = f3_tree.GetReleaseID(o.GetNode())
- o.forgejoAsset.UUID = uuid.New().String()
-
- download := o.downloadFunc()
- defer download.Close()
-
- _, err = attachment.NewAttachment(ctx, o.forgejoAsset, download, o.forgejoAsset.Size)
- if err != nil {
- panic(err)
- }
-
- o.Trace("asset created %d", o.forgejoAsset.ID)
- return generic.NewNodeID(o.forgejoAsset.ID)
-}
-
-func (o *asset) Delete(ctx context.Context) {
- node := o.GetNode()
- o.Trace("%s", node.GetID())
-
- if err := repo_model.DeleteAttachment(ctx, o.forgejoAsset, true); err != nil {
- panic(err)
- }
-}
-
-func newAsset() generic.NodeDriverInterface {
- return &asset{}
-}
diff --git a/services/f3/driver/assets.go b/services/f3/driver/assets.go
deleted file mode 100644
index 88a3979713..0000000000
--- a/services/f3/driver/assets.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright Earl Warren
-// Copyright Loïc Dachary
-// SPDX-License-Identifier: MIT
-
-package driver
-
-import (
- "context"
- "fmt"
-
- repo_model "code.gitea.io/gitea/models/repo"
-
- f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
- "code.forgejo.org/f3/gof3/v3/tree/generic"
-)
-
-type assets struct {
- container
-}
-
-func (o *assets) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
- if page > 1 {
- return generic.NewChildrenSlice(0)
- }
-
- releaseID := f3_tree.GetReleaseID(o.GetNode())
-
- release, err := repo_model.GetReleaseByID(ctx, releaseID)
- if err != nil {
- panic(fmt.Errorf("GetReleaseByID %v %w", releaseID, err))
- }
-
- if err := release.LoadAttributes(ctx); err != nil {
- panic(fmt.Errorf("error while listing assets: %v", err))
- }
-
- return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(release.Attachments...)...)
-}
-
-func newAssets() generic.NodeDriverInterface {
- return &assets{}
-}
diff --git a/services/f3/driver/attachment.go b/services/f3/driver/attachment.go
new file mode 100644
index 0000000000..64c188d6e0
--- /dev/null
+++ b/services/f3/driver/attachment.go
@@ -0,0 +1,185 @@
+// Copyright Earl Warren
+// Copyright Loïc Dachary
+// SPDX-License-Identifier: MIT
+
+package driver
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/timeutil"
+ forgejo_attachment "forgejo.org/services/attachment"
+
+ "code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
+ f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
+ "code.forgejo.org/f3/gof3/v3/tree/generic"
+ f3_util "code.forgejo.org/f3/gof3/v3/util"
+ "github.com/google/uuid"
+)
+
+var _ f3_tree.ForgeDriverInterface = &issue{}
+
+type attachment struct {
+ common
+
+ forgejoAttachment *repo_model.Attachment
+ sha string
+ contentType string
+ downloadFunc f3.DownloadFuncType
+}
+
+func (o *attachment) SetNative(attachment any) {
+ o.forgejoAttachment = attachment.(*repo_model.Attachment)
+}
+
+func (o *attachment) GetNativeID() string {
+ return fmt.Sprintf("%d", o.forgejoAttachment.ID)
+}
+
+func (o *attachment) NewFormat() f3.Interface {
+ node := o.GetNode()
+ return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
+}
+
+func (o *attachment) ToFormat() f3.Interface {
+ if o.forgejoAttachment == nil {
+ return o.NewFormat()
+ }
+
+ return &f3.Attachment{
+ Common: f3.NewCommon(o.GetNativeID()),
+ Name: o.forgejoAttachment.Name,
+ ContentType: o.contentType,
+ Size: o.forgejoAttachment.Size,
+ DownloadCount: o.forgejoAttachment.DownloadCount,
+ Created: o.forgejoAttachment.CreatedUnix.AsTime(),
+ SHA256: o.sha,
+ DownloadURL: o.forgejoAttachment.DownloadURL(),
+ DownloadFunc: o.downloadFunc,
+ }
+}
+
+func (o *attachment) FromFormat(content f3.Interface) {
+ attachment := content.(*f3.Attachment)
+ o.forgejoAttachment = &repo_model.Attachment{
+ ID: f3_util.ParseInt(attachment.GetID()),
+ Name: attachment.Name,
+ Size: attachment.Size,
+ DownloadCount: attachment.DownloadCount,
+ CreatedUnix: timeutil.TimeStamp(attachment.Created.Unix()),
+ CustomDownloadURL: attachment.DownloadURL,
+ }
+ o.contentType = attachment.ContentType
+ o.sha = attachment.SHA256
+ o.downloadFunc = attachment.DownloadFunc
+}
+
+func (o *attachment) Get(ctx context.Context) bool {
+ node := o.GetNode()
+ o.Trace("%s", node.GetID())
+
+ id := node.GetID().Int64()
+
+ attachment, err := repo_model.GetAttachmentByID(ctx, id)
+ if repo_model.IsErrAttachmentNotExist(err) {
+ return false
+ }
+ if err != nil {
+ panic(fmt.Errorf("attachment %v %w", id, err))
+ }
+
+ o.forgejoAttachment = attachment
+
+ path := o.forgejoAttachment.RelativePath()
+
+ {
+ f, err := storage.Attachments.Open(path)
+ if err != nil {
+ panic(err)
+ }
+ hasher := sha256.New()
+ if _, err := io.Copy(hasher, f); err != nil {
+ panic(fmt.Errorf("io.Copy to hasher: %v", err))
+ }
+ o.sha = hex.EncodeToString(hasher.Sum(nil))
+ }
+
+ o.downloadFunc = func() io.ReadCloser {
+ o.Trace("download %s from copy stored in temporary file %s", o.forgejoAttachment.DownloadURL, path)
+ f, err := os.Open(path)
+ if err != nil {
+ panic(err)
+ }
+ return f
+ }
+ return true
+}
+
+func (o *attachment) Patch(ctx context.Context) {
+ o.Trace("%d", o.forgejoAttachment.ID)
+ if _, err := db.GetEngine(ctx).ID(o.forgejoAttachment.ID).Cols("name").Update(o.forgejoAttachment); err != nil {
+ panic(fmt.Errorf("UpdateAttachmentCols: %v %v", o.forgejoAttachment, err))
+ }
+}
+
+func (o *attachment) Put(ctx context.Context) f3_id.NodeID {
+ node := o.GetNode()
+ o.Trace("%s", node.GetID())
+
+ uploader, err := user_model.GetAdminUser(ctx)
+ if err != nil {
+ panic(fmt.Errorf("GetAdminUser %w", err))
+ }
+
+ attachable := f3_tree.GetAttachable(o.GetNode())
+ attachableID := f3_tree.GetAttachableID(o.GetNode())
+
+ switch attachable.GetKind() {
+ case f3_tree.KindRelease:
+ o.forgejoAttachment.ReleaseID = attachableID
+ case f3_tree.KindComment:
+ o.forgejoAttachment.CommentID = attachableID
+ case f3_tree.KindIssue, f3_tree.KindPullRequest:
+ o.forgejoAttachment.IssueID = attachableID
+ default:
+ panic(fmt.Errorf("unexpected type %s", attachable.GetKind()))
+ }
+
+ o.forgejoAttachment.UploaderID = uploader.ID
+ o.forgejoAttachment.RepoID = f3_tree.GetProjectID(o.GetNode())
+ o.forgejoAttachment.UUID = uuid.New().String()
+
+ download := o.downloadFunc()
+ defer download.Close()
+
+ _, err = forgejo_attachment.NewAttachment(ctx, o.forgejoAttachment, download, o.forgejoAttachment.Size)
+ if err != nil {
+ panic(err)
+ }
+
+ o.Trace("attachment created %d", o.forgejoAttachment.ID)
+ return f3_id.NewNodeID(o.forgejoAttachment.ID)
+}
+
+func (o *attachment) Delete(ctx context.Context) {
+ node := o.GetNode()
+ o.Trace("%s", node.GetID())
+
+ if err := repo_model.DeleteAttachment(ctx, o.forgejoAttachment, true); err != nil {
+ panic(err)
+ }
+}
+
+func newAttachment() generic.NodeDriverInterface {
+ return &attachment{}
+}
diff --git a/services/f3/driver/attachments.go b/services/f3/driver/attachments.go
new file mode 100644
index 0000000000..392afda52c
--- /dev/null
+++ b/services/f3/driver/attachments.go
@@ -0,0 +1,79 @@
+// Copyright Earl Warren
+// Copyright Loïc Dachary
+// SPDX-License-Identifier: MIT
+
+package driver
+
+import (
+ "context"
+ "fmt"
+
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+
+ f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
+ "code.forgejo.org/f3/gof3/v3/tree/generic"
+)
+
+type attachments struct {
+ container
+}
+
+func (o *attachments) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
+ if page > 1 {
+ return generic.NewChildrenSlice(0)
+ }
+
+ attachable := f3_tree.GetAttachable(o.GetNode())
+ attachableID := f3_tree.GetAttachableID(o.GetNode())
+
+ var attachments []*repo_model.Attachment
+
+ switch attachable.GetKind() {
+ case f3_tree.KindRelease:
+ release, err := repo_model.GetReleaseByID(ctx, attachableID)
+ if err != nil {
+ panic(fmt.Errorf("GetReleaseByID %v %w", attachableID, err))
+ }
+
+ if err := release.LoadAttributes(ctx); err != nil {
+ panic(fmt.Errorf("error while listing attachments: %v", err))
+ }
+
+ attachments = release.Attachments
+
+ case f3_tree.KindComment:
+ comment, err := issues_model.GetCommentByID(ctx, attachableID)
+ if err != nil {
+ panic(fmt.Errorf("GetCommentByID %v %w", attachableID, err))
+ }
+
+ if err := comment.LoadAttachments(ctx); err != nil {
+ panic(fmt.Errorf("error while listing attachments: %v", err))
+ }
+
+ attachments = comment.Attachments
+
+ case f3_tree.KindIssue, f3_tree.KindPullRequest:
+ repoID := f3_tree.GetProjectID(o.GetNode())
+ issue, err := issues_model.GetIssueByIndex(ctx, repoID, attachableID)
+ if err != nil {
+ panic(fmt.Errorf("GetIssueByID %v %w", attachableID, err))
+ }
+
+ if err := issue.LoadAttachments(ctx); err != nil {
+ panic(fmt.Errorf("error while listing attachments: %v", err))
+ }
+
+ attachments = issue.Attachments
+
+ default:
+ panic(fmt.Errorf("unexpected type %s", attachable.GetKind()))
+ }
+
+ return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(attachments...)...)
+}
+
+func newAttachments() generic.NodeDriverInterface {
+ return &attachments{}
+}
diff --git a/services/f3/driver/comment.go b/services/f3/driver/comment.go
index 0c10fd744d..bd924930b5 100644
--- a/services/f3/driver/comment.go
+++ b/services/f3/driver/comment.go
@@ -8,12 +8,13 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -95,7 +96,7 @@ func (o *comment) Patch(ctx context.Context) {
}
}
-func (o *comment) Put(ctx context.Context) generic.NodeID {
+func (o *comment) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -105,7 +106,7 @@ func (o *comment) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("comment created %d", o.forgejoComment.ID)
- return generic.NewNodeID(o.forgejoComment.ID)
+ return f3_id.NewNodeID(o.forgejoComment.ID)
}
func (o *comment) Delete(ctx context.Context) {
diff --git a/services/f3/driver/comments.go b/services/f3/driver/comments.go
index eb79b74066..d8c84e290c 100644
--- a/services/f3/driver/comments.go
+++ b/services/f3/driver/comments.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/common.go b/services/f3/driver/common.go
index 104f91c977..4c5dfd8b1a 100644
--- a/services/f3/driver/common.go
+++ b/services/f3/driver/common.go
@@ -7,6 +7,7 @@ package driver
import (
"context"
+ f3_kind "code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -37,7 +38,7 @@ func (o *common) getPageSize() int {
return o.getTreeDriver().GetPageSize()
}
-func (o *common) getKind() generic.Kind {
+func (o *common) getKind() f3_kind.Kind {
return o.GetNode().GetKind()
}
diff --git a/services/f3/driver/container.go b/services/f3/driver/container.go
index 153044416e..1ca47ef285 100644
--- a/services/f3/driver/container.go
+++ b/services/f3/driver/container.go
@@ -8,8 +8,8 @@ import (
"context"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
- "code.forgejo.org/f3/gof3/v3/tree/generic"
)
type container struct {
@@ -30,7 +30,7 @@ func (o *container) FromFormat(content f3.Interface) {
func (o *container) Get(context.Context) bool { return true }
-func (o *container) Put(ctx context.Context) generic.NodeID {
+func (o *container) Put(ctx context.Context) f3_id.NodeID {
return o.upsert(ctx)
}
@@ -38,6 +38,6 @@ func (o *container) Patch(ctx context.Context) {
o.upsert(ctx)
}
-func (o *container) upsert(context.Context) generic.NodeID {
- return generic.NewNodeID(o.getKind())
+func (o *container) upsert(context.Context) f3_id.NodeID {
+ return f3_id.NewNodeID(o.getKind())
}
diff --git a/services/f3/driver/forge.go b/services/f3/driver/forge.go
index a4bcf61231..03acb41450 100644
--- a/services/f3/driver/forge.go
+++ b/services/f3/driver/forge.go
@@ -8,9 +8,11 @@ import (
"context"
"fmt"
- user_model "code.gitea.io/gitea/models/user"
+ user_model "forgejo.org/models/user"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
+ f3_kind "code.forgejo.org/f3/gof3/v3/kind"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/util"
@@ -19,16 +21,16 @@ import (
type forge struct {
generic.NullDriver
- ownersKind map[string]generic.Kind
+ ownersKind map[string]f3_kind.Kind
}
func newForge() generic.NodeDriverInterface {
return &forge{
- ownersKind: make(map[string]generic.Kind),
+ ownersKind: make(map[string]f3_kind.Kind),
}
}
-func (o *forge) getOwnersKind(ctx context.Context, id string) generic.Kind {
+func (o *forge) getOwnersKind(ctx context.Context, id string) f3_kind.Kind {
kind, ok := o.ownersKind[id]
if !ok {
user, err := user_model.GetUserByID(ctx, util.ParseInt(id))
@@ -50,7 +52,7 @@ func (o *forge) getOwnersPath(ctx context.Context, id string) f3_tree.Path {
func (o *forge) Equals(context.Context, generic.NodeInterface) bool { return true }
func (o *forge) Get(context.Context) bool { return true }
-func (o *forge) Put(context.Context) generic.NodeID { return generic.NewNodeID("forge") }
+func (o *forge) Put(context.Context) f3_id.NodeID { return f3_id.NewNodeID("forge") }
func (o *forge) Patch(context.Context) {}
func (o *forge) Delete(context.Context) {}
func (o *forge) NewFormat() f3.Interface { return &f3.Forge{} }
diff --git a/services/f3/driver/issue.go b/services/f3/driver/issue.go
index 371ff9d45a..6308c4cc2d 100644
--- a/services/f3/driver/issue.go
+++ b/services/f3/driver/issue.go
@@ -8,15 +8,16 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/timeutil"
- issue_service "code.gitea.io/gitea/services/issue"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/timeutil"
+ issue_service "forgejo.org/services/issue"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -210,7 +211,7 @@ func updateIssueLabels(ctx context.Context, issueID int64, labels []*issues_mode
}
}
-func (o *issue) Put(ctx context.Context) generic.NodeID {
+func (o *issue) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -232,7 +233,7 @@ func (o *issue) Put(ctx context.Context) generic.NodeID {
updateIssueLabels(ctx, o.forgejoIssue.ID, o.forgejoIssue.Labels)
o.Trace("issue created %d/%d", o.forgejoIssue.ID, o.forgejoIssue.Index)
- return generic.NewNodeID(o.forgejoIssue.Index)
+ return f3_id.NewNodeID(o.forgejoIssue.Index)
}
func (o *issue) Delete(ctx context.Context) {
diff --git a/services/f3/driver/issues.go b/services/f3/driver/issues.go
index 3a5a64e2b1..dd6828dc86 100644
--- a/services/f3/driver/issues.go
+++ b/services/f3/driver/issues.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/label.go b/services/f3/driver/label.go
index aef0d0256d..707ac2bab3 100644
--- a/services/f3/driver/label.go
+++ b/services/f3/driver/label.go
@@ -9,10 +9,11 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -86,7 +87,7 @@ func (o *label) Patch(ctx context.Context) {
}
}
-func (o *label) Put(ctx context.Context) generic.NodeID {
+func (o *label) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -95,7 +96,7 @@ func (o *label) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("label created %d", o.forgejoLabel.ID)
- return generic.NewNodeID(o.forgejoLabel.ID)
+ return f3_id.NewNodeID(o.forgejoLabel.ID)
}
func (o *label) Delete(ctx context.Context) {
diff --git a/services/f3/driver/labels.go b/services/f3/driver/labels.go
index 03f986b57a..4f705ed206 100644
--- a/services/f3/driver/labels.go
+++ b/services/f3/driver/labels.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/main.go b/services/f3/driver/main.go
index 825d456692..eb6e4a6fb6 100644
--- a/services/f3/driver/main.go
+++ b/services/f3/driver/main.go
@@ -5,7 +5,7 @@
package driver
import (
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
+ driver_options "forgejo.org/services/f3/driver/options"
"code.forgejo.org/f3/gof3/v3/options"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
diff --git a/services/f3/driver/main_test.go b/services/f3/driver/main_test.go
index 8505b69b7e..b136fd5b23 100644
--- a/services/f3/driver/main_test.go
+++ b/services/f3/driver/main_test.go
@@ -7,14 +7,14 @@ package driver
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
+ "forgejo.org/models/unittest"
+ driver_options "forgejo.org/services/f3/driver/options"
- _ "code.gitea.io/gitea/models"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/activities"
- _ "code.gitea.io/gitea/models/perm/access"
- _ "code.gitea.io/gitea/services/f3/driver/tests"
+ _ "forgejo.org/models"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/activities"
+ _ "forgejo.org/models/perm/access"
+ _ "forgejo.org/services/f3/driver/tests"
tests_f3 "code.forgejo.org/f3/gof3/v3/tree/tests/f3"
"github.com/stretchr/testify/require"
diff --git a/services/f3/driver/milestone.go b/services/f3/driver/milestone.go
index f133d37f7a..d10e6918ac 100644
--- a/services/f3/driver/milestone.go
+++ b/services/f3/driver/milestone.go
@@ -9,12 +9,13 @@ import (
"fmt"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -122,7 +123,7 @@ func (o *milestone) Patch(ctx context.Context) {
}
}
-func (o *milestone) Put(ctx context.Context) generic.NodeID {
+func (o *milestone) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -131,7 +132,7 @@ func (o *milestone) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("milestone created %d", o.forgejoMilestone.ID)
- return generic.NewNodeID(o.forgejoMilestone.ID)
+ return f3_id.NewNodeID(o.forgejoMilestone.ID)
}
func (o *milestone) Delete(ctx context.Context) {
diff --git a/services/f3/driver/milestones.go b/services/f3/driver/milestones.go
index c816903bb1..cf0b70c158 100644
--- a/services/f3/driver/milestones.go
+++ b/services/f3/driver/milestones.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/options.go b/services/f3/driver/options.go
index abc5015dd0..516f9baf7a 100644
--- a/services/f3/driver/options.go
+++ b/services/f3/driver/options.go
@@ -7,7 +7,7 @@ package driver
import (
"net/http"
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
+ driver_options "forgejo.org/services/f3/driver/options"
"code.forgejo.org/f3/gof3/v3/options"
)
diff --git a/services/f3/driver/organization.go b/services/f3/driver/organization.go
index 76b240068d..af1eea4dda 100644
--- a/services/f3/driver/organization.go
+++ b/services/f3/driver/organization.go
@@ -8,11 +8,12 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -81,7 +82,7 @@ func (o *organization) Patch(ctx context.Context) {
}
}
-func (o *organization) Put(ctx context.Context) generic.NodeID {
+func (o *organization) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -94,7 +95,7 @@ func (o *organization) Put(ctx context.Context) generic.NodeID {
panic(err)
}
- return generic.NewNodeID(o.forgejoOrganization.ID)
+ return f3_id.NewNodeID(o.forgejoOrganization.ID)
}
func (o *organization) Delete(ctx context.Context) {
diff --git a/services/f3/driver/organizations.go b/services/f3/driver/organizations.go
index 98c4c1497d..eca6bfb9d4 100644
--- a/services/f3/driver/organizations.go
+++ b/services/f3/driver/organizations.go
@@ -8,10 +8,11 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -36,13 +37,13 @@ func (o *organizations) ListPage(ctx context.Context, page int) generic.Children
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(organizations...)...)
}
-func (o *organizations) GetIDFromName(ctx context.Context, name string) generic.NodeID {
+func (o *organizations) GetIDFromName(ctx context.Context, name string) f3_id.NodeID {
organization, err := org_model.GetOrgByName(ctx, name)
if err != nil {
panic(fmt.Errorf("GetOrganizationByName: %v", err))
}
- return generic.NewNodeID(organization.ID)
+ return f3_id.NewNodeID(organization.ID)
}
func newOrganizations() generic.NodeDriverInterface {
diff --git a/services/f3/driver/project.go b/services/f3/driver/project.go
index c2a2df38c4..5a3ec81e40 100644
--- a/services/f3/driver/project.go
+++ b/services/f3/driver/project.go
@@ -9,11 +9,12 @@ import (
"fmt"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- repo_service "code.gitea.io/gitea/services/repository"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ repo_service "forgejo.org/services/repository"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -121,7 +122,7 @@ func (o *project) Patch(ctx context.Context) {
}
}
-func (o *project) Put(ctx context.Context) generic.NodeID {
+func (o *project) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -166,7 +167,7 @@ func (o *project) Put(ctx context.Context) generic.NodeID {
o.forgejoProject = repo
o.Trace("project created %d", o.forgejoProject.ID)
}
- return generic.NewNodeID(o.forgejoProject.ID)
+ return f3_id.NewNodeID(o.forgejoProject.ID)
}
func (o *project) Delete(ctx context.Context) {
diff --git a/services/f3/driver/projects.go b/services/f3/driver/projects.go
index a2dabc3f95..0c76854f43 100644
--- a/services/f3/driver/projects.go
+++ b/services/f3/driver/projects.go
@@ -8,9 +8,10 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -19,18 +20,18 @@ type projects struct {
container
}
-func (o *projects) GetIDFromName(ctx context.Context, name string) generic.NodeID {
+func (o *projects) GetIDFromName(ctx context.Context, name string) f3_id.NodeID {
owner := f3_tree.GetOwnerName(o.GetNode())
forgejoProject, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, name)
if repo_model.IsErrRepoNotExist(err) {
- return generic.NilID
+ return f3_id.NilID
}
if err != nil {
panic(fmt.Errorf("error GetRepositoryByOwnerAndName(%s, %s): %v", owner, name, err))
}
- return generic.NewNodeID(forgejoProject.ID)
+ return f3_id.NewNodeID(forgejoProject.ID)
}
func (o *projects) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
diff --git a/services/f3/driver/pullrequest.go b/services/f3/driver/pullrequest.go
index 466b4bd576..664ee6b13b 100644
--- a/services/f3/driver/pullrequest.go
+++ b/services/f3/driver/pullrequest.go
@@ -9,15 +9,17 @@ import (
"fmt"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/timeutil"
- issue_service "code.gitea.io/gitea/services/issue"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/timeutil"
+ issue_service "forgejo.org/services/issue"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
+ f3_path "code.forgejo.org/f3/gof3/v3/path"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -51,7 +53,7 @@ func (o *pullRequest) repositoryToReference(ctx context.Context, repository *rep
if repository == nil {
panic("unexpected nil repository")
}
- forge := o.getTree().GetRoot().GetChild(generic.NewNodeID(f3_tree.KindForge)).GetDriver().(*forge)
+ forge := o.getTree().GetRoot().GetChild(f3_id.NewNodeID(f3_tree.KindForge)).GetDriver().(*forge)
owners := forge.getOwnersPath(ctx, fmt.Sprintf("%d", repository.OwnerID))
return f3_tree.NewRepositoryReference(owners.String(), repository.OwnerID, repository.ID)
}
@@ -61,7 +63,7 @@ func (o *pullRequest) referenceToRepository(reference *f3.Reference) int64 {
if reference.Get() == "../../repository/vcs" {
project = f3_tree.GetProjectID(o.GetNode())
} else {
- p := f3_tree.ToPath(generic.PathAbsolute(o.GetNode().GetCurrentPath().String(), reference.Get()))
+ p := f3_tree.ToPath(f3_path.PathAbsolute(generic.NewElementNode, o.GetNode().GetCurrentPath().String(), reference.Get()))
o.Trace("%v %v", o.GetNode().GetCurrentPath().String(), p)
_, project = p.OwnerAndProjectID()
}
@@ -237,7 +239,7 @@ func (o *pullRequest) GetPullRequestRef() string {
return fmt.Sprintf("refs/pull/%s/head", o.GetNativeID())
}
-func (o *pullRequest) Put(ctx context.Context) generic.NodeID {
+func (o *pullRequest) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -289,7 +291,7 @@ func (o *pullRequest) Put(ctx context.Context) generic.NodeID {
}
o.Trace("pullRequest created %d/%d", o.forgejoPullRequest.ID, o.forgejoPullRequest.Index)
- return generic.NewNodeID(o.forgejoPullRequest.Index)
+ return f3_id.NewNodeID(o.forgejoPullRequest.Index)
}
func (o *pullRequest) Delete(ctx context.Context) {
diff --git a/services/f3/driver/pullrequests.go b/services/f3/driver/pullrequests.go
index e7f2910314..227171994c 100644
--- a/services/f3/driver/pullrequests.go
+++ b/services/f3/driver/pullrequests.go
@@ -8,9 +8,9 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/optional"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/optional"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/reaction.go b/services/f3/driver/reaction.go
index 0dc486c729..b959206074 100644
--- a/services/f3/driver/reaction.go
+++ b/services/f3/driver/reaction.go
@@ -8,11 +8,12 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -87,8 +88,8 @@ func (o *reaction) Patch(ctx context.Context) {
}
}
-func (o *reaction) Put(ctx context.Context) generic.NodeID {
- o.Error("%v", o.forgejoReaction.User)
+func (o *reaction) Put(ctx context.Context) f3_id.NodeID {
+ o.Trace("%v", o.forgejoReaction.User)
sess := db.GetEngine(ctx)
@@ -109,13 +110,13 @@ func (o *reaction) Put(ctx context.Context) generic.NodeID {
panic(fmt.Errorf("unexpected type %v", reactionable.GetKind()))
}
- o.Error("%v", o.forgejoReaction)
+ o.Trace("%v", o.forgejoReaction)
if _, err := sess.Insert(o.forgejoReaction); err != nil {
panic(err)
}
o.Trace("reaction created %d", o.forgejoReaction.ID)
- return generic.NewNodeID(o.forgejoReaction.ID)
+ return f3_id.NewNodeID(o.forgejoReaction.ID)
}
func (o *reaction) Delete(ctx context.Context) {
diff --git a/services/f3/driver/reactions.go b/services/f3/driver/reactions.go
index b7fd5e8f0a..a546927b92 100644
--- a/services/f3/driver/reactions.go
+++ b/services/f3/driver/reactions.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/release.go b/services/f3/driver/release.go
index e937f84d05..df38bd8bc0 100644
--- a/services/f3/driver/release.go
+++ b/services/f3/driver/release.go
@@ -9,14 +9,15 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/timeutil"
- release_service "code.gitea.io/gitea/services/release"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/timeutil"
+ release_service "forgejo.org/services/release"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -115,7 +116,7 @@ func (o *release) Patch(ctx context.Context) {
}
}
-func (o *release) Put(ctx context.Context) generic.NodeID {
+func (o *release) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -133,7 +134,7 @@ func (o *release) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("release created %d", o.forgejoRelease.ID)
- return generic.NewNodeID(o.forgejoRelease.ID)
+ return f3_id.NewNodeID(o.forgejoRelease.ID)
}
func (o *release) Delete(ctx context.Context) {
diff --git a/services/f3/driver/releases.go b/services/f3/driver/releases.go
index 3b46bc7c54..a631c0b60e 100644
--- a/services/f3/driver/releases.go
+++ b/services/f3/driver/releases.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/repository.go b/services/f3/driver/repository.go
index da968b4c47..3cd9aa7f2e 100644
--- a/services/f3/driver/repository.go
+++ b/services/f3/driver/repository.go
@@ -7,10 +7,11 @@ package driver
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
+ repo_model "forgejo.org/models/repo"
"code.forgejo.org/f3/gof3/v3/f3"
helpers_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/repository"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -57,7 +58,7 @@ func (o *repository) Get(ctx context.Context) bool {
return o.h.Get(ctx)
}
-func (o *repository) Put(ctx context.Context) generic.NodeID {
+func (o *repository) Put(ctx context.Context) f3_id.NodeID {
return o.upsert(ctx)
}
@@ -65,13 +66,13 @@ func (o *repository) Patch(ctx context.Context) {
o.upsert(ctx)
}
-func (o *repository) upsert(ctx context.Context) generic.NodeID {
+func (o *repository) upsert(ctx context.Context) f3_id.NodeID {
o.Trace("%s", o.GetNativeID())
o.h.Upsert(ctx, o.f)
- return generic.NewNodeID(o.f.Name)
+ return f3_id.NewNodeID(o.f.Name)
}
-func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination string)) {
+func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination, internalRef string)) {
o.f.FetchFunc = fetchFunc
}
@@ -92,6 +93,16 @@ func (o *repository) GetRepositoryPushURL() string {
return o.getURL()
}
+func (o *repository) GetRepositoryInternalRef() string {
+ return ""
+}
+
+func (o *repository) GetPullRequestBranch(pr *f3.PullRequestBranch) *f3.PullRequestBranch {
+ panic("")
+}
+func (o *repository) CreatePullRequestBranch(pr *f3.PullRequestBranch) {}
+func (o *repository) DeletePullRequestBranch(pr *f3.PullRequestBranch) {}
+
func newRepository(_ context.Context) generic.NodeDriverInterface {
r := &repository{
f: &f3.Repository{},
diff --git a/services/f3/driver/review.go b/services/f3/driver/review.go
index a3c074bd63..f4f5ff44b8 100644
--- a/services/f3/driver/review.go
+++ b/services/f3/driver/review.go
@@ -8,12 +8,13 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -134,7 +135,7 @@ func (o *review) Patch(ctx context.Context) {
}
}
-func (o *review) Put(ctx context.Context) generic.NodeID {
+func (o *review) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -153,7 +154,7 @@ func (o *review) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("review created %d", o.forgejoReview.ID)
- return generic.NewNodeID(o.forgejoReview.ID)
+ return f3_id.NewNodeID(o.forgejoReview.ID)
}
func (o *review) Delete(ctx context.Context) {
diff --git a/services/f3/driver/reviewcomment.go b/services/f3/driver/reviewcomment.go
index 8e13d86b63..22759b6df3 100644
--- a/services/f3/driver/reviewcomment.go
+++ b/services/f3/driver/reviewcomment.go
@@ -9,12 +9,13 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -115,7 +116,7 @@ func (o *reviewComment) Patch(ctx context.Context) {
}
}
-func (o *reviewComment) Put(ctx context.Context) generic.NodeID {
+func (o *reviewComment) Put(ctx context.Context) f3_id.NodeID {
node := o.GetNode()
o.Trace("%s", node.GetID())
@@ -125,7 +126,7 @@ func (o *reviewComment) Put(ctx context.Context) generic.NodeID {
panic(err)
}
o.Trace("reviewComment created %d", o.forgejoReviewComment.ID)
- return generic.NewNodeID(o.forgejoReviewComment.ID)
+ return f3_id.NewNodeID(o.forgejoReviewComment.ID)
}
func (o *reviewComment) Delete(ctx context.Context) {
diff --git a/services/f3/driver/reviewcomments.go b/services/f3/driver/reviewcomments.go
index e11aaa489b..2aa4dea22c 100644
--- a/services/f3/driver/reviewcomments.go
+++ b/services/f3/driver/reviewcomments.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/reviews.go b/services/f3/driver/reviews.go
index a20d5741d1..7c3dcb37de 100644
--- a/services/f3/driver/reviews.go
+++ b/services/f3/driver/reviews.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/root.go b/services/f3/driver/root.go
index 0e8a67faf3..f4a6e6a7ca 100644
--- a/services/f3/driver/root.go
+++ b/services/f3/driver/root.go
@@ -8,6 +8,7 @@ import (
"context"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -33,8 +34,8 @@ func (o *root) ToFormat() f3.Interface {
func (o *root) Get(context.Context) bool { return true }
-func (o *root) Put(context.Context) generic.NodeID {
- return generic.NilID
+func (o *root) Put(context.Context) f3_id.NodeID {
+ return f3_id.NilID
}
func (o *root) Patch(context.Context) {
diff --git a/services/f3/driver/tests/init.go b/services/f3/driver/tests/init.go
index d7bf23ac88..9035296dc0 100644
--- a/services/f3/driver/tests/init.go
+++ b/services/f3/driver/tests/init.go
@@ -5,7 +5,7 @@
package tests
import (
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
+ driver_options "forgejo.org/services/f3/driver/options"
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
)
diff --git a/services/f3/driver/tests/new.go b/services/f3/driver/tests/new.go
index 2e3dfc3c95..2f5c6c64db 100644
--- a/services/f3/driver/tests/new.go
+++ b/services/f3/driver/tests/new.go
@@ -7,10 +7,10 @@ package tests
import (
"testing"
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
+ driver_options "forgejo.org/services/f3/driver/options"
+ f3_kind "code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/options"
- "code.forgejo.org/f3/gof3/v3/tree/generic"
forge_test "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
)
@@ -22,8 +22,8 @@ func (o *forgeTest) NewOptions(t *testing.T) options.Interface {
return newTestOptions(t)
}
-func (o *forgeTest) GetExceptions() []generic.Kind {
- return []generic.Kind{}
+func (o *forgeTest) GetExceptions() []f3_kind.Kind {
+ return []f3_kind.Kind{}
}
func (o *forgeTest) GetNonTestUsers() []string {
diff --git a/services/f3/driver/tests/options.go b/services/f3/driver/tests/options.go
index adaa1da588..f61b10c9ef 100644
--- a/services/f3/driver/tests/options.go
+++ b/services/f3/driver/tests/options.go
@@ -7,9 +7,9 @@ package tests
import (
"testing"
- forgejo_log "code.gitea.io/gitea/modules/log"
- driver_options "code.gitea.io/gitea/services/f3/driver/options"
- "code.gitea.io/gitea/services/f3/util"
+ forgejo_log "forgejo.org/modules/log"
+ driver_options "forgejo.org/services/f3/driver/options"
+ "forgejo.org/services/f3/util"
"code.forgejo.org/f3/gof3/v3/options"
)
diff --git a/services/f3/driver/topic.go b/services/f3/driver/topic.go
index 16b2eb3142..cc94aa35fa 100644
--- a/services/f3/driver/topic.go
+++ b/services/f3/driver/topic.go
@@ -8,10 +8,11 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -79,14 +80,14 @@ func (o *topic) Patch(ctx context.Context) {
}
}
-func (o *topic) Put(ctx context.Context) generic.NodeID {
+func (o *topic) Put(ctx context.Context) f3_id.NodeID {
sess := db.GetEngine(ctx)
if _, err := sess.Insert(o.forgejoTopic); err != nil {
panic(err)
}
o.Trace("topic created %d", o.forgejoTopic.ID)
- return generic.NewNodeID(o.forgejoTopic.ID)
+ return f3_id.NewNodeID(o.forgejoTopic.ID)
}
func (o *topic) Delete(ctx context.Context) {
diff --git a/services/f3/driver/topics.go b/services/f3/driver/topics.go
index 2685a47928..38f03dbd2d 100644
--- a/services/f3/driver/topics.go
+++ b/services/f3/driver/topics.go
@@ -8,8 +8,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
diff --git a/services/f3/driver/tree.go b/services/f3/driver/tree.go
index 0302ed74ae..fe11b15f6e 100644
--- a/services/f3/driver/tree.go
+++ b/services/f3/driver/tree.go
@@ -8,8 +8,9 @@ import (
"context"
"fmt"
- forgejo_options "code.gitea.io/gitea/services/f3/driver/options"
+ forgejo_options "forgejo.org/services/f3/driver/options"
+ f3_kind "code.forgejo.org/f3/gof3/v3/kind"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -24,7 +25,7 @@ func (o *treeDriver) Init() {
o.NullTreeDriver.Init()
}
-func (o *treeDriver) Factory(ctx context.Context, kind generic.Kind) generic.NodeDriverInterface {
+func (o *treeDriver) Factory(ctx context.Context, kind f3_kind.Kind) generic.NodeDriverInterface {
switch kind {
case f3_tree.KindForge:
return newForge()
@@ -48,10 +49,10 @@ func (o *treeDriver) Factory(ctx context.Context, kind generic.Kind) generic.Nod
return newComments()
case f3_tree.KindComment:
return newComment()
- case f3_tree.KindAssets:
- return newAssets()
- case f3_tree.KindAsset:
- return newAsset()
+ case f3_tree.KindAttachments:
+ return newAttachments()
+ case f3_tree.KindAttachment:
+ return newAttachment()
case f3_tree.KindLabels:
return newLabels()
case f3_tree.KindLabel:
@@ -88,7 +89,7 @@ func (o *treeDriver) Factory(ctx context.Context, kind generic.Kind) generic.Nod
return newRepositories()
case f3_tree.KindRepository:
return newRepository(ctx)
- case generic.KindRoot:
+ case f3_kind.KindRoot:
return newRoot(o.GetTree().(f3_tree.TreeInterface).NewFormat(kind))
default:
panic(fmt.Errorf("unexpected kind %s", kind))
diff --git a/services/f3/driver/user.go b/services/f3/driver/user.go
index 221b06e834..bf8bfaf9c9 100644
--- a/services/f3/driver/user.go
+++ b/services/f3/driver/user.go
@@ -9,11 +9,12 @@ import (
"fmt"
"strings"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- user_service "code.gitea.io/gitea/services/user"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ user_service "forgejo.org/services/user"
"code.forgejo.org/f3/gof3/v3/f3"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
f3_util "code.forgejo.org/f3/gof3/v3/util"
@@ -96,9 +97,9 @@ func (o *user) Get(ctx context.Context) bool {
func (o *user) Patch(context.Context) {
}
-func (o *user) Put(ctx context.Context) generic.NodeID {
+func (o *user) Put(ctx context.Context) f3_id.NodeID {
if user := getSystemUserByName(o.forgejoUser.Name); user != nil {
- return generic.NewNodeID(user.ID)
+ return f3_id.NewNodeID(user.ID)
}
o.forgejoUser.LowerName = strings.ToLower(o.forgejoUser.Name)
@@ -111,7 +112,7 @@ func (o *user) Put(ctx context.Context) generic.NodeID {
panic(err)
}
- return generic.NewNodeID(o.forgejoUser.ID)
+ return f3_id.NewNodeID(o.forgejoUser.ID)
}
func (o *user) Delete(ctx context.Context) {
diff --git a/services/f3/driver/users.go b/services/f3/driver/users.go
index 92ed0bcbc5..cb413ae05d 100644
--- a/services/f3/driver/users.go
+++ b/services/f3/driver/users.go
@@ -8,9 +8,10 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ f3_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
"code.forgejo.org/f3/gof3/v3/tree/generic"
)
@@ -34,13 +35,13 @@ func (o *users) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(users...)...)
}
-func (o *users) GetIDFromName(ctx context.Context, name string) generic.NodeID {
+func (o *users) GetIDFromName(ctx context.Context, name string) f3_id.NodeID {
user, err := user_model.GetUserByName(ctx, name)
if err != nil {
panic(fmt.Errorf("GetUserByName: %v", err))
}
- return generic.NewNodeID(user.ID)
+ return f3_id.NewNodeID(user.ID)
}
func newUsers() generic.NodeDriverInterface {
diff --git a/services/f3/util/logger.go b/services/f3/util/logger.go
index 21d8d6bbfa..9a1409ae84 100644
--- a/services/f3/util/logger.go
+++ b/services/f3/util/logger.go
@@ -6,8 +6,8 @@ package util
import (
"fmt"
- forgejo_log "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/migration"
+ forgejo_log "forgejo.org/modules/log"
+ "forgejo.org/modules/migration"
"code.forgejo.org/f3/gof3/v3/logger"
)
diff --git a/services/f3/util/logger_test.go b/services/f3/util/logger_test.go
index db880aa439..f62d9e2e82 100644
--- a/services/f3/util/logger_test.go
+++ b/services/f3/util/logger_test.go
@@ -8,8 +8,8 @@ import (
"testing"
"time"
- forgejo_log "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/test"
+ forgejo_log "forgejo.org/modules/log"
+ "forgejo.org/modules/test"
"code.forgejo.org/f3/gof3/v3/logger"
"github.com/stretchr/testify/assert"
@@ -23,7 +23,7 @@ func TestF3UtilMessage(t *testing.T) {
actual = fmt.Sprintf(message, args...)
}, nil)
logger.Message("EXPECTED %s", "MESSAGE")
- assert.EqualValues(t, expected, actual)
+ assert.Equal(t, expected, actual)
}
func TestF3UtilLogger(t *testing.T) {
diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go
index 4c6f5ca0ca..a3b719d1a7 100644
--- a/services/federation/federation_service.go
+++ b/services/federation/federation_service.go
@@ -1,25 +1,26 @@
-// Copyright 2024 The Forgejo Authors. All rights reserved.
+// Copyright 2024, 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package federation
import (
"context"
+ "errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
- "code.gitea.io/gitea/models/forgefed"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/auth/password"
- fm "code.gitea.io/gitea/modules/forgefed"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/validation"
+ "forgejo.org/models/forgefed"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/auth/password"
+ fm "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/validation"
"github.com/google/uuid"
)
@@ -46,7 +47,7 @@ func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int
return http.StatusInternalServerError, "Wrong FederationHost", err
}
if !activity.IsNewer(federationHost.LatestActivity) {
- return http.StatusNotAcceptable, "Activity out of order.", fmt.Errorf("Activity already processed")
+ return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed")
}
actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName))
if err != nil {
@@ -98,39 +99,47 @@ func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int
}
func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) {
- actionsUser := user.NewActionsUser()
+ actionsUser := user.NewAPServerActor()
clientFactory, err := activitypub.GetClientFactory(ctx)
if err != nil {
return nil, err
}
- client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.")
+
+ client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
if err != nil {
return nil, err
}
+
body, err := client.GetBody(actorID.AsWellKnownNodeInfoURI())
if err != nil {
return nil, err
}
+
nodeInfoWellKnown, err := forgefed.NewNodeInfoWellKnown(body)
if err != nil {
return nil, err
}
+
body, err = client.GetBody(nodeInfoWellKnown.Href)
if err != nil {
return nil, err
}
+
nodeInfo, err := forgefed.NewNodeInfo(body)
if err != nil {
return nil, err
}
- result, err := forgefed.NewFederationHost(nodeInfo, actorID.Host)
+
+ result, err := forgefed.NewFederationHost(actorID.Host, nodeInfo, actorID.HostPort, actorID.HostSchema)
if err != nil {
return nil, err
}
+
err = forgefed.CreateFederationHost(ctx, &result)
if err != nil {
return nil, err
}
+
return &result, nil
}
@@ -140,7 +149,7 @@ func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.Fe
if err != nil {
return nil, err
}
- federationHost, err := forgefed.FindFederationHostByFqdn(ctx, rawActorID.Host)
+ federationHost, err := forgefed.FindFederationHostByFqdnAndPort(ctx, rawActorID.Host, rawActorID.HostPort)
if err != nil {
return nil, err
}
@@ -155,18 +164,18 @@ func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.Fe
}
func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) {
- // ToDo: Do we get a publicKeyId from server, repo or owner or repo?
- actionsUser := user.NewActionsUser()
+ actionsUser := user.NewAPServerActor()
clientFactory, err := activitypub.GetClientFactory(ctx)
if err != nil {
return nil, nil, err
}
- client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.")
+
+ apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
if err != nil {
return nil, nil, err
}
- body, err := client.GetBody(personID.AsURI())
+ body, err := apClient.GetBody(personID.AsURI())
if err != nil {
return nil, nil, err
}
@@ -176,26 +185,37 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI
if err != nil {
return nil, nil, err
}
+
if res, err := validation.IsValid(person); !res {
return nil, nil, err
}
+
log.Info("Fetched valid person:%q", person)
localFqdn, err := url.ParseRequestURI(setting.AppURL)
if err != nil {
return nil, nil, err
}
+
email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname())
loginName := personID.AsLoginName()
name := fmt.Sprintf("%v%v", person.PreferredUsername.String(), personID.HostSuffix())
fullName := person.Name.String()
+
if len(person.Name) == 0 {
fullName = name
}
+
password, err := password.Generate(32)
if err != nil {
return nil, nil, err
}
+
+ inbox, err := url.ParseRequestURI(person.Inbox.GetLink().String())
+ if err != nil {
+ return nil, nil, err
+ }
+
newUser := user.User{
LowerName: strings.ToLower(name),
Name: name,
@@ -207,18 +227,21 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI
LoginName: loginName,
Type: user.UserTypeRemoteUser,
IsAdmin: false,
- NormalizedFederatedURI: personID.AsURI(),
}
+
federatedUser := user.FederatedUser{
- ExternalID: personID.ID,
- FederationHostID: federationHostID,
+ ExternalID: personID.ID,
+ FederationHostID: federationHostID,
+ InboxPath: inbox.Path,
+ NormalizedOriginalURL: personID.AsURI(),
}
+
err = user.CreateFederatedUser(ctx, &newUser, &federatedUser)
if err != nil {
return nil, nil, err
}
- log.Info("Created federatedUser:%q", federatedUser)
+ log.Info("Created federatedUser:%q", federatedUser)
return &newUser, &federatedUser, nil
}
@@ -274,7 +297,8 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error
if err != nil {
return err
}
- apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID())
+
+ apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorKeyID())
if err != nil {
return err
}
@@ -285,7 +309,7 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error
return err
}
- _, err = apclient.Post(json, fmt.Sprintf("%v/inbox/", activity.Object))
+ _, err = apclient.Post(json, fmt.Sprintf("%s/inbox", activity.Object))
if err != nil {
log.Error("error %v while sending activity: %q", err, activity)
}
diff --git a/services/feed/action.go b/services/feed/action.go
index 83daaa1438..7d179bd1c8 100644
--- a/services/feed/action.go
+++ b/services/feed/action.go
@@ -9,16 +9,17 @@ import (
"path"
"strings"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
)
type actionNotifier struct {
@@ -38,6 +39,24 @@ func NewNotifier() notify_service.Notifier {
return &actionNotifier{}
}
+func notifyAll(ctx context.Context, action *activities_model.Action) error {
+ _, err := activities_model.NotifyWatchers(ctx, action)
+ if err != nil {
+ return err
+ }
+ return err
+ // return federation_service.NotifyActivityPubFollowers(ctx, out)
+}
+
+func notifyAllActions(ctx context.Context, acts []*activities_model.Action) error {
+ _, err := activities_model.NotifyWatchersActions(ctx, acts)
+ if err != nil {
+ return err
+ }
+ return nil
+ // return federation_service.NotifyActivityPubFollowers(ctx, out)
+}
+
func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) {
if err := issue.LoadPoster(ctx); err != nil {
log.Error("issue.LoadPoster: %v", err)
@@ -49,7 +68,7 @@ func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue
}
repo := issue.Repo
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: issue.Poster.ID,
ActUser: issue.Poster,
OpType: activities_model.ActionCreateIssue,
@@ -90,7 +109,7 @@ func (a *actionNotifier) IssueChangeStatus(ctx context.Context, doer *user_model
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := activities_model.NotifyWatchers(ctx, act); err != nil {
+ if err := notifyAll(ctx, act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -126,7 +145,7 @@ func (a *actionNotifier) CreateIssueComment(ctx context.Context, doer *user_mode
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := activities_model.NotifyWatchers(ctx, act); err != nil {
+ if err := notifyAll(ctx, act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -145,7 +164,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: pull.Issue.Poster.ID,
ActUser: pull.Issue.Poster,
OpType: activities_model.ActionCreatePullRequest,
@@ -159,7 +178,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model.
}
func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldRepoName string) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionRenameRepo,
@@ -173,7 +192,7 @@ func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model.
}
func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionTransferRepo,
@@ -187,7 +206,7 @@ func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_mode
}
func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionCreateRepo,
@@ -200,7 +219,7 @@ func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_mod
}
func (a *actionNotifier) ForkRepository(ctx context.Context, doer *user_model.User, oldRepo, repo *repo_model.Repository) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionCreateRepo,
@@ -265,13 +284,13 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model
actions = append(actions, action)
}
- if err := activities_model.NotifyWatchersActions(ctx, actions); err != nil {
+ if err := notifyAllActions(ctx, actions); err != nil {
log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
}
}
func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionMergePullRequest,
@@ -285,7 +304,7 @@ func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.Us
}
func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionAutoMergePullRequest,
@@ -303,7 +322,7 @@ func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_
if len(review.OriginalAuthor) > 0 {
reviewerName = review.OriginalAuthor
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionPullReviewDismissed,
@@ -319,6 +338,10 @@ func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_
}
func (a *actionNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+ if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
+ commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
+ }
+
data, err := json.Marshal(commits)
if err != nil {
log.Error("Marshal: %v", err)
@@ -337,7 +360,7 @@ func (a *actionNotifier) PushCommits(ctx context.Context, pusher *user_model.Use
opType = activities_model.ActionDeleteBranch
}
- if err = activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err = notifyAll(ctx, &activities_model.Action{
ActUserID: pusher.ID,
ActUser: pusher,
OpType: opType,
@@ -357,7 +380,7 @@ func (a *actionNotifier) CreateRef(ctx context.Context, doer *user_model.User, r
// has sent same action in `PushCommits`, so skip it.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -376,7 +399,7 @@ func (a *actionNotifier) DeleteRef(ctx context.Context, doer *user_model.User, r
// has sent same action in `PushCommits`, so skip it.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -390,13 +413,17 @@ func (a *actionNotifier) DeleteRef(ctx context.Context, doer *user_model.User, r
}
func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+ if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
+ commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
+ }
+
data, err := json.Marshal(commits)
if err != nil {
log.Error("json.Marshal: %v", err)
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncPush,
@@ -411,7 +438,7 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model
}
func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncCreate,
@@ -425,7 +452,7 @@ func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.Use
}
func (a *actionNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncDelete,
@@ -443,7 +470,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
log.Error("LoadAttributes: %v", err)
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := notifyAll(ctx, &activities_model.Action{
ActUserID: rel.PublisherID,
ActUser: rel.Publisher,
OpType: activities_model.ActionPublishRelease,
diff --git a/services/feed/action_test.go b/services/feed/action_test.go
index 87bb13330a..93ca543a1a 100644
--- a/services/feed/action_test.go
+++ b/services/feed/action_test.go
@@ -1,4 +1,5 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package feed
@@ -7,15 +8,20 @@ import (
"strings"
"testing"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -51,3 +57,89 @@ func TestRenameRepoAction(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, actionBean)
unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
+
+func pushCommits() *repository.PushCommits {
+ pushCommits := repository.NewPushCommits()
+ pushCommits.Commits = []*repository.PushCommit{
+ {
+ Sha1: "69554a6",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "not signed commit",
+ },
+ {
+ Sha1: "27566bd",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "good signed commit (with not yet validated email)",
+ },
+ {
+ Sha1: "5099b81",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "good signed commit",
+ },
+ }
+ pushCommits.HeadCommit = &repository.PushCommit{Sha1: "69554a6"}
+ return pushCommits
+}
+
+func TestSyncPushCommits(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID})
+
+ t.Run("All commits", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.UI.FeedMaxCommitNum, 10)()
+
+ maxID := unittest.GetCount(t, &activities_model.Action{})
+ NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master")}, pushCommits())
+
+ newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/master"}, unittest.Cond("id > ?", maxID))
+ assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ })
+
+ t.Run("Only one commit", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.UI.FeedMaxCommitNum, 1)()
+
+ maxID := unittest.GetCount(t, &activities_model.Action{})
+ NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main")}, pushCommits())
+
+ newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/main"}, unittest.Cond("id > ?", maxID))
+ assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ })
+}
+
+func TestPushCommits(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID})
+
+ t.Run("All commits", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.UI.FeedMaxCommitNum, 10)()
+
+ maxID := unittest.GetCount(t, &activities_model.Action{})
+ NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master")}, pushCommits())
+
+ newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/master"}, unittest.Cond("id > ?", maxID))
+ assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ })
+
+ t.Run("Only one commit", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.UI.FeedMaxCommitNum, 1)()
+
+ maxID := unittest.GetCount(t, &activities_model.Action{})
+ NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main")}, pushCommits())
+
+ newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/main"}, unittest.Cond("id > ?", maxID))
+ assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ })
+}
diff --git a/services/forgejo/main_test.go b/services/forgejo/main_test.go
index 40ce1715b1..5523ed1aab 100644
--- a/services/forgejo/main_test.go
+++ b/services/forgejo/main_test.go
@@ -5,12 +5,12 @@ package forgejo
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/activities"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/activities"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/forgejo/sanity.go b/services/forgejo/sanity.go
index 5e817d67f5..70f15889d4 100644
--- a/services/forgejo/sanity.go
+++ b/services/forgejo/sanity.go
@@ -3,9 +3,9 @@
package forgejo
import (
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
var (
diff --git a/services/forgejo/sanity_test.go b/services/forgejo/sanity_test.go
index 657f7e2720..065a9fda4d 100644
--- a/services/forgejo/sanity_test.go
+++ b/services/forgejo/sanity_test.go
@@ -7,9 +7,9 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/require"
)
diff --git a/services/forgejo/sanity_v1TOv5_0_1Included.go b/services/forgejo/sanity_v1TOv5_0_1Included.go
index 49de636f33..1d3f07d8e1 100644
--- a/services/forgejo/sanity_v1TOv5_0_1Included.go
+++ b/services/forgejo/sanity_v1TOv5_0_1Included.go
@@ -6,9 +6,9 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/forgejo/semver"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ "forgejo.org/models/forgejo/semver"
+ "forgejo.org/modules/setting"
"github.com/hashicorp/go-version"
)
diff --git a/services/forgejo/sanity_v1TOv5_0_1Included_test.go b/services/forgejo/sanity_v1TOv5_0_1Included_test.go
index 56618ebd5f..2521afb496 100644
--- a/services/forgejo/sanity_v1TOv5_0_1Included_test.go
+++ b/services/forgejo/sanity_v1TOv5_0_1Included_test.go
@@ -6,10 +6,10 @@ import (
"fmt"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/forgejo/semver"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/db"
+ "forgejo.org/models/forgejo/semver"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/log"
"github.com/stretchr/testify/require"
)
diff --git a/services/forms/admin.go b/services/forms/admin.go
index 1f055cff55..5a5d46634b 100644
--- a/services/forms/admin.go
+++ b/services/forms/admin.go
@@ -6,9 +6,9 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go
index f0da63155a..e665ca0d19 100644
--- a/services/forms/auth_form.go
+++ b/services/forms/auth_form.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
@@ -75,12 +75,8 @@ type AuthenticationForm struct {
Oauth2RestrictedGroup string
Oauth2GroupTeamMap string `binding:"ValidGroupTeamMap"`
Oauth2GroupTeamMapRemoval bool
+ Oauth2AttributeSSHPublicKey string
SkipLocalTwoFA bool
- SSPIAutoCreateUsers bool
- SSPIAutoActivateUsers bool
- SSPIStripDomainNames bool
- SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"`
- SSPIDefaultLanguage string
GroupTeamMap string `binding:"ValidGroupTeamMap"`
GroupTeamMapRemoval bool
}
diff --git a/services/forms/org.go b/services/forms/org.go
index dea2e159e9..a6e4e72c4a 100644
--- a/services/forms/org.go
+++ b/services/forms/org.go
@@ -7,9 +7,9 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/package_form.go b/services/forms/package_form.go
index d475d2f569..82e5a09f86 100644
--- a/services/forms/package_form.go
+++ b/services/forms/package_form.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
@@ -15,7 +15,7 @@ import (
type PackageCleanupRuleForm struct {
ID int64
Enabled bool
- Type string `binding:"Required;In(alpine,arch,cargo,chef,composer,conan,conda,container,cran,debian,generic,go,helm,maven,npm,nuget,pub,pypi,rpm,rubygems,swift,vagrant)"`
+ Type string `binding:"Required;In(alpine,arch,cargo,chef,composer,conan,conda,container,cran,debian,generic,go,helm,maven,npm,nuget,pub,pypi,rpm,alt,rubygems,swift,vagrant)"`
KeepCount int `binding:"In(0,1,5,10,25,50,100)"`
KeepPattern string `binding:"RegexPattern"`
RemoveDays int `binding:"In(0,7,14,30,60,90,180)"`
diff --git a/services/forms/repo_branch_form.go b/services/forms/repo_branch_form.go
index 186a4ad367..c34e7c6d17 100644
--- a/services/forms/repo_branch_form.go
+++ b/services/forms/repo_branch_form.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index 1ce9b298ad..bb81e939b0 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -12,14 +12,14 @@ import (
"regexp"
"strings"
- "code.gitea.io/gitea/models"
- issues_model "code.gitea.io/gitea/models/issues"
- project_model "code.gitea.io/gitea/models/project"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models"
+ issues_model "forgejo.org/models/issues"
+ project_model "forgejo.org/models/project"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
@@ -188,8 +188,8 @@ type RepoUnitSettingForm struct {
PullsAllowSquash bool
PullsAllowFastForwardOnly bool
PullsAllowManualMerge bool
- PullsDefaultMergeStyle string
- PullsDefaultUpdateStyle string
+ PullsDefaultMergeStyle string `binding:"In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged,rebase-update-only)"`
+ PullsDefaultUpdateStyle string `binding:"In(merge,rebase)"`
EnableAutodetectManualMerge bool
PullsAllowRebaseUpdate bool
DefaultDeleteBranchAfterMerge bool
@@ -277,6 +277,9 @@ type WebhookCoreForm struct {
Wiki bool
Repository bool
Package bool
+ ActionFailure bool
+ ActionRecover bool
+ ActionSuccess bool
Active bool
BranchFilter string `binding:"GlobPattern"`
AuthorizationHeader string
@@ -725,8 +728,8 @@ func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) bi
// AddTimeManuallyForm form that adds spent time manually.
type AddTimeManuallyForm struct {
- Hours int `binding:"Range(0,1000)"`
- Minutes int `binding:"Range(0,1000)"`
+ Hours int `binding:"Range(0,1000)" locale:"repo.issues.add_time_hours"`
+ Minutes int `binding:"Range(0,1000)" locale:"repo.issues.add_time_minutes"`
}
// Validate validates the fields
@@ -740,17 +743,6 @@ type SaveTopicForm struct {
Topics []string `binding:"topics;Required;"`
}
-// DeadlineForm hold the validation rules for deadlines
-type DeadlineForm struct {
- DateString string `form:"date" binding:"Required;Size(10)"`
-}
-
-// Validate validates the fields
-func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
type CommitNotesForm struct {
Notes string
}
diff --git a/services/forms/repo_form_test.go b/services/forms/repo_form_test.go
index 2c5a8e2c0f..4047762096 100644
--- a/services/forms/repo_form_test.go
+++ b/services/forms/repo_form_test.go
@@ -6,7 +6,7 @@ package forms
import (
"testing"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
)
diff --git a/services/forms/repo_tag_form.go b/services/forms/repo_tag_form.go
index 38f5996db3..1254c84d07 100644
--- a/services/forms/repo_tag_form.go
+++ b/services/forms/repo_tag_form.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/report_abuse.go b/services/forms/report_abuse.go
new file mode 100644
index 0000000000..5e9d7dc45f
--- /dev/null
+++ b/services/forms/report_abuse.go
@@ -0,0 +1,28 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package forms
+
+import (
+ "net/http"
+
+ "forgejo.org/models/moderation"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
+
+ "code.forgejo.org/go-chi/binding"
+)
+
+// ReportAbuseForm is used to interact with the UI of the form that submits new abuse reports.
+type ReportAbuseForm struct {
+ ContentID int64
+ ContentType moderation.ReportedContentType
+ AbuseCategory moderation.AbuseCategoryType `binding:"Required" locale:"moderation.abuse_category"`
+ Remarks string `binding:"Required;MinSize(20);MaxSize(500)" preprocess:"TrimSpace" locale:"moderation.report_remarks"`
+}
+
+// Validate validates the fields of ReportAbuseForm.
+func (f *ReportAbuseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
+ ctx := context.GetValidateContext(req)
+ return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
+}
diff --git a/services/forms/runner.go b/services/forms/runner.go
index f933750858..fcf6c5a694 100644
--- a/services/forms/runner.go
+++ b/services/forms/runner.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index 3ba8724c92..dfd5b3da9b 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -9,12 +9,12 @@ import (
"net/http"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/validation"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
@@ -224,6 +224,7 @@ type UpdateProfileForm struct {
Biography string `binding:"MaxSize(255)"`
Visibility structs.VisibleType
KeepActivityPrivate bool
+ KeepPronounsPrivate bool
}
// Validate validates the fields
@@ -290,7 +291,7 @@ func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) bindi
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// IsThemeExists checks if the theme is a theme available in the config.
+// IsThemeExists checks if the theme is available in the config.
func (f UpdateThemeForm) IsThemeExists() bool {
var exists bool
diff --git a/services/forms/user_form_auth_openid.go b/services/forms/user_form_auth_openid.go
index c5ab703fa1..02d4f873bc 100644
--- a/services/forms/user_form_auth_openid.go
+++ b/services/forms/user_form_auth_openid.go
@@ -6,8 +6,8 @@ package forms
import (
"net/http"
- "code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/context"
"code.forgejo.org/go-chi/binding"
)
diff --git a/services/forms/user_form_hidden_comments.go b/services/forms/user_form_hidden_comments.go
index b9677c1800..74a1aaccb0 100644
--- a/services/forms/user_form_hidden_comments.go
+++ b/services/forms/user_form_hidden_comments.go
@@ -6,9 +6,9 @@ package forms
import (
"math/big"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/context"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
)
type hiddenCommentTypeGroupsType map[string][]issues_model.CommentType
diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go
index 66050187c9..ae08f65f23 100644
--- a/services/forms/user_form_test.go
+++ b/services/forms/user_form_test.go
@@ -7,20 +7,16 @@ import (
"strconv"
"testing"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/setting"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/gobwas/glob"
"github.com/stretchr/testify/assert"
)
func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = nil
+ defer test.MockVariableValue(&setting.Service.EmailDomainAllowList, nil)()
form := RegisterForm{}
@@ -28,12 +24,7 @@ func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainAllowList, []glob.Glob{glob.MustCompile("gitea.io")})()
tt := []struct {
email string
@@ -50,12 +41,7 @@ func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.allow")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainAllowList, []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.allow")})()
tt := []struct {
email string
@@ -78,13 +64,7 @@ func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = nil
- setting.Service.EmailDomainBlockList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.block")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainBlockList, []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.block")})()
tt := []struct {
email string
diff --git a/services/gitdiff/csv_test.go b/services/gitdiff/csv_test.go
index 1dbe616374..9bffba33fd 100644
--- a/services/gitdiff/csv_test.go
+++ b/services/gitdiff/csv_test.go
@@ -8,9 +8,9 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/models/db"
- csv_module "code.gitea.io/gitea/modules/csv"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ csv_module "forgejo.org/modules/csv"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 7d137fb214..7033264f18 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -17,19 +17,19 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- pull_model "code.gitea.io/gitea/models/pull"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/analyze"
- "code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/highlight"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ pull_model "forgejo.org/models/pull"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/analyze"
+ "forgejo.org/modules/charset"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/highlight"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
"github.com/sergi/go-diff/diffmatchpatch"
stdcharset "golang.org/x/net/html/charset"
@@ -440,11 +440,29 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int {
if err != nil {
return 0
}
- lineCount, err := blob.GetBlobLineCount()
+ reader, err := blob.DataAsync()
if err != nil {
return 0
}
- return lineCount
+ defer reader.Close()
+ buf := make([]byte, 32*1024)
+ count := 1
+ lineSep := []byte{'\n'}
+
+ c, err := reader.Read(buf)
+ if c == 0 && err == io.EOF {
+ return 0
+ }
+ for {
+ count += bytes.Count(buf[:c], lineSep)
+ switch {
+ case err == io.EOF:
+ return count
+ case err != nil:
+ return count
+ }
+ c, err = reader.Read(buf)
+ }
}
// Diff represents a difference between two git trees.
@@ -1060,7 +1078,7 @@ func readFileName(rd *strings.Reader) (string, bool) {
_, _ = fmt.Fscanf(rd, "%s ", &name)
char, _ := rd.ReadByte()
_ = rd.UnreadByte()
- for !(char == 0 || char == '"' || char == 'b') {
+ for char != 0 && char != '"' && char != 'b' {
var suffix string
_, _ = fmt.Fscanf(rd, "%s ", &suffix)
name += " " + suffix
@@ -1088,60 +1106,63 @@ type DiffOptions struct {
FileOnly bool
}
-// GetDiff builds a Diff between two commits of a repository.
+// GetDiffSimple builds a Diff between two commits of a repository.
// Passing the empty string as beforeCommitID returns a diff from the parent commit.
-// The whitespaceBehavior is either an empty string or a git flag
-func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
- repoPath := gitRepo.Path
-
- var beforeCommit *git.Commit
- commit, err := gitRepo.GetCommit(opts.AfterCommitID)
+// The whitespaceBehavior is either an empty string or a git flag.
+func GetDiffSimple(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (diff *Diff, afterCommit *git.Commit, err error) {
+ afterCommit, err = gitRepo.GetCommit(opts.AfterCommitID)
if err != nil {
- return nil, err
+ return nil, nil, fmt.Errorf("unable to get the after commit %q: %w", opts.AfterCommitID, err)
}
cmdCtx, cmdCancel := context.WithCancel(ctx)
defer cmdCancel()
- cmdDiff := git.NewCommand(cmdCtx)
+ cmdDiff := git.NewCommand(cmdCtx).
+ AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
+ AddArguments(opts.WhitespaceBehavior...)
+
objectFormat, err := gitRepo.GetObjectFormat()
if err != nil {
- return nil, err
+ return nil, nil, fmt.Errorf("not able to determine the object format: %w", err)
}
- if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String()) && commit.ParentCount() == 0 {
- cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
- AddArguments(opts.WhitespaceBehavior...).
- AddDynamicArguments(objectFormat.EmptyTree().String()).
- AddDynamicArguments(opts.AfterCommitID)
+ // If before commit is empty or the empty object and the after commit has no
+ // parents, then use the empty tree as before commit.
+ //
+ // This is the case for a 'initial commit' of a Git tree, which obviously has
+ // no parents.
+ if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String()) && afterCommit.ParentCount() == 0 {
+ // Reset before commit ID to indicate empty tree was used.
+ opts.BeforeCommitID = ""
+ // Add enpty tree as before commit.
+ cmdDiff.AddDynamicArguments(objectFormat.EmptyTree().String())
} else {
- actualBeforeCommitID := opts.BeforeCommitID
- if len(actualBeforeCommitID) == 0 {
- parentCommit, _ := commit.Parent(0)
- actualBeforeCommitID = parentCommit.ID.String()
+ // If before commit ID is empty, use the first parent of the after commit.
+ if len(opts.BeforeCommitID) == 0 {
+ parentCommit, err := afterCommit.Parent(0)
+ if err != nil {
+ return nil, nil, fmt.Errorf("not able to get first parent of %q: %w", afterCommit.ID.String(), err)
+ }
+ opts.BeforeCommitID = parentCommit.ID.String()
}
- cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
- AddArguments(opts.WhitespaceBehavior...).
- AddDynamicArguments(actualBeforeCommitID, opts.AfterCommitID)
- opts.BeforeCommitID = actualBeforeCommitID
-
- var err error
- beforeCommit, err = gitRepo.GetCommit(opts.BeforeCommitID)
- if err != nil {
- return nil, err
- }
+ cmdDiff.AddDynamicArguments(opts.BeforeCommitID)
}
+ // Add the after commit to the diff command.
+ cmdDiff.AddDynamicArguments(opts.AfterCommitID)
+
// In git 2.31, git diff learned --skip-to which we can use to shortcut skip to file
// so if we are using at least this version of git we don't have to tell ParsePatch to do
// the skipping for us
parsePatchSkipToFile := opts.SkipTo
- if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil {
+ if opts.SkipTo != "" {
cmdDiff.AddOptionFormat("--skip-to=%s", opts.SkipTo)
parsePatchSkipToFile = ""
}
+ // If we only want to diff for some files, add that as well.
cmdDiff.AddDashesAndList(files...)
reader, writer := io.Pipe()
@@ -1151,6 +1172,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
}()
go func() {
+ repoPath := gitRepo.Path
stderr := &bytes.Buffer{}
cmdDiff.SetDescription(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
if err := cmdDiff.Run(&git.RunOpts{
@@ -1165,14 +1187,33 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
_ = writer.Close()
}()
- diff, err := ParsePatch(cmdCtx, opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, reader, parsePatchSkipToFile)
+ diff, err = ParsePatch(cmdCtx, opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, reader, parsePatchSkipToFile)
// Ensure the git process is killed if it didn't exit already
cmdCancel()
if err != nil {
- return nil, fmt.Errorf("unable to ParsePatch: %w", err)
+ return nil, nil, fmt.Errorf("unable to parse a git diff: %w", err)
}
diff.Start = opts.SkipTo
+ return diff, afterCommit, nil
+}
+
+func GetDiffFull(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
+ diff, afterCommit, err := GetDiffSimple(ctx, gitRepo, opts, files...)
+ if err != nil {
+ return nil, err
+ }
+
+ // If there's a before commit, then GetDiffSimple will set it, otherwise it
+ // is empty.
+ var beforeCommit *git.Commit
+ if len(opts.BeforeCommitID) != 0 {
+ beforeCommit, err = gitRepo.GetCommit(opts.BeforeCommitID)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get before commit %q: %w", opts.BeforeCommitID, err)
+ }
+ }
+
checker, err := gitRepo.GitAttributeChecker(opts.AfterCommitID, git.LinguistAttributes...)
if err != nil {
return nil, fmt.Errorf("unable to GitAttributeChecker: %w", err)
@@ -1208,7 +1249,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
diffFile.IsGenerated = analyze.IsGenerated(diffFile.Name)
}
- tailSection := diffFile.GetTailSection(gitRepo, beforeCommit, commit)
+ tailSection := diffFile.GetTailSection(gitRepo, beforeCommit, afterCommit)
if tailSection != nil {
diffFile.Sections = append(diffFile.Sections, tailSection)
}
@@ -1270,7 +1311,7 @@ func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStat
// SyncAndGetUserSpecificDiff is like GetDiff, except that user specific data such as which files the given user has already viewed on the given PR will also be set
// Additionally, the database asynchronously is updated if files have changed since the last review
func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_model.PullRequest, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
- diff, err := GetDiff(ctx, gitRepo, opts, files...)
+ diff, err := GetDiffFull(ctx, gitRepo, opts, files...)
if err != nil {
return nil, err
}
@@ -1379,10 +1420,8 @@ func GetWhitespaceFlag(whitespaceBehavior string) git.TrustedCmdArgs {
"ignore-eol": {"--ignore-space-at-eol"},
"show-all": nil,
}
-
if flag, ok := whitespaceFlags[whitespaceBehavior]; ok {
return flag
}
- log.Warn("unknown whitespace behavior: %q, default to 'show-all'", whitespaceBehavior)
return nil
}
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index f2c099d554..695b177b8b 100644
--- a/services/gitdiff/gitdiff_test.go
+++ b/services/gitdiff/gitdiff_test.go
@@ -9,13 +9,13 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
@@ -635,7 +635,7 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
defer gitRepo.Close()
for _, behavior := range []git.TrustedCmdArgs{{"-w"}, {"--ignore-space-at-eol"}, {"-b"}, nil} {
- diffs, err := GetDiff(db.DefaultContext, gitRepo,
+ diffs, _, err := GetDiffSimple(db.DefaultContext, gitRepo,
&DiffOptions{
AfterCommitID: "bd7063cc7c04689c4d082183d32a604ed27a24f9",
BeforeCommitID: "559c156f8e0178b71cb44355428f24001b08fc68",
@@ -651,6 +651,72 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
}
}
+func TestGetDiffFull(t *testing.T) {
+ gitRepo, err := git.OpenRepository(git.DefaultContext, "./../../modules/git/tests/repos/language_stats_repo")
+ require.NoError(t, err)
+
+ defer gitRepo.Close()
+
+ t.Run("Initial commit", func(t *testing.T) {
+ diff, err := GetDiffFull(db.DefaultContext, gitRepo,
+ &DiffOptions{
+ AfterCommitID: "8fee858da5796dfb37704761701bb8e800ad9ef3",
+ MaxLines: setting.Git.MaxGitDiffLines,
+ MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
+ MaxFiles: setting.Git.MaxGitDiffFiles,
+ })
+ require.NoError(t, err)
+
+ assert.Empty(t, diff.Start)
+ assert.Empty(t, diff.End)
+ assert.False(t, diff.IsIncomplete)
+ assert.Equal(t, 5, diff.NumFiles)
+ assert.Equal(t, 23, diff.TotalAddition)
+ assert.Len(t, diff.Files, 5)
+
+ assert.True(t, diff.Files[0].IsVendored)
+ assert.Equal(t, ".gitattributes", diff.Files[0].Name)
+ assert.Equal(t, "24139dae656713ba861751fb2c2ac38839349a7a", diff.Files[0].NameHash)
+
+ assert.Equal(t, "Python", diff.Files[1].Language)
+ assert.Equal(t, "i-am-a-python.p", diff.Files[1].Name)
+ assert.Equal(t, "32154957b043de62cbcdbe254a53ec4c3e00c5a0", diff.Files[1].NameHash)
+
+ assert.Equal(t, "java-hello/main.java", diff.Files[2].Name)
+ assert.Equal(t, "ef9f6a406a4cde7bb5480ba7b027bdc8cd6fa11d", diff.Files[2].NameHash)
+
+ assert.Equal(t, "main.vendor.java", diff.Files[3].Name)
+ assert.Equal(t, "c94fd7272f109d4d21d6df2b637c864a5ab63f46", diff.Files[3].NameHash)
+
+ assert.Equal(t, "python-hello/hello.py", diff.Files[4].Name)
+ assert.Equal(t, "021705ba8b98778dc4e277d3a6ea1b8c6122a7f9", diff.Files[4].NameHash)
+ })
+
+ t.Run("Normal diff", func(t *testing.T) {
+ diff, err := GetDiffFull(db.DefaultContext, gitRepo,
+ &DiffOptions{
+ AfterCommitID: "341fca5b5ea3de596dc483e54c2db28633cd2f97",
+ BeforeCommitID: "8fee858da5796dfb37704761701bb8e800ad9ef3",
+ MaxLines: setting.Git.MaxGitDiffLines,
+ MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
+ MaxFiles: setting.Git.MaxGitDiffFiles,
+ })
+ require.NoError(t, err)
+
+ assert.Empty(t, diff.Start)
+ assert.Empty(t, diff.End)
+ assert.False(t, diff.IsIncomplete)
+ assert.Equal(t, 1, diff.NumFiles)
+ assert.Equal(t, 1, diff.TotalAddition)
+ assert.Len(t, diff.Files, 1)
+
+ assert.Equal(t, ".gitattributes", diff.Files[0].Name)
+ assert.Equal(t, "24139dae656713ba861751fb2c2ac38839349a7a", diff.Files[0].NameHash)
+ assert.Len(t, diff.Files[0].Sections, 2)
+ assert.Equal(t, 4, diff.Files[0].Sections[1].Lines[0].SectionInfo.LeftIdx)
+ })
+}
+
func TestNoCrashes(t *testing.T) {
type testcase struct {
gitdiff string
diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go
index c72959ea16..61d52d91e6 100644
--- a/services/gitdiff/highlightdiff.go
+++ b/services/gitdiff/highlightdiff.go
@@ -6,7 +6,7 @@ package gitdiff
import (
"strings"
- "code.gitea.io/gitea/modules/highlight"
+ "forgejo.org/modules/highlight"
"github.com/sergi/go-diff/diffmatchpatch"
)
@@ -14,13 +14,14 @@ import (
// token is a html tag or entity, eg: "", "", "<"
func extractHTMLToken(s string) (before, token, after string, valid bool) {
for pos1 := 0; pos1 < len(s); pos1++ {
- if s[pos1] == '<' {
+ switch s[pos1] {
+ case '<':
pos2 := strings.IndexByte(s[pos1:], '>')
if pos2 == -1 {
return "", "", s, false
}
return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true
- } else if s[pos1] == '&' {
+ case '&':
pos2 := strings.IndexByte(s[pos1:], ';')
if pos2 == -1 {
return "", "", s, false
diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go
index 2ff4472bcc..0070173b9f 100644
--- a/services/gitdiff/highlightdiff_test.go
+++ b/services/gitdiff/highlightdiff_test.go
@@ -43,7 +43,7 @@ func TestDiffWithHighlight(t *testing.T) {
diff.Text = "C"
hcd.recoverOneDiff(&diff)
- assert.Equal(t, "", diff.Text)
+ assert.Empty(t, diff.Text)
}
func TestDiffWithHighlightPlaceholder(t *testing.T) {
@@ -53,8 +53,8 @@ func TestDiffWithHighlightPlaceholder(t *testing.T) {
"a='\U00100000'",
"a='\U0010FFFD''",
)
- assert.Equal(t, "", hcd.PlaceholderTokenMap[0x00100000])
- assert.Equal(t, "", hcd.PlaceholderTokenMap[0x0010FFFD])
+ assert.Empty(t, hcd.PlaceholderTokenMap[0x00100000])
+ assert.Empty(t, hcd.PlaceholderTokenMap[0x0010FFFD])
expected := fmt.Sprintf(`a='%s'`, "\U00100000")
output := diffToHTML(hcd.lineWrapperTags, diffs, DiffLineDel)
diff --git a/services/gitdiff/main_test.go b/services/gitdiff/main_test.go
index 3d4d480530..cd7a6a4a6b 100644
--- a/services/gitdiff/main_test.go
+++ b/services/gitdiff/main_test.go
@@ -6,12 +6,12 @@ package gitdiff
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/activities"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/activities"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/indexer/indexer.go b/services/indexer/indexer.go
index 38dd012a51..92036f95c3 100644
--- a/services/indexer/indexer.go
+++ b/services/indexer/indexer.go
@@ -4,10 +4,10 @@
package indexer
import (
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- stats_indexer "code.gitea.io/gitea/modules/indexer/stats"
- notify_service "code.gitea.io/gitea/services/notify"
+ code_indexer "forgejo.org/modules/indexer/code"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ stats_indexer "forgejo.org/modules/indexer/stats"
+ notify_service "forgejo.org/services/notify"
)
// Init initialize the repo indexer
diff --git a/services/indexer/notify.go b/services/indexer/notify.go
index e2cfe477d3..ddd89f733c 100644
--- a/services/indexer/notify.go
+++ b/services/indexer/notify.go
@@ -6,16 +6,16 @@ package indexer
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- stats_indexer "code.gitea.io/gitea/modules/indexer/stats"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- notify_service "code.gitea.io/gitea/services/notify"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ code_indexer "forgejo.org/modules/indexer/code"
+ issue_indexer "forgejo.org/modules/indexer/issues"
+ stats_indexer "forgejo.org/modules/indexer/stats"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ notify_service "forgejo.org/services/notify"
)
type indexerNotifier struct {
diff --git a/services/issue/assignee.go b/services/issue/assignee.go
index 3d6d0b881a..a5f9c2731f 100644
--- a/services/issue/assignee.go
+++ b/services/issue/assignee.go
@@ -6,15 +6,15 @@ package issue
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- notify_service "code.gitea.io/gitea/services/notify"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ notify_service "forgejo.org/services/notify"
)
// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
diff --git a/services/issue/assignee_test.go b/services/issue/assignee_test.go
index 2b70b8c8ce..66a66459cb 100644
--- a/services/issue/assignee_test.go
+++ b/services/issue/assignee_test.go
@@ -6,10 +6,10 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/issue/comments.go b/services/issue/comments.go
index 3ab577b83f..2cac900d41 100644
--- a/services/issue/comments.go
+++ b/services/issue/comments.go
@@ -5,20 +5,21 @@ package issue
import (
"context"
+ "errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/timeutil"
+ notify_service "forgejo.org/services/notify"
)
// CreateRefComment creates a commit reference comment to issue.
func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error {
if len(commitSHA) == 0 {
- return fmt.Errorf("cannot create reference with empty commit SHA")
+ return errors.New("cannot create reference with empty commit SHA")
}
// Check if same reference from same commit has already existed.
@@ -119,7 +120,28 @@ func UpdateComment(ctx context.Context, c *issues_model.Comment, contentVersion
// DeleteComment deletes the comment
func DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) error {
err := db.WithTx(ctx, func(ctx context.Context) error {
- return issues_model.DeleteComment(ctx, comment)
+ reviewID := comment.ReviewID
+
+ err := issues_model.DeleteComment(ctx, comment)
+ if err != nil {
+ return err
+ }
+
+ if comment.Review != nil {
+ reviewType := comment.Review.Type
+ if reviewType == issues_model.ReviewTypePending {
+ found, err := db.GetEngine(ctx).Table("comment").Where("review_id = ?", reviewID).Exist()
+ if err != nil {
+ return err
+ } else if !found {
+ _, err := db.GetEngine(ctx).Table("review").Where("id = ?", reviewID).Delete()
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
})
if err != nil {
return err
diff --git a/services/issue/comments_test.go b/services/issue/comments_test.go
index 62547a584a..8fa410c0f0 100644
--- a/services/issue/comments_test.go
+++ b/services/issue/comments_test.go
@@ -6,17 +6,17 @@ package issue_test
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
- issue_service "code.gitea.io/gitea/services/issue"
- "code.gitea.io/gitea/tests"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ issue_service "forgejo.org/services/issue"
+ "forgejo.org/tests"
- _ "code.gitea.io/gitea/services/webhook"
+ _ "forgejo.org/services/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -48,9 +48,9 @@ func TestDeleteComment(t *testing.T) {
// Reactions don't exist anymore for this comment.
unittest.AssertNotExistsBean(t, &issues_model.Reaction{CommentID: comment.ID})
// Number of comments was decreased.
- assert.EqualValues(t, issue.NumComments-1, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments)
+ assert.Equal(t, issue.NumComments-1, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments)
// A notification was fired for the deletion of this comment.
- assert.EqualValues(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{}))
+ assert.Equal(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{}))
})
t.Run("Comment of pending review", func(t *testing.T) {
@@ -59,7 +59,7 @@ func TestDeleteComment(t *testing.T) {
// We have to ensure that this comment's linked review is pending.
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4}, "review_id != 0")
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: comment.ReviewID})
- assert.EqualValues(t, issues_model.ReviewTypePending, review.Type)
+ assert.Equal(t, issues_model.ReviewTypePending, review.Type)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
require.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, &webhook_model.Webhook{
@@ -69,14 +69,17 @@ func TestDeleteComment(t *testing.T) {
}))
hookTaskCount := unittest.GetCount(t, &webhook_model.HookTask{})
+ require.NoError(t, comment.LoadReview(t.Context()))
require.NoError(t, issue_service.DeleteComment(db.DefaultContext, nil, comment))
// The comment doesn't exist anymore.
unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID})
// Ensure that the number of comments wasn't decreased.
- assert.EqualValues(t, issue.NumComments, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments)
+ assert.Equal(t, issue.NumComments, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments)
// No notification was fired for the deletion of this comment.
- assert.EqualValues(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{}))
+ assert.Equal(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{}))
+ // The review doesn't exist anymore.
+ unittest.AssertNotExistsBean(t, &issues_model.Review{ID: comment.ReviewID})
})
}
@@ -105,11 +108,11 @@ func TestUpdateComment(t *testing.T) {
newComment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
// Content was updated.
- assert.EqualValues(t, comment.Content, newComment.Content)
+ assert.Equal(t, comment.Content, newComment.Content)
// Content version was updated.
- assert.EqualValues(t, 2, newComment.ContentVersion)
+ assert.Equal(t, 2, newComment.ContentVersion)
// A notification was fired for the update of this comment.
- assert.EqualValues(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{}))
+ assert.Equal(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{}))
// Issue history was saved for this comment.
unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{CommentID: comment.ID, IsFirstCreated: true, ContentText: oldContent})
unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{CommentID: comment.ID, ContentText: comment.Content}, "is_first_created = false")
@@ -120,7 +123,7 @@ func TestUpdateComment(t *testing.T) {
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4}, "review_id != 0")
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: comment.ReviewID})
- assert.EqualValues(t, issues_model.ReviewTypePending, review.Type)
+ assert.Equal(t, issues_model.ReviewTypePending, review.Type)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID})
require.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, &webhook_model.Webhook{
@@ -136,11 +139,11 @@ func TestUpdateComment(t *testing.T) {
newComment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
// Content was updated.
- assert.EqualValues(t, comment.Content, newComment.Content)
+ assert.Equal(t, comment.Content, newComment.Content)
// Content version was updated.
- assert.EqualValues(t, 2, newComment.ContentVersion)
+ assert.Equal(t, 2, newComment.ContentVersion)
// No notification was fired for the update of this comment.
- assert.EqualValues(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{}))
+ assert.Equal(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{}))
// Issue history was not saved for this comment.
unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID})
})
diff --git a/services/issue/commit.go b/services/issue/commit.go
index 8b927d52b6..1e51fb32b7 100644
--- a/services/issue/commit.go
+++ b/services/issue/commit.go
@@ -13,15 +13,15 @@ import (
"strings"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/references"
- "code.gitea.io/gitea/modules/repository"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/references"
+ "forgejo.org/modules/repository"
)
const (
diff --git a/services/issue/commit_test.go b/services/issue/commit_test.go
index c3c3e4c042..e3a41d2305 100644
--- a/services/issue/commit_test.go
+++ b/services/issue/commit_test.go
@@ -6,14 +6,14 @@ package issue
import (
"testing"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/require"
)
diff --git a/services/issue/content.go b/services/issue/content.go
index 612a9a6b4c..d5c79e5fde 100644
--- a/services/issue/content.go
+++ b/services/issue/content.go
@@ -6,9 +6,9 @@ package issue
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- notify_service "code.gitea.io/gitea/services/notify"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ notify_service "forgejo.org/services/notify"
)
// ChangeContent changes issue content, as the given user.
diff --git a/services/issue/issue.go b/services/issue/issue.go
index 5e726176d0..7071a912b0 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -1,26 +1,28 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issue
import (
"context"
+ "errors"
"fmt"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/timeutil"
- notify_service "code.gitea.io/gitea/services/notify"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ project_model "forgejo.org/models/project"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/timeutil"
+ notify_service "forgejo.org/services/notify"
)
// NewIssue creates new issue with labels for repository.
@@ -59,7 +61,6 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo
// ChangeTitle changes the title of this issue, as the given user.
func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, title string) error {
oldTitle := issue.Title
- issue.Title = title
if oldTitle == title {
return nil
@@ -73,6 +74,12 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
return user_model.ErrBlockedByUser
}
+ // If the issue was reported as abusive, a shadow copy should be created before first update.
+ if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil {
+ return err
+ }
+
+ issue.Title = title
if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil {
return err
}
@@ -252,6 +259,12 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
defer committer.Close()
e := db.GetEngine(ctx)
+
+ // If the issue was reported as abusive, a shadow copy should be created before deletion.
+ if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil {
+ return err
+ }
+
if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
return err
}
@@ -333,13 +346,13 @@ func SetIssueUpdateDate(ctx context.Context, issue *issues_model.Issue, updated
return err
}
if !perm.IsAdmin() && !perm.IsOwner() {
- return fmt.Errorf("user needs to have admin or owner right")
+ return errors.New("user needs to have admin or owner right")
}
// A simple guard against potential inconsistent calls
updatedUnix := timeutil.TimeStamp(updated.Unix())
if updatedUnix < issue.CreatedUnix || updatedUnix > timeutil.TimeStampNow() {
- return fmt.Errorf("unallowed update date")
+ return errors.New("unallowed update date")
}
issue.UpdatedUnix = updatedUnix
diff --git a/services/issue/issue_test.go b/services/issue/issue_test.go
index a0bb88e387..fb2b2870bd 100644
--- a/services/issue/issue_test.go
+++ b/services/issue/issue_test.go
@@ -6,11 +6,11 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,8 +25,8 @@ func TestGetRefEndNamesAndURLs(t *testing.T) {
repoLink := "/foo/bar"
endNames, urls := GetRefEndNamesAndURLs(issues, repoLink)
- assert.EqualValues(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames)
- assert.EqualValues(t, map[int64]string{
+ assert.Equal(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames)
+ assert.Equal(t, map[int64]string{
1: repoLink + "/src/branch/branch1",
2: repoLink + "/src/tag/tag1",
3: repoLink + "/src/commit/c0ffee",
diff --git a/services/issue/label.go b/services/issue/label.go
index 6b8070d8aa..f18dd67a19 100644
--- a/services/issue/label.go
+++ b/services/issue/label.go
@@ -6,11 +6,10 @@ package issue
import (
"context"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- user_model "code.gitea.io/gitea/models/user"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ notify_service "forgejo.org/services/notify"
)
// ClearLabels clears all of an issue's labels
@@ -56,17 +55,6 @@ func RemoveLabel(ctx context.Context, issue *issues_model.Issue, doer *user_mode
return err
}
- perm, err := access_model.GetUserRepoPermission(dbCtx, issue.Repo, doer)
- if err != nil {
- return err
- }
- if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
- if label.OrgID > 0 {
- return issues_model.ErrOrgLabelNotExist{}
- }
- return issues_model.ErrRepoLabelNotExist{}
- }
-
if err := issues_model.DeleteIssueLabel(dbCtx, issue, label, doer); err != nil {
return err
}
diff --git a/services/issue/label_test.go b/services/issue/label_test.go
index b9d26345c1..73a028684b 100644
--- a/services/issue/label_test.go
+++ b/services/issue/label_test.go
@@ -6,10 +6,10 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/require"
)
diff --git a/services/issue/main_test.go b/services/issue/main_test.go
index c3da441537..673ec5e4cc 100644
--- a/services/issue/main_test.go
+++ b/services/issue/main_test.go
@@ -6,11 +6,11 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/webhook"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/webhook"
- _ "code.gitea.io/gitea/models/actions"
+ _ "forgejo.org/models/actions"
)
func TestMain(m *testing.M) {
diff --git a/services/issue/milestone.go b/services/issue/milestone.go
index 407ad0a59b..a561bf8eee 100644
--- a/services/issue/milestone.go
+++ b/services/issue/milestone.go
@@ -5,12 +5,13 @@ package issue
import (
"context"
+ "errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ notify_service "forgejo.org/services/notify"
)
func updateMilestoneCounters(ctx context.Context, issue *issues_model.Issue, id int64) error {
@@ -47,7 +48,7 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is
return fmt.Errorf("HasMilestoneByRepoID: %w", err)
}
if !has {
- return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist")
+ return errors.New("HasMilestoneByRepoID: issue doesn't exist")
}
}
diff --git a/services/issue/milestone_test.go b/services/issue/milestone_test.go
index e75f64550c..4123433c2a 100644
--- a/services/issue/milestone_test.go
+++ b/services/issue/milestone_test.go
@@ -6,10 +6,10 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/issue/pull.go b/services/issue/pull.go
index 3b61c00afa..2eef1fbfa8 100644
--- a/services/issue/pull.go
+++ b/services/issue/pull.go
@@ -8,15 +8,15 @@ import (
"fmt"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- org_model "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ issues_model "forgejo.org/models/issues"
+ org_model "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) {
@@ -43,8 +43,6 @@ type ReviewRequestNotifier struct {
}
func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) {
- files := []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}
-
if pr.IsWorkInProgress(ctx) {
return nil, nil
}
@@ -72,18 +70,17 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
return nil, err
}
- var data string
- for _, file := range files {
+ var rules []*issues_model.CodeOwnerRule
+ for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} {
if blob, err := commit.GetBlobByPath(file); err == nil {
- data, err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize)
+ rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize)
if err == nil {
+ rules, _ = issues_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize)
break
}
}
}
- rules, _ := issues_model.GetCodeOwnersFromContent(ctx, data)
-
// get the mergebase
mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName())
if err != nil {
diff --git a/services/issue/reaction.go b/services/issue/reaction.go
index dbb4735de2..c6a11aa0f0 100644
--- a/services/issue/reaction.go
+++ b/services/issue/reaction.go
@@ -5,8 +5,8 @@ package issue
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
)
// CreateIssueReaction creates a reaction on issue.
diff --git a/services/issue/status.go b/services/issue/status.go
index 9b6c683f4f..6664da7daa 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -6,10 +6,10 @@ package issue
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- notify_service "code.gitea.io/gitea/services/notify"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ notify_service "forgejo.org/services/notify"
)
// ChangeStatus changes issue status to open or closed.
diff --git a/services/issue/template.go b/services/issue/template.go
index 9a2b048401..67a01825d2 100644
--- a/services/issue/template.go
+++ b/services/issue/template.go
@@ -10,11 +10,11 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/issue/template"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/issue/template"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
"gopkg.in/yaml.v3"
)
diff --git a/services/lfs/locks.go b/services/lfs/locks.go
index 2a362b1c0d..a45b2cc93b 100644
--- a/services/lfs/locks.go
+++ b/services/lfs/locks.go
@@ -8,16 +8,16 @@ import (
"strconv"
"strings"
- auth_model "code.gitea.io/gitea/models/auth"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/json"
- lfs_module "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ auth_model "forgejo.org/models/auth"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/json"
+ lfs_module "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
)
func handleLockListOut(ctx *context.Context, repo *repo_model.Repository, lock *git_model.LFSLock, err error) {
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 51d6f42776..17e6d0eec7 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -18,21 +18,21 @@ import (
"strconv"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- lfs_module "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/services/context"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ lfs_module "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/services/context"
"github.com/golang-jwt/jwt/v5"
)
@@ -163,11 +163,12 @@ func BatchHandler(ctx *context.Context) {
}
var isUpload bool
- if br.Operation == "upload" {
+ switch br.Operation {
+ case "upload":
isUpload = true
- } else if br.Operation == "download" {
+ case "download":
isUpload = false
- } else {
+ default:
log.Trace("Attempt to BATCH with invalid operation: %s", br.Operation)
writeStatus(ctx, http.StatusBadRequest)
return
@@ -594,15 +595,15 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo
claims, claimsOk := token.Claims.(*Claims)
if !token.Valid || !claimsOk {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
if claims.RepoID != target.ID {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
if mode == perm.AccessModeWrite && claims.Op != "upload" {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
u, err := user_model.GetUserByID(ctx, claims.UserID)
@@ -615,12 +616,12 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo
func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) {
if authorization == "" {
- return nil, fmt.Errorf("no token")
+ return nil, errors.New("no token")
}
parts := strings.SplitN(authorization, " ", 2)
if len(parts) != 2 {
- return nil, fmt.Errorf("no token")
+ return nil, errors.New("no token")
}
tokenSHA := parts[1]
switch strings.ToLower(parts[0]) {
@@ -629,7 +630,7 @@ func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Rep
case "token":
return handleLFSToken(ctx, tokenSHA, target, mode)
}
- return nil, fmt.Errorf("token not found")
+ return nil, errors.New("token not found")
}
func requireAuth(ctx *context.Context) {
diff --git a/services/mailer/incoming/incoming.go b/services/mailer/incoming/incoming.go
index 249dac66cd..b1b9191df3 100644
--- a/services/mailer/incoming/incoming.go
+++ b/services/mailer/incoming/incoming.go
@@ -7,15 +7,17 @@ import (
"context"
"crypto/tls"
"fmt"
+ "mime"
net_mail "net/mail"
"regexp"
+ "slices"
"strings"
"time"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/mailer/token"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/mailer/token"
"code.forgejo.org/forgejo/reply"
"github.com/emersion/go-imap"
@@ -297,6 +299,10 @@ func isAutomaticReply(env *enmime.Envelope) bool {
if autoReply == "yes" {
return true
}
+ precedence := env.GetHeader("Precedence")
+ if precedence == "auto_reply" {
+ return true
+ }
autoRespond := env.GetHeader("X-Autorespond")
return autoRespond != ""
}
@@ -370,25 +376,56 @@ type Attachment struct {
// getContentFromMailReader grabs the plain content and the attachments from the mail.
// A potential reply/signature gets stripped from the content.
func getContentFromMailReader(env *enmime.Envelope) *MailContent {
+ // get attachments
attachments := make([]*Attachment, 0, len(env.Attachments))
for _, attachment := range env.Attachments {
attachments = append(attachments, &Attachment{
- Name: attachment.FileName,
+ Name: constructFilename(attachment),
Content: attachment.Content,
})
}
+ // get inlines
inlineAttachments := make([]*Attachment, 0, len(env.Inlines))
for _, inline := range env.Inlines {
if inline.FileName != "" && inline.ContentType != "text/plain" {
inlineAttachments = append(inlineAttachments, &Attachment{
- Name: inline.FileName,
+ Name: constructFilename(inline),
Content: inline.Content,
})
}
}
+ // get other parts (mostly multipart/related files, these are for example embedded images in an html mail)
+ otherParts := make([]*Attachment, 0, len(env.Inlines))
+ for _, otherPart := range env.OtherParts {
+ otherParts = append(otherParts, &Attachment{
+ Name: constructFilename(otherPart),
+ Content: otherPart.Content,
+ })
+ }
return &MailContent{
Content: reply.FromText(env.Text),
- Attachments: append(attachments, inlineAttachments...),
+ Attachments: slices.Concat(attachments, inlineAttachments, otherParts),
}
}
+
+// constructFilename interprets the mime part as an (inline) attachment and returns its filename
+// If no filename is given it guesses a sensible filename for it based on the filetype.
+func constructFilename(part *enmime.Part) string {
+ if strings.TrimSpace(part.FileName) != "" {
+ return part.FileName
+ }
+
+ filenameWOExtension := "unnamed_file"
+ if strings.TrimSpace(part.ContentID) != "" {
+ filenameWOExtension = part.ContentID
+ }
+
+ fileExtension := ".unknown"
+ mimeExtensions, err := mime.ExtensionsByType(part.ContentType)
+ if err == nil && len(mimeExtensions) != 0 {
+ // just use the first one we find
+ fileExtension = mimeExtensions[0]
+ }
+ return filenameWOExtension + fileExtension
+}
diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go
index dc3c4ec69b..7505148978 100644
--- a/services/mailer/incoming/incoming_handler.go
+++ b/services/mailer/incoming/incoming_handler.go
@@ -8,19 +8,19 @@ import (
"context"
"fmt"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- attachment_service "code.gitea.io/gitea/services/attachment"
- "code.gitea.io/gitea/services/context/upload"
- issue_service "code.gitea.io/gitea/services/issue"
- incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
- "code.gitea.io/gitea/services/mailer/token"
- pull_service "code.gitea.io/gitea/services/pull"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ attachment_service "forgejo.org/services/attachment"
+ "forgejo.org/services/context/upload"
+ issue_service "forgejo.org/services/issue"
+ incoming_payload "forgejo.org/services/mailer/incoming/payload"
+ "forgejo.org/services/mailer/token"
+ pull_service "forgejo.org/services/pull"
)
type MailHandler interface {
diff --git a/services/mailer/incoming/incoming_test.go b/services/mailer/incoming/incoming_test.go
index 6101bc7e32..2ffaac57ae 100644
--- a/services/mailer/incoming/incoming_test.go
+++ b/services/mailer/incoming/incoming_test.go
@@ -4,6 +4,7 @@
package incoming
import (
+ "encoding/base64"
"strings"
"testing"
@@ -65,6 +66,12 @@ func TestIsAutomaticReply(t *testing.T) {
},
Expected: true,
},
+ {
+ Headers: map[string]string{
+ "Precedence": "auto_reply",
+ },
+ Expected: true,
+ },
}
for _, c := range cases {
@@ -188,4 +195,191 @@ func TestGetContentFromMailReader(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "mail content without signature", content.Content)
assert.Empty(t, content.Attachments)
+
+ // Some versions of Outlook send inline attachments like this, inside a multipart/related part.
+ // the attached image is from: https://openmoji.org/library/emoji-1F684
+ mailString = "Content-Type: multipart/related; boundary=\"=_related boundary=\"\r\n" +
+ "\r\n" +
+ "This text is for clients unable to decode multipart/related with multipart/alternative.\r\n" +
+ "\r\n" +
+ "--=_related boundary=\r\n" +
+ "Content-Type: multipart/alternative; boundary=\"=_alternative boundary=\"\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=\r\n" +
+ "Content-Type: text/plain\r\n" +
+ "\r\n" +
+ "This is the plaintext.\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=\r\n" +
+ "Content-Type: text/html\r\n" +
+ "\r\n" +
+ "This is a mail with multipart/related. Here is an image sent with a filename.
\r\n" +
+ "
\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=--\r\n" +
+ "\r\n" +
+ "--=_related boundary=\r\n" +
+ "Content-Transfer-Encoding: base64\r\n" +
+ "Content-Type: image/png;\r\n" +
+ " name=\"image001.png\"\r\n" +
+ "Content-ID: <_1_2845>\r\n" +
+ "\r\n" +
+ "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" +
+ "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" +
+ "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" +
+ "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" +
+ "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" +
+ "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" +
+ "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" +
+ "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" +
+ "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" +
+ "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" +
+ "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" +
+ "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" +
+ "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" +
+ "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" +
+ "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" +
+ "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" +
+ "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" +
+ "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" +
+ "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" +
+ "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" +
+ "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" +
+ "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" +
+ "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" +
+ "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" +
+ "exgAAAAASUVORK5CYII=\r\n" +
+ "\r\n" +
+ "--=_related boundary=--\r\n" +
+ "\r\n"
+
+ env, err = enmime.ReadEnvelope(strings.NewReader(mailString))
+ require.NoError(t, err)
+ content = getContentFromMailReader(env)
+ assert.Equal(t, "This is the plaintext.", content.Content)
+ assert.Len(t, content.Attachments, 1)
+ assert.Equal(t, "image001.png", content.Attachments[0].Name)
+ expectedAttachment, err := base64.StdEncoding.DecodeString(
+ "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" +
+ "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" +
+ "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" +
+ "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" +
+ "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" +
+ "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" +
+ "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" +
+ "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" +
+ "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" +
+ "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" +
+ "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" +
+ "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" +
+ "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" +
+ "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" +
+ "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" +
+ "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" +
+ "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" +
+ "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" +
+ "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" +
+ "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" +
+ "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" +
+ "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" +
+ "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" +
+ "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" +
+ "exgAAAAASUVORK5CYII=\r\n")
+ require.NoError(t, err)
+ assert.Equal(t, expectedAttachment, content.Attachments[0].Content)
+
+ // HCL Notes inlines attachments like this: without a filename.
+ // the attached image is from: https://openmoji.org/library/emoji-1F684
+ mailString = "Content-Type: multipart/related; boundary=\"=_related boundary=\"\r\n" +
+ "\r\n" +
+ "This text is for clients unable to decode multipart/related with multipart/alternative.\r\n" +
+ "\r\n" +
+ "--=_related boundary=\r\n" +
+ "Content-Type: multipart/alternative; boundary=\"=_alternative boundary=\"\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=\r\n" +
+ "Content-Type: text/plain\r\n" +
+ "\r\n" +
+ "This is the plaintext.\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=\r\n" +
+ "Content-Type: text/html\r\n" +
+ "\r\n" +
+ "This is a mail with multipart/related. Here is an image sent without a filename.
\r\n" +
+ "
\r\n" +
+ "\r\n" +
+ "--=_alternative boundary=--\r\n" +
+ "\r\n" +
+ "--=_related boundary=\r\n" +
+ "Content-Transfer-Encoding: base64\r\n" +
+ "Content-Type: image/png\r\n" +
+ "Content-ID: <_1_2845>\r\n" +
+ "\r\n" +
+ "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" +
+ "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" +
+ "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" +
+ "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" +
+ "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" +
+ "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" +
+ "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" +
+ "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" +
+ "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" +
+ "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" +
+ "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" +
+ "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" +
+ "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" +
+ "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" +
+ "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" +
+ "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" +
+ "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" +
+ "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" +
+ "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" +
+ "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" +
+ "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" +
+ "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" +
+ "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" +
+ "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" +
+ "exgAAAAASUVORK5CYII=\r\n" +
+ "\r\n" +
+ "--=_related boundary=--\r\n" +
+ "\r\n"
+
+ env, err = enmime.ReadEnvelope(strings.NewReader(mailString))
+ require.NoError(t, err)
+ content = getContentFromMailReader(env)
+ assert.Equal(t, "This is the plaintext.", content.Content)
+ assert.Len(t, content.Attachments, 1)
+ assert.Equal(t, "_1_2845.png", content.Attachments[0].Name)
+ expectedAttachment, err = base64.StdEncoding.DecodeString(
+ "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" +
+ "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" +
+ "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" +
+ "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" +
+ "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" +
+ "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" +
+ "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" +
+ "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" +
+ "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" +
+ "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" +
+ "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" +
+ "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" +
+ "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" +
+ "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" +
+ "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" +
+ "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" +
+ "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" +
+ "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" +
+ "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" +
+ "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" +
+ "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" +
+ "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" +
+ "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" +
+ "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" +
+ "exgAAAAASUVORK5CYII=\r\n")
+ require.NoError(t, err)
+ assert.Equal(t, expectedAttachment, content.Attachments[0].Content)
}
diff --git a/services/mailer/incoming/payload/payload.go b/services/mailer/incoming/payload/payload.go
index 00ada7826b..bb7a65e3d5 100644
--- a/services/mailer/incoming/payload/payload.go
+++ b/services/mailer/incoming/payload/payload.go
@@ -6,8 +6,8 @@ package payload
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/util"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/util"
)
const replyPayloadVersion1 byte = 1
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index bfede28bbe..410fdf6894 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -16,21 +16,21 @@ import (
texttmpl "text/template"
"time"
- activities_model "code.gitea.io/gitea/models/activities"
- auth_model "code.gitea.io/gitea/models/auth"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/emoji"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/translation"
- incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
- "code.gitea.io/gitea/services/mailer/token"
+ activities_model "forgejo.org/models/activities"
+ auth_model "forgejo.org/models/auth"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/emoji"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/translation"
+ incoming_payload "forgejo.org/services/mailer/incoming/payload"
+ "forgejo.org/services/mailer/token"
"gopkg.in/gomail.v2"
)
@@ -685,19 +685,14 @@ func SendRemovedSecurityKey(ctx context.Context, u *user_model.User, securityKey
}
locale := translation.NewLocale(u.Language)
- hasWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(ctx, u.ID)
- if err != nil {
- return err
- }
- hasTOTP, err := auth_model.HasTwoFactorByUID(ctx, u.ID)
+ hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, u.ID)
if err != nil {
return err
}
data := map[string]any{
"locale": locale,
- "HasWebAuthn": hasWebAuthn,
- "HasTOTP": hasTOTP,
+ "HasTwoFactor": hasTwoFactor,
"SecurityKeyName": securityKeyName,
"DisplayName": u.DisplayName(),
"Username": u.Name,
diff --git a/services/mailer/mail_actions.go b/services/mailer/mail_actions.go
new file mode 100644
index 0000000000..09763e164e
--- /dev/null
+++ b/services/mailer/mail_actions.go
@@ -0,0 +1,89 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+package mailer
+
+import (
+ "bytes"
+
+ actions_model "forgejo.org/models/actions"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
+)
+
+const (
+ tplActionNowDone base.TplName = "actions/now_done"
+)
+
+// requires !run.Status.IsSuccess() or !lastRun.Status.IsSuccess()
+func MailActionRun(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error {
+ if setting.MailService == nil {
+ // No mail service configured
+ return nil
+ }
+
+ if !run.NotifyEmail {
+ return nil
+ }
+
+ user := run.TriggerUser
+ // this happens e.g. when this is a scheduled run
+ if user.IsSystem() {
+ user = run.Repo.Owner
+ }
+ if user.IsSystem() || user.Email == "" {
+ return nil
+ }
+
+ if user.EmailNotificationsPreference == user_model.EmailNotificationsDisabled {
+ return nil
+ }
+
+ return sendMailActionRun(user, run, priorStatus, lastRun)
+}
+
+func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error {
+ var (
+ locale = translation.NewLocale(to.Language)
+ content bytes.Buffer
+ )
+
+ var subject string
+ if run.Status.IsSuccess() {
+ subject = locale.TrString("mail.actions.successful_run_after_failure_subject", run.Title, run.Repo.FullName())
+ } else {
+ subject = locale.TrString("mail.actions.not_successful_run", run.Title, run.Repo.FullName())
+ }
+
+ commitSHA := run.CommitSHA
+ if len(commitSHA) > 7 {
+ commitSHA = commitSHA[:7]
+ }
+ branch := run.PrettyRef()
+
+ data := map[string]any{
+ "locale": locale,
+ "Link": run.HTMLURL(),
+ "Subject": subject,
+ "Language": locale.Language(),
+ "RepoFullName": run.Repo.FullName(),
+ "Run": run,
+ "TriggerUserLink": run.TriggerUser.HTMLURL(),
+ "LastRun": lastRun,
+ "PriorStatus": priorStatus,
+ "CommitSHA": commitSHA,
+ "Branch": branch,
+ "IsSuccess": run.Status.IsSuccess(),
+ }
+
+ if err := bodyTemplates.ExecuteTemplate(&content, string(tplActionNowDone), data); err != nil {
+ return err
+ }
+
+ msg := NewMessage(to.EmailTo(), subject, content.String())
+ msg.Info = subject
+ SendAsync(msg)
+
+ return nil
+}
diff --git a/services/mailer/mail_actions_now_done_test.go b/services/mailer/mail_actions_now_done_test.go
new file mode 100644
index 0000000000..6a01ea7631
--- /dev/null
+++ b/services/mailer/mail_actions_now_done_test.go
@@ -0,0 +1,240 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package mailer
+
+import (
+ "slices"
+ "testing"
+
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ organization_model "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ notify_service "forgejo.org/services/notify"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func getActionsNowDoneTestUser(t *testing.T, name, email, notifications string) *user_model.User {
+ t.Helper()
+ user := new(user_model.User)
+ user.Name = name
+ user.Language = "en_US"
+ user.IsAdmin = false
+ user.Email = email
+ user.LastLoginUnix = 1693648327
+ user.CreatedUnix = 1693648027
+ opts := user_model.CreateUserOverwriteOptions{
+ AllowCreateOrganization: optional.Some(true),
+ EmailNotificationsPreference: ¬ifications,
+ }
+ require.NoError(t, user_model.AdminCreateUser(db.DefaultContext, user, &opts))
+ return user
+}
+
+func getActionsNowDoneTestOrg(t *testing.T, name, email string, owner *user_model.User) *user_model.User {
+ t.Helper()
+ org := new(organization_model.Organization)
+ org.Name = name
+ org.Language = "en_US"
+ org.IsAdmin = false
+ // contact email for the organization, for display purposes but otherwise not used as of v12
+ org.Email = email
+ org.LastLoginUnix = 1693648327
+ org.CreatedUnix = 1693648027
+ org.Email = email
+ require.NoError(t, organization_model.CreateOrganization(db.DefaultContext, org, owner))
+ return (*user_model.User)(org)
+}
+
+func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) {
+ AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_ref", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on")
+}
+
+func TestActionRunNowDoneNotificationMail(t *testing.T) {
+ ctx := t.Context()
+
+ defer test.MockVariableValue(&setting.Admin.DisableRegularOrgCreation, false)()
+
+ actionsUser := user_model.NewActionsUser()
+ require.NotEmpty(t, actionsUser.Email)
+
+ repo := repo_model.Repository{
+ Name: "some repo",
+ Description: "rockets are cool",
+ }
+
+ // Do some funky stuff with the action run's ids:
+ // The run with the larger ID finished first.
+ // This is odd but something that must work.
+ run1 := &actions_model.ActionRun{ID: 2, Repo: &repo, RepoID: repo.ID, Title: "some workflow", Status: actions_model.StatusFailure, Stopped: 1745821796, TriggerEvent: "workflow_dispatch"}
+ run2 := &actions_model.ActionRun{ID: 1, Repo: &repo, RepoID: repo.ID, Title: "some workflow", Status: actions_model.StatusSuccess, Stopped: 1745822796, TriggerEvent: "push"}
+
+ assignUsers := func(triggerUser, owner *user_model.User) {
+ for _, run := range []*actions_model.ActionRun{run1, run2} {
+ run.TriggerUser = triggerUser
+ run.TriggerUserID = triggerUser.ID
+ run.NotifyEmail = true
+ }
+ repo.Owner = owner
+ repo.OwnerID = owner.ID
+ }
+
+ notify_service.RegisterNotifier(NewNotifier())
+
+ orgOwner := getActionsNowDoneTestUser(t, "org_owner", "org_owner@example.com", "disabled")
+ defer CleanUpUsers(ctx, []*user_model.User{orgOwner})
+
+ t.Run("DontSendNotificationEmailOnFirstActionSuccess", func(t *testing.T) {
+ user := getActionsNowDoneTestUser(t, "new_user", "new_user@example.com", "enabled")
+ defer CleanUpUsers(ctx, []*user_model.User{user})
+ assignUsers(user, user)
+ defer MockMailSettings(func(msgs ...*Message) {
+ assert.Fail(t, "no mail should be sent")
+ })()
+ notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, nil)
+ })
+
+ t.Run("WorkflowEnableEmailNotificationIsFalse", func(t *testing.T) {
+ user := getActionsNowDoneTestUser(t, "new_user1", "new_user1@example.com", "enabled")
+ defer CleanUpUsers(ctx, []*user_model.User{user})
+ assignUsers(user, user)
+ defer MockMailSettings(func(msgs ...*Message) {
+ assert.Fail(t, "no mail should be sent")
+ })()
+ run2.NotifyEmail = false
+ notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, nil)
+ })
+
+ for _, testCase := range []struct {
+ name string
+ triggerUser *user_model.User
+ owner *user_model.User
+ expected string
+ expectMail bool
+ }{
+ {
+ // if the action is assigned a trigger user in a repository
+ // owned by a regular user, the mail is sent to the trigger user
+ name: "RegularTriggerUser",
+ triggerUser: getActionsNowDoneTestUser(t, "new_trigger_user0", "new_trigger_user0@example.com", user_model.EmailNotificationsEnabled),
+ owner: getActionsNowDoneTestUser(t, "new_owner0", "new_owner0@example.com", user_model.EmailNotificationsEnabled),
+ expected: "trigger",
+ expectMail: true,
+ },
+ {
+ // if the action is assigned to a system user (e.g. ActionsUser)
+ // in a repository owned by a regular user, the mail is sent to
+ // the user that owns the repository
+ name: "SystemTriggerUserAndRegularOwner",
+ triggerUser: actionsUser,
+ owner: getActionsNowDoneTestUser(t, "new_owner1", "new_owner1@example.com", user_model.EmailNotificationsEnabled),
+ expected: "owner",
+ expectMail: true,
+ },
+ {
+ // if the action is assigned a trigger user with disabled notifications in a repository
+ // owned by a regular user, no mail is sent
+ name: "RegularTriggerUserNotificationsDisabled",
+ triggerUser: getActionsNowDoneTestUser(t, "new_trigger_user2", "new_trigger_user2@example.com", user_model.EmailNotificationsDisabled),
+ owner: getActionsNowDoneTestUser(t, "new_owner2", "new_owner2@example.com", user_model.EmailNotificationsEnabled),
+ expectMail: false,
+ },
+ {
+ // if the action is assigned to a system user (e.g. ActionsUser)
+ // owned by a regular user with disabled notifications, no mail is sent
+ name: "SystemTriggerUserAndRegularOwnerNotificationsDisabled",
+ triggerUser: actionsUser,
+ owner: getActionsNowDoneTestUser(t, "new_owner3", "new_owner3@example.com", user_model.EmailNotificationsDisabled),
+ expectMail: false,
+ },
+ {
+ // if the action is assigned to a system user (e.g. ActionsUser)
+ // in a repository owned by an organization with an email contact, the mail is sent to
+ // this email contact
+ name: "SystemTriggerUserAndOrgOwner",
+ triggerUser: actionsUser,
+ owner: getActionsNowDoneTestOrg(t, "new_org1", "new_org_owner0@example.com", orgOwner),
+ expected: "owner",
+ expectMail: true,
+ },
+ {
+ // if the action is assigned to a system user (e.g. ActionsUser)
+ // in a repository owned by an organization without an email contact, no mail is sent
+ name: "SystemTriggerUserAndNoMailOrgOwner",
+ triggerUser: actionsUser,
+ owner: getActionsNowDoneTestOrg(t, "new_org2", "", orgOwner),
+ expectMail: false,
+ },
+ } {
+ t.Run(testCase.name, func(t *testing.T) {
+ assignUsers(testCase.triggerUser, testCase.owner)
+ defer CleanUpUsers(ctx, slices.DeleteFunc([]*user_model.User{testCase.triggerUser, testCase.owner}, func(user *user_model.User) bool {
+ return user.IsSystem()
+ }))
+
+ t.Run("SendNotificationEmailOnActionRunFailed", func(t *testing.T) {
+ mailSent := false
+ defer MockMailSettings(func(msgs ...*Message) {
+ assert.Len(t, msgs, 1)
+ msg := msgs[0]
+ assert.False(t, mailSent, "sent mail twice")
+ expectedEmail := testCase.triggerUser.Email
+ if testCase.expected == "owner" { // otherwise "trigger"
+ expectedEmail = testCase.owner.Email
+ }
+ require.Contains(t, msg.To, expectedEmail, "sent mail to unknown sender")
+ mailSent = true
+ assert.Contains(t, msg.Body, testCase.triggerUser.HTMLURL())
+ assert.Contains(t, msg.Body, testCase.triggerUser.Name)
+ // what happened
+ assert.Contains(t, msg.Body, "failed")
+ // new status of run
+ assert.Contains(t, msg.Body, "failure")
+ // prior status of this run
+ assert.Contains(t, msg.Body, "waiting")
+ assertTranslatedLocaleMailActionsNowDone(t, msg.Body)
+ })()
+ require.NotNil(t, setting.MailService)
+
+ notify_service.ActionRunNowDone(ctx, run1, actions_model.StatusWaiting, nil)
+ assert.Equal(t, testCase.expectMail, mailSent)
+ })
+
+ t.Run("SendNotificationEmailOnActionRunRecovered", func(t *testing.T) {
+ mailSent := false
+ defer MockMailSettings(func(msgs ...*Message) {
+ assert.Len(t, msgs, 1)
+ msg := msgs[0]
+ assert.False(t, mailSent, "sent mail twice")
+ expectedEmail := testCase.triggerUser.Email
+ if testCase.expected == "owner" { // otherwise "trigger"
+ expectedEmail = testCase.owner.Email
+ }
+ require.Contains(t, msg.To, expectedEmail, "sent mail to unknown sender")
+ mailSent = true
+ assert.Contains(t, msg.Body, testCase.triggerUser.HTMLURL())
+ assert.Contains(t, msg.Body, testCase.triggerUser.Name)
+ // what happened
+ assert.Contains(t, msg.Body, "recovered")
+ // old status of run
+ assert.Contains(t, msg.Body, "failure")
+ // new status of run
+ assert.Contains(t, msg.Body, "success")
+ // prior status of this run
+ assert.Contains(t, msg.Body, "running")
+ })()
+ require.NotNil(t, setting.MailService)
+
+ notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, run1)
+ assert.Equal(t, testCase.expectMail, mailSent)
+ })
+ })
+ }
+}
diff --git a/services/mailer/mail_admin_new_user.go b/services/mailer/mail_admin_new_user.go
index 0713de8a95..ffb03197b7 100644
--- a/services/mailer/mail_admin_new_user.go
+++ b/services/mailer/mail_admin_new_user.go
@@ -7,12 +7,12 @@ import (
"context"
"strconv"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/translation"
)
const (
diff --git a/services/mailer/mail_admin_new_user_test.go b/services/mailer/mail_admin_new_user_test.go
index f7f27832f9..58afcfcda6 100644
--- a/services/mailer/mail_admin_new_user_test.go
+++ b/services/mailer/mail_admin_new_user_test.go
@@ -4,20 +4,19 @@
package mailer
import (
- "context"
"strconv"
"testing"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func getTestUsers(t *testing.T) []*user_model.User {
+func getAdminNewUserTestUsers(t *testing.T) []*user_model.User {
t.Helper()
admin := new(user_model.User)
admin.Name = "testadmin"
@@ -38,16 +37,10 @@ func getTestUsers(t *testing.T) []*user_model.User {
return []*user_model.User{admin, newUser}
}
-func cleanUpUsers(ctx context.Context, users []*user_model.User) {
- for _, u := range users {
- db.DeleteByID[user_model.User](ctx, u.ID)
- }
-}
-
func TestAdminNotificationMail_test(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
- users := getTestUsers(t)
+ users := getAdminNewUserTestUsers(t)
t.Run("SendNotificationEmailOnNewUser_true", func(t *testing.T) {
defer test.MockVariableValue(&setting.Admin.SendNotificationEmailOnNewUser, true)()
@@ -75,5 +68,5 @@ func TestAdminNotificationMail_test(t *testing.T) {
MailNewUser(ctx, users[1])
})
- cleanUpUsers(ctx, users)
+ CleanUpUsers(ctx, users)
}
diff --git a/services/mailer/mail_auth_test.go b/services/mailer/mail_auth_test.go
index 38e3721a22..e40a0d6fa0 100644
--- a/services/mailer/mail_auth_test.go
+++ b/services/mailer/mail_auth_test.go
@@ -6,14 +6,14 @@ package mailer_test
import (
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/services/mailer"
- user_service "code.gitea.io/gitea/services/user"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
+ "forgejo.org/services/mailer"
+ user_service "forgejo.org/services/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index 1812441d5a..b4ed3145ed 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -6,12 +6,12 @@ package mailer
import (
"context"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
// MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 1bb6fdc7a3..0d8e054041 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -7,15 +7,15 @@ import (
"context"
"fmt"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
func fallbackMailSubject(issue *issues_model.Issue) string {
@@ -85,7 +85,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
// =========== Repo watchers ===========
// Make repo watchers last, since it's likely the list with the most users
- if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress(ctx) && ctx.ActionType != activities_model.ActionCreatePullRequest) {
+ if !ctx.Issue.IsPull || !ctx.Issue.PullRequest.IsWorkInProgress(ctx) || ctx.ActionType == activities_model.ActionCreatePullRequest {
ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID)
if err != nil {
return fmt.Errorf("GetRepoWatchersIDs(%d): %w", ctx.Issue.RepoID, err)
@@ -137,9 +137,8 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
}
// At this point we exclude:
// user that don't have all mails enabled or users only get mail on mention and this is one ...
- if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled ||
- user.EmailNotificationsPreference == user_model.EmailNotificationsAndYourOwn ||
- fromMention && user.EmailNotificationsPreference == user_model.EmailNotificationsOnMention) {
+ if user.EmailNotificationsPreference != user_model.EmailNotificationsEnabled &&
+ user.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && (!fromMention || user.EmailNotificationsPreference != user_model.EmailNotificationsOnMention) {
continue
}
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index 0b8b97e9cd..0f2ef33fe1 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -7,14 +7,14 @@ import (
"bytes"
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
)
const (
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index 7003584786..eed650f3ac 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -8,11 +8,11 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
)
// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go
index ceecefa50f..5375133415 100644
--- a/services/mailer/mail_team_invite.go
+++ b/services/mailer/mail_team_invite.go
@@ -6,15 +6,16 @@ package mailer
import (
"bytes"
"context"
+ "errors"
"fmt"
"net/url"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/translation"
+ org_model "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/translation"
)
const (
@@ -39,7 +40,7 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod
if err != nil && !user_model.IsErrUserNotExist(err) {
return err
} else if user != nil && user.ProhibitLogin {
- return fmt.Errorf("login is prohibited for the invited user")
+ return errors.New("login is prohibited for the invited user")
}
inviteRedirect := url.QueryEscape(fmt.Sprintf("/org/invite/%s", invite.Token))
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index b2768a2bad..afbcb8064e 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -15,15 +15,15 @@ import (
"testing"
texttmpl "text/template"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -79,7 +79,7 @@ func TestComposeIssueCommentMessage(t *testing.T) {
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
msgs, err := composeIssueCommentMessages(&mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
Comment: comment,
@@ -123,7 +123,7 @@ func TestComposeIssueMessage(t *testing.T) {
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
msgs, err := composeIssueCommentMessages(&mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, "en-US", recipients, false, "issue create")
@@ -168,7 +168,7 @@ func TestMailerIssueTemplate(t *testing.T) {
t.Helper()
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
- ctx.Context = context.Background()
+ ctx.Context = t.Context()
fromMention := false
msgs, err := composeIssueCommentMessages(ctx, "en-US", recipients, fromMention, "TestMailerIssueTemplate")
require.NoError(t, err)
@@ -266,14 +266,14 @@ func TestTemplateSelection(t *testing.T) {
}
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "issue/new/subject", "issue/new/body")
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
@@ -282,14 +282,14 @@ func TestTemplateSelection(t *testing.T) {
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "pull/comment/subject", "pull/comment/body")
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
@@ -309,7 +309,7 @@ func TestTemplateServices(t *testing.T) {
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
+ Context: t.Context(), // TODO: use a correct context
Issue: issue, Doer: doer, ActionType: actionType,
Content: "test body", Comment: comment,
}, recipients, fromMention, "TestTemplateServices")
@@ -353,7 +353,7 @@ func TestGenerateAdditionalHeaders(t *testing.T) {
defer MockMailSettings(nil)()
doer, _, issue, _ := prepareMailerTest(t)
- ctx := &mailCommentContext{Context: context.TODO() /* TODO: use a correct context */, Issue: issue, Doer: doer}
+ ctx := &mailCommentContext{Context: t.Context() /* TODO: use a correct context */, Issue: issue, Doer: doer}
recipient := &user_model.User{Name: "test", Email: "test@gitea.com"}
headers := generateAdditionalHeaders(ctx, "dummy-reason", recipient)
@@ -494,8 +494,7 @@ func Test_createReference(t *testing.T) {
func TestFromDisplayName(t *testing.T) {
template, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
require.NoError(t, err)
- setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
- defer func() { setting.MailService = nil }()
+ defer test.MockVariableValue(&setting.MailService, &setting.Mailer{FromDisplayNameFormatTemplate: template})()
tests := []struct {
userDisplayName string
@@ -518,24 +517,18 @@ func TestFromDisplayName(t *testing.T) {
t.Run(tc.userDisplayName, func(t *testing.T) {
user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"}
got := fromDisplayName(user)
- assert.EqualValues(t, tc.fromDisplayName, got)
+ assert.Equal(t, tc.fromDisplayName, got)
})
}
t.Run("template with all available vars", func(t *testing.T) {
template, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])")
require.NoError(t, err)
- setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
- oldAppName := setting.AppName
- setting.AppName = "Code IT"
- oldDomain := setting.Domain
- setting.Domain = "code.it"
- defer func() {
- setting.AppName = oldAppName
- setting.Domain = oldDomain
- }()
+ defer test.MockVariableValue(&setting.MailService, &setting.Mailer{FromDisplayNameFormatTemplate: template})()
+ defer test.MockVariableValue(&setting.AppName, "Code IT")()
+ defer test.MockVariableValue(&setting.Domain, "code.it")()
- assert.EqualValues(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
+ assert.Equal(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
})
}
diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go
index 0a723f974a..d8646d9ddd 100644
--- a/services/mailer/mailer.go
+++ b/services/mailer/mailer.go
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"crypto/tls"
+ "errors"
"fmt"
"hash/fnv"
"io"
@@ -18,17 +19,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ notify_service "forgejo.org/services/notify"
ntlmssp "github.com/Azure/go-ntlmssp"
- "github.com/jaytaylor/html2text"
+ "github.com/inbucket/html2text"
"gopkg.in/gomail.v2"
)
@@ -176,7 +177,7 @@ func (a *ntlmAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
func (a *ntlmAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
if len(fromServer) == 0 {
- return nil, fmt.Errorf("ntlm ChallengeMessage is empty")
+ return nil, errors.New("ntlm ChallengeMessage is empty")
}
authenticateMessage, err := ntlmssp.ProcessChallenge(fromServer, a.username, a.password, a.domainNeeded)
return authenticateMessage, err
@@ -264,7 +265,7 @@ func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
canAuth, options := client.Extension("AUTH")
if len(opts.User) > 0 {
if !canAuth {
- return fmt.Errorf("SMTP server does not support AUTH, but credentials provided")
+ return errors.New("SMTP server does not support AUTH, but credentials provided")
}
var auth smtp.Auth
diff --git a/services/mailer/mailer_test.go b/services/mailer/mailer_test.go
index 045701f3a5..34fd847c05 100644
--- a/services/mailer/mailer_test.go
+++ b/services/mailer/mailer_test.go
@@ -8,9 +8,9 @@ import (
"testing"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -72,7 +72,7 @@ func TestToMessage(t *testing.T) {
_, err := m1.ToMessage().WriteTo(buf)
require.NoError(t, err)
header, _ := extractMailHeaderAndContent(t, buf.String())
- assert.EqualValues(t, map[string]string{
+ assert.Equal(t, map[string]string{
"Content-Type": "multipart/alternative;",
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
"From": "\"Test Gitea\" ",
@@ -92,7 +92,7 @@ func TestToMessage(t *testing.T) {
_, err = m1.ToMessage().WriteTo(buf)
require.NoError(t, err)
header, _ = extractMailHeaderAndContent(t, buf.String())
- assert.EqualValues(t, map[string]string{
+ assert.Equal(t, map[string]string{
"Content-Type": "multipart/alternative;",
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
"From": "\"Test Gitea\" ",
diff --git a/services/mailer/main_test.go b/services/mailer/main_test.go
index 908976e7ef..5e9cbe3e99 100644
--- a/services/mailer/main_test.go
+++ b/services/mailer/main_test.go
@@ -7,13 +7,16 @@ import (
"context"
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/translation"
+ "forgejo.org/models/db"
+ organization_model "forgejo.org/models/organization"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/templates"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/translation"
- _ "code.gitea.io/gitea/models/actions"
+ _ "forgejo.org/models/actions"
"github.com/stretchr/testify/assert"
)
@@ -46,3 +49,14 @@ func MockMailSettings(send func(msgs ...*Message)) func() {
}
}
}
+
+func CleanUpUsers(ctx context.Context, users []*user_model.User) {
+ for _, u := range users {
+ if u.IsOrganization() {
+ organization_model.DeleteOrganization(ctx, (*organization_model.Organization)(u))
+ } else {
+ db.DeleteByID[user_model.User](ctx, u.ID)
+ db.DeleteByBean(ctx, &user_model.EmailAddress{UID: u.ID})
+ }
+ }
+}
diff --git a/services/mailer/notify.go b/services/mailer/notify.go
index 54ab80aab9..7461a67181 100644
--- a/services/mailer/notify.go
+++ b/services/mailer/notify.go
@@ -7,12 +7,13 @@ import (
"context"
"fmt"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- notify_service "code.gitea.io/gitea/services/notify"
+ actions_model "forgejo.org/models/actions"
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ notify_service "forgejo.org/services/notify"
)
type mailNotifier struct {
@@ -30,15 +31,16 @@ func (m *mailNotifier) CreateIssueComment(ctx context.Context, doer *user_model.
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
var act activities_model.ActionType
- if comment.Type == issues_model.CommentTypeClose {
+ switch comment.Type {
+ case issues_model.CommentTypeClose:
act = activities_model.ActionCloseIssue
- } else if comment.Type == issues_model.CommentTypeReopen {
+ case issues_model.CommentTypeReopen:
act = activities_model.ActionReopenIssue
- } else if comment.Type == issues_model.CommentTypeComment {
+ case issues_model.CommentTypeComment:
act = activities_model.ActionCommentIssue
- } else if comment.Type == issues_model.CommentTypeCode {
+ case issues_model.CommentTypeCode:
act = activities_model.ActionCommentIssue
- } else if comment.Type == issues_model.CommentTypePullRequestPush {
+ case issues_model.CommentTypePullRequestPush:
act = 0
}
@@ -94,11 +96,12 @@ func (m *mailNotifier) NewPullRequest(ctx context.Context, pr *issues_model.Pull
func (m *mailNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
var act activities_model.ActionType
- if comment.Type == issues_model.CommentTypeClose {
+ switch comment.Type {
+ case issues_model.CommentTypeClose:
act = activities_model.ActionCloseIssue
- } else if comment.Type == issues_model.CommentTypeReopen {
+ case issues_model.CommentTypeReopen:
act = activities_model.ActionReopenIssue
- } else if comment.Type == issues_model.CommentTypeComment {
+ case issues_model.CommentTypeComment:
act = activities_model.ActionCommentPull
}
if err := MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil {
@@ -206,3 +209,13 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
MailNewUser(ctx, newUser)
}
+
+func (m *mailNotifier) ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) {
+ // Only send a mail on a successful run when the workflow recovered (i.e., the run before failed).
+ if run.Status.IsSuccess() && (lastRun == nil || lastRun.Status.IsSuccess()) {
+ return
+ }
+ if err := MailActionRun(run, priorStatus, lastRun); err != nil {
+ log.Error("MailActionRunNowDone: %v", err)
+ }
+}
diff --git a/services/mailer/token/token.go b/services/mailer/token/token.go
index 1a52bce803..f3d7286cb0 100644
--- a/services/mailer/token/token.go
+++ b/services/mailer/token/token.go
@@ -11,8 +11,8 @@ import (
"fmt"
"time"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/util"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/util"
)
// A token is a verifiable container describing an action.
diff --git a/services/markup/main_test.go b/services/markup/main_test.go
index 89fe3e7e34..1b085b4929 100644
--- a/services/markup/main_test.go
+++ b/services/markup/main_test.go
@@ -6,7 +6,7 @@ package markup
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go
index 40bf1d65da..2f1b1e738c 100644
--- a/services/markup/processorhelper.go
+++ b/services/markup/processorhelper.go
@@ -5,18 +5,18 @@ package markup
import (
"context"
- "fmt"
+ "errors"
- "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- gitea_context "code.gitea.io/gitea/services/context"
- file_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models/perm/access"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ gitea_context "forgejo.org/services/context"
+ file_service "forgejo.org/services/repository/files"
)
func ProcessorHelper() *markup.ProcessorHelper {
@@ -55,7 +55,7 @@ func ProcessorHelper() *markup.ProcessorHelper {
return nil, err
}
if !perms.CanRead(unit.TypeCode) {
- return nil, fmt.Errorf("cannot access repository code")
+ return nil, errors.New("cannot access repository code")
}
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go
index fafde746d2..8195451746 100644
--- a/services/markup/processorhelper_test.go
+++ b/services/markup/processorhelper_test.go
@@ -4,16 +4,15 @@
package markup
import (
- "context"
"net/http"
"net/http/httptest"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ "forgejo.org/models/user"
+ gitea_context "forgejo.org/services/context"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -33,10 +32,10 @@ func TestProcessorHelper(t *testing.T) {
unittest.AssertCount(t, &user.User{Name: userNoSuch}, 0)
// when using general context, use user's visibility to check
- assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPublic))
- assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userLimited))
- assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPrivate))
- assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))
+ assert.True(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userPublic))
+ assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userLimited))
+ assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userPrivate))
+ assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userNoSuch))
// when using web context, use user.IsUserVisibleToViewer to check
req, err := http.NewRequest("GET", "/", nil)
diff --git a/services/migrations/codebase.go b/services/migrations/codebase.go
index 492fc908e9..843df0f973 100644
--- a/services/migrations/codebase.go
+++ b/services/migrations/codebase.go
@@ -13,10 +13,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/structs"
)
var (
diff --git a/services/migrations/codebase_test.go b/services/migrations/codebase_test.go
index 23626d16d7..315c7be709 100644
--- a/services/migrations/codebase_test.go
+++ b/services/migrations/codebase_test.go
@@ -4,13 +4,12 @@
package migrations
import (
- "context"
"net/url"
"os"
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -33,7 +32,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
}
factory := &CodebaseDownloaderFactory{}
- downloader, err := factory.New(context.Background(), base.MigrateOptions{
+ downloader, err := factory.New(t.Context(), base.MigrateOptions{
CloneAddr: u.String(),
AuthUsername: apiUser,
AuthPassword: apiPassword,
diff --git a/services/migrations/common.go b/services/migrations/common.go
index d88518899d..ee74461447 100644
--- a/services/migrations/common.go
+++ b/services/migrations/common.go
@@ -7,10 +7,10 @@ import (
"fmt"
"strings"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
)
// WarnAndNotice will log the provided message and send a repository notice
diff --git a/services/migrations/dump.go b/services/migrations/dump.go
index 07812002af..cbf6b87668 100644
--- a/services/migrations/dump.go
+++ b/services/migrations/dump.go
@@ -16,13 +16,13 @@ import (
"strings"
"time"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
"github.com/google/uuid"
"gopkg.in/yaml.v3"
@@ -128,6 +128,7 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
"comments": opts.Comments,
"pulls": opts.PullRequests,
"assets": opts.ReleaseAssets,
+ "website": repo.Website,
})
if err != nil {
return err
diff --git a/services/migrations/forgejo_downloader.go b/services/migrations/forgejo_downloader.go
index 25dbb6ec51..5f809b82be 100644
--- a/services/migrations/forgejo_downloader.go
+++ b/services/migrations/forgejo_downloader.go
@@ -4,7 +4,7 @@
package migrations
import (
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/structs"
)
func init() {
diff --git a/services/migrations/forgejo_downloader_test.go b/services/migrations/forgejo_downloader_test.go
index 5bd37551cc..db1930ebba 100644
--- a/services/migrations/forgejo_downloader_test.go
+++ b/services/migrations/forgejo_downloader_test.go
@@ -6,7 +6,7 @@ package migrations
import (
"testing"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/structs"
"github.com/stretchr/testify/require"
)
diff --git a/services/migrations/git.go b/services/migrations/git.go
index 22ffd5e765..46710b0abe 100644
--- a/services/migrations/git.go
+++ b/services/migrations/git.go
@@ -6,7 +6,7 @@ package migrations
import (
"context"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
)
var _ base.Downloader = &PlainGitDownloader{}
diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go
index 4fe9e30a39..b68fc01083 100644
--- a/services/migrations/gitbucket.go
+++ b/services/migrations/gitbucket.go
@@ -9,9 +9,9 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/structs"
)
var (
diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go
index 272bf02e11..133cc5c928 100644
--- a/services/migrations/gitea_downloader.go
+++ b/services/migrations/gitea_downloader.go
@@ -13,9 +13,9 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/structs"
gitea_sdk "code.gitea.io/sdk/gitea"
)
@@ -160,6 +160,7 @@ func (g *GiteaDownloader) GetRepoInfo() (*base.Repository, error) {
CloneURL: repo.CloneURL,
OriginalURL: repo.HTMLURL,
DefaultBranch: repo.DefaultBranch,
+ Website: repo.Website,
}, nil
}
@@ -503,6 +504,28 @@ func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Com
return allComments, true, nil
}
+type ForgejoPullRequest struct {
+ gitea_sdk.PullRequest
+ Flow int64 `json:"flow"`
+}
+
+// Extracted from https://gitea.com/gitea/go-sdk/src/commit/164e3358bc02213954fb4380b821bed80a14824d/gitea/pull.go#L347-L364
+func (g *GiteaDownloader) fixPullHeadSha(pr *ForgejoPullRequest) error {
+ if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil && pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
+ owner := pr.Base.Repository.Owner.UserName
+ repo := pr.Base.Repository.Name
+ refs, _, err := g.client.GetRepoRefs(owner, repo, pr.Head.Ref)
+ if err != nil {
+ return err
+ }
+ if len(refs) == 0 {
+ return fmt.Errorf("unable to resolve PR ref %q", pr.Head.Ref)
+ }
+ pr.Head.Sha = refs[0].Object.SHA
+ }
+ return nil
+}
+
// GetPullRequests returns pull requests according page and perPage
func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
if perPage > g.maxPerPage {
@@ -510,16 +533,30 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
}
allPRs := make([]*base.PullRequest, 0, perPage)
- prs, _, err := g.client.ListRepoPullRequests(g.repoOwner, g.repoName, gitea_sdk.ListPullRequestsOptions{
+ prs := make([]*ForgejoPullRequest, 0, perPage)
+ opt := gitea_sdk.ListPullRequestsOptions{
ListOptions: gitea_sdk.ListOptions{
Page: page,
PageSize: perPage,
},
State: gitea_sdk.StateAll,
- })
+ }
+
+ link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", url.PathEscape(g.repoOwner), url.PathEscape(g.repoName)))
+ link.RawQuery = opt.QueryEncode()
+ _, err := getParsedResponse(g.client, "GET", link.String(), http.Header{"content-type": []string{"application/json"}}, nil, &prs)
if err != nil {
return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err)
}
+
+ if g.client.CheckServerVersionConstraint(">= 1.14.0") != nil {
+ for i := range prs {
+ if err := g.fixPullHeadSha(prs[i]); err != nil {
+ return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err)
+ }
+ }
+ }
+
for _, pr := range prs {
var milestone string
if pr.Milestone != nil {
@@ -597,6 +634,7 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
MergeCommitSHA: mergeCommitSHA,
IsLocked: pr.IsLocked,
PatchURL: pr.PatchURL,
+ Flow: pr.Flow,
Head: base.PullRequestBranch{
Ref: headRef,
SHA: headSHA,
diff --git a/services/migrations/gitea_downloader_test.go b/services/migrations/gitea_downloader_test.go
index 8429682613..5acc3b86a9 100644
--- a/services/migrations/gitea_downloader_test.go
+++ b/services/migrations/gitea_downloader_test.go
@@ -4,14 +4,13 @@
package migrations
import (
- "context"
"os"
"sort"
"testing"
"time"
- "code.gitea.io/gitea/models/unittest"
- base "code.gitea.io/gitea/modules/migration"
+ "forgejo.org/models/unittest"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -24,7 +23,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
server := unittest.NewMockWebServer(t, "https://gitea.com", fixturePath, giteaToken != "")
defer server.Close()
- downloader, err := NewGiteaDownloader(context.Background(), server.URL, "gitea/test_repo", "", "", giteaToken)
+ downloader, err := NewGiteaDownloader(t.Context(), server.URL, "gitea/test_repo", "", "", giteaToken)
if downloader == nil {
t.Fatal("NewGitlabDownloader is nil")
}
@@ -40,12 +39,13 @@ func TestGiteaDownloadRepo(t *testing.T) {
CloneURL: server.URL + "/gitea/test_repo.git",
OriginalURL: server.URL + "/gitea/test_repo",
DefaultBranch: "master",
+ Website: "https://codeberg.org/forgejo/forgejo/",
}, repo)
topics, err := downloader.GetTopics()
require.NoError(t, err)
sort.Strings(topics)
- assert.EqualValues(t, []string{"ci", "gitea", "migration", "test"}, topics)
+ assert.Equal(t, []string{"ci", "gitea", "migration", "test"}, topics)
labels, err := downloader.GetLabels()
require.NoError(t, err)
@@ -132,7 +132,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
require.NoError(t, err)
assert.True(t, isEnd)
assert.Len(t, issues, 7)
- assert.EqualValues(t, "open", issues[0].State)
+ assert.Equal(t, "open", issues[0].State)
issues, isEnd, err = downloader.GetIssues(3, 2)
require.NoError(t, err)
@@ -307,3 +307,46 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, reviews)
}
+
+func TestForgejoDownloadRepo(t *testing.T) {
+ token := os.Getenv("CODE_FORGEJO_TOKEN")
+
+ fixturePath := "./testdata/code-forgejo-org/full_download"
+ server := unittest.NewMockWebServer(t, "https://code.forgejo.org", fixturePath, token != "")
+ defer server.Close()
+
+ downloader, err := NewGiteaDownloader(t.Context(), server.URL, "Gusted/agit-test", "", "", token)
+ require.NoError(t, err)
+ require.NotNil(t, downloader)
+
+ prs, _, err := downloader.GetPullRequests(1, 50)
+ require.NoError(t, err)
+ assert.Len(t, prs, 1)
+
+ assertPullRequestEqual(t, &base.PullRequest{
+ Number: 1,
+ PosterID: 63,
+ PosterName: "Gusted",
+ PosterEmail: "postmaster@gusted.xyz",
+ Title: "Add extra information",
+ State: "open",
+ Created: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC),
+ Updated: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC),
+ Base: base.PullRequestBranch{
+ CloneURL: "",
+ Ref: "main",
+ SHA: "79ebb873a6497c8847141ba9706b3f757196a1e6",
+ RepoName: "agit-test",
+ OwnerName: "Gusted",
+ },
+ Head: base.PullRequestBranch{
+ CloneURL: server.URL + "/Gusted/agit-test.git",
+ Ref: "refs/pull/1/head",
+ SHA: "667e9317ec37b977e6d3d7d43e3440636970563c",
+ RepoName: "agit-test",
+ OwnerName: "Gusted",
+ },
+ PatchURL: server.URL + "/Gusted/agit-test/pulls/1.patch",
+ Flow: 1,
+ }, prs[0])
+}
diff --git a/services/migrations/gitea_sdk_hack.go b/services/migrations/gitea_sdk_hack.go
new file mode 100644
index 0000000000..f3959717a8
--- /dev/null
+++ b/services/migrations/gitea_sdk_hack.go
@@ -0,0 +1,16 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package migrations
+
+import (
+ "io"
+ "net/http"
+
+ _ "unsafe" // Needed for go:linkname support
+
+ gitea_sdk "code.gitea.io/sdk/gitea"
+)
+
+//go:linkname getParsedResponse code.gitea.io/sdk/gitea.(*Client).getParsedResponse
+func getParsedResponse(client *gitea_sdk.Client, method, path string, header http.Header, body io.Reader, obj any) (*gitea_sdk.Response, error)
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 2076348535..7887dacdb1 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -14,26 +14,26 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- base_module "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/uri"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/pull"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ base_module "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/label"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/uri"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/pull"
+ repo_service "forgejo.org/services/repository"
"github.com/google/uuid"
)
@@ -105,6 +105,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
r, err = repo_service.CreateRepositoryDirectly(g.ctx, g.doer, owner, repo_service.CreateRepoOptions{
Name: g.repoName,
Description: repo.Description,
+ Website: repo.Website,
OriginalURL: repo.OriginalURL,
GitServiceType: opts.GitServiceType,
IsPrivate: opts.Private || setting.Repository.ForcePrivate,
@@ -119,20 +120,17 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
}
r.DefaultBranch = repo.DefaultBranch
r.Description = repo.Description
+ r.Website = repo.Website
r, err = repo_service.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{
- RepoName: g.repoName,
- Description: repo.Description,
- OriginalURL: repo.OriginalURL,
- GitServiceType: opts.GitServiceType,
- Mirror: repo.IsMirror,
+ CloneAddr: repo.CloneURL, // SECURITY: we will assume that this has already been checked
LFS: opts.LFS,
LFSEndpoint: opts.LFSEndpoint,
- CloneAddr: repo.CloneURL, // SECURITY: we will assume that this has already been checked
- Private: repo.IsPrivate,
- Wiki: opts.Wiki,
- Releases: opts.Releases, // if didn't get releases, then sync them from tags
+ Mirror: repo.IsMirror,
MirrorInterval: opts.MirrorInterval,
+ Releases: opts.Releases, // if didn't get releases, then sync them from tags
+ RepoName: g.repoName,
+ Wiki: opts.Wiki,
}, NewMigrationHTTPTransport())
g.sameApp = strings.HasPrefix(repo.OriginalURL, setting.AppURL)
@@ -768,7 +766,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
issue := issues_model.Issue{
RepoID: g.repo.ID,
Repo: g.repo,
- Title: prTitle,
+ Title: util.TruncateRunes(prTitle, 255),
Index: pr.Number,
Content: pr.Content,
MilestoneID: milestoneID,
@@ -804,6 +802,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
MergeBase: pr.Base.SHA,
Index: pr.Number,
HasMerged: pr.Merged,
+ Flow: issues_model.PullRequestFlow(pr.Flow),
Issue: &issue,
}
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index ad193b2253..85e733cc51 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -5,7 +5,6 @@
package migrations
import (
- "context"
"fmt"
"os"
"path/filepath"
@@ -13,19 +12,19 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -40,7 +39,7 @@ func TestGiteaUploadRepo(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var (
- ctx = context.Background()
+ ctx = t.Context()
downloader = NewGithubDownloaderV3(ctx, "https://github.com", "", "", "", "go-xorm", "builder")
repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05")
uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName)
@@ -65,7 +64,7 @@ func TestGiteaUploadRepo(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName})
assert.True(t, repo.HasWiki())
- assert.EqualValues(t, repo_model.RepositoryReady, repo.Status)
+ assert.Equal(t, repo_model.RepositoryReady, repo.Status)
milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
RepoID: repo.ID,
@@ -133,7 +132,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
+ uploader := NewGiteaLocalUploader(t.Context(), doer, doer.Name, repoName)
// call remapLocalUser
uploader.sameApp = true
@@ -174,7 +173,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
uploader.userMap = make(map[int64]int64)
err = uploader.remapUser(&source, &target)
require.NoError(t, err)
- assert.EqualValues(t, user.ID, target.GetUserID())
+ assert.Equal(t, user.ID, target.GetUserID())
}
func TestGiteaUploadRemapExternalUser(t *testing.T) {
@@ -182,7 +181,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
+ uploader := NewGiteaLocalUploader(t.Context(), doer, doer.Name, repoName)
uploader.gitServiceType = structs.GiteaService
// call remapExternalUser
uploader.sameApp = false
@@ -225,7 +224,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
target = repo_model.Release{}
err = uploader.remapUser(&source, &target)
require.NoError(t, err)
- assert.EqualValues(t, linkedUser.ID, target.GetUserID())
+ assert.Equal(t, linkedUser.ID, target.GetUserID())
}
func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
@@ -301,7 +300,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
require.NoError(t, err)
toRepoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), fromRepoOwner, fromRepoOwner.Name, toRepoName)
+ uploader := NewGiteaLocalUploader(t.Context(), fromRepoOwner, fromRepoOwner.Name, toRepoName)
uploader.gitServiceType = structs.GiteaService
require.NoError(t, uploader.CreateRepo(&base.Repository{
Description: "description",
@@ -505,14 +504,14 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
head, err := uploader.updateGitForPullRequest(&testCase.pr)
require.NoError(t, err)
- assert.EqualValues(t, testCase.head, head)
+ assert.Equal(t, testCase.head, head)
log.Info(stopMark)
logFiltered, logStopped := logChecker.Check(5 * time.Second)
assert.True(t, logStopped)
if len(testCase.logFilter) > 0 {
- assert.EqualValues(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter)
+ assert.Equal(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter)
}
})
}
diff --git a/services/migrations/github.go b/services/migrations/github.go
index ca7aaeb68f..9721c86180 100644
--- a/services/migrations/github.go
+++ b/services/migrations/github.go
@@ -14,11 +14,11 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/structs"
"github.com/google/go-github/v64/github"
"golang.org/x/oauth2"
@@ -140,7 +140,7 @@ func (g *GithubDownloaderV3) LogString() string {
func (g *GithubDownloaderV3) addClient(client *http.Client, baseURL string) {
githubClient := github.NewClient(client)
if baseURL != "https://github.com" {
- githubClient, _ = github.NewClient(client).WithEnterpriseURLs(baseURL, baseURL)
+ githubClient, _ = githubClient.WithEnterpriseURLs(baseURL, baseURL)
}
g.clients = append(g.clients, githubClient)
g.rates = append(g.rates, nil)
@@ -220,6 +220,7 @@ func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) {
OriginalURL: gr.GetHTMLURL(),
CloneURL: gr.GetCloneURL(),
DefaultBranch: gr.GetDefaultBranch(),
+ Website: gr.GetHomepage(),
}, nil
}
@@ -884,3 +885,18 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
}
return allReviews, nil
}
+
+// FormatCloneURL add authentication into remote URLs
+func (g *GithubDownloaderV3) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
+ u, err := url.Parse(remoteAddr)
+ if err != nil {
+ return "", err
+ }
+ if len(opts.AuthToken) > 0 {
+ // "multiple tokens" are used to benefit more "API rate limit quota"
+ // git clone doesn't count for rate limits, so only use the first token.
+ // source: https://github.com/orgs/community/discussions/44515
+ u.User = url.UserPassword("oauth2", strings.Split(opts.AuthToken, ",")[0])
+ }
+ return u.String(), nil
+}
diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go
index 20a9282756..c5e24ebbcd 100644
--- a/services/migrations/github_test.go
+++ b/services/migrations/github_test.go
@@ -5,13 +5,12 @@
package migrations
import (
- "context"
"os"
"testing"
"time"
- "code.gitea.io/gitea/models/unittest"
- base "code.gitea.io/gitea/modules/migration"
+ "forgejo.org/models/unittest"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,7 +24,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, token != "")
defer server.Close()
- downloader := NewGithubDownloaderV3(context.Background(), server.URL, "", "", token, "go-gitea", "test_repo")
+ downloader := NewGithubDownloaderV3(t.Context(), server.URL, "", "", token, "go-gitea", "test_repo")
err := downloader.RefreshRate()
require.NoError(t, err)
@@ -38,6 +37,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
CloneURL: server.URL + "/go-gitea/test_repo.git",
OriginalURL: server.URL + "/go-gitea/test_repo",
DefaultBranch: "master",
+ Website: "https://codeberg.org/forgejo/forgejo/",
}, repo)
topics, err := downloader.GetTopics()
@@ -433,3 +433,36 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, reviews)
}
+
+func TestGithubMultiToken(t *testing.T) {
+ testCases := []struct {
+ desc string
+ token string
+ expectedCloneURL string
+ }{
+ {
+ desc: "Single Token",
+ token: "single_token",
+ expectedCloneURL: "https://oauth2:single_token@github.com",
+ },
+ {
+ desc: "Multi Token",
+ token: "token1,token2",
+ expectedCloneURL: "https://oauth2:token1@github.com",
+ },
+ }
+ factory := GithubDownloaderV3Factory{}
+
+ for _, tC := range testCases {
+ t.Run(tC.desc, func(t *testing.T) {
+ opts := base.MigrateOptions{CloneAddr: "https://github.com/go-gitea/gitea", AuthToken: tC.token}
+ client, err := factory.New(t.Context(), opts)
+ require.NoError(t, err)
+
+ cloneURL, err := client.FormatCloneURL(opts, "https://github.com")
+ require.NoError(t, err)
+
+ assert.Equal(t, tC.expectedCloneURL, cloneURL)
+ })
+ }
+}
diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go
index da56d2a175..f54f682c47 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -15,13 +15,14 @@ import (
"strings"
"time"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/structs"
+ issues_model "forgejo.org/models/issues"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/structs"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
)
var (
@@ -98,6 +99,7 @@ func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, passw
// Only use basic auth if token is blank and password is NOT
// Basic auth will fail with empty strings, but empty token will allow anonymous public API usage
if token == "" && password != "" {
+ //nolint // SA1019 gitlab.NewBasicAuthClient is deprecated: GitLab recommends against using this authentication method
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
}
@@ -212,7 +214,7 @@ func (g *GitlabDownloader) GetTopics() ([]string, error) {
if err != nil {
return nil, err
}
- return gr.TagList, err
+ return gr.Topics, err
}
// GetMilestones returns milestones
@@ -543,11 +545,19 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co
}
for _, stateEvent := range stateEvents {
+ // If the user is deleted, then `stateEvent.User == nil` holds. Fallback
+ // to the Ghost user in that case.
+ posterID := int64(user_model.GhostUserID)
+ posterName := user_model.GhostUserName
+ if stateEvent.User != nil {
+ posterID = int64(stateEvent.User.ID)
+ posterName = stateEvent.User.Username
+ }
comment := &base.Comment{
IssueIndex: commentable.GetLocalIndex(),
Index: int64(stateEvent.ID),
- PosterID: int64(stateEvent.User.ID),
- PosterName: stateEvent.User.Username,
+ PosterID: posterID,
+ PosterName: posterName,
Content: "",
Created: *stateEvent.CreatedAt,
}
diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go
index 39edba3cda..30b24f09e8 100644
--- a/services/migrations/gitlab_test.go
+++ b/services/migrations/gitlab_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,13 +12,13 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/json"
- base "code.gitea.io/gitea/modules/migration"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/json"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
)
func TestGitlabDownloadRepo(t *testing.T) {
@@ -31,7 +30,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "")
defer server.Close()
- downloader, err := NewGitlabDownloader(context.Background(), server.URL, "forgejo/test_repo", "", "", gitlabPersonalAccessToken)
+ downloader, err := NewGitlabDownloader(t.Context(), server.URL, "forgejo/test_repo", "", "", gitlabPersonalAccessToken)
if err != nil {
t.Fatalf("NewGitlabDownloader is nil: %v", err)
}
@@ -50,7 +49,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
topics, err := downloader.GetTopics()
require.NoError(t, err)
assert.Len(t, topics, 2)
- assert.EqualValues(t, []string{"migration", "test"}, topics)
+ assert.Equal(t, []string{"migration", "test"}, topics)
milestones, err := downloader.GetMilestones()
require.NoError(t, err)
@@ -331,7 +330,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) {
server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "")
defer server.Close()
- downloader, err := NewGitlabDownloader(context.Background(), server.URL, "troyengel/archbuild", "", "", gitlabPersonalAccessToken)
+ downloader, err := NewGitlabDownloader(t.Context(), server.URL, "troyengel/archbuild", "", "", gitlabPersonalAccessToken)
if err != nil {
t.Fatalf("NewGitlabDownloader is nil: %v", err)
}
@@ -353,7 +352,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) {
// the only issue in this repository has number 2
assert.Len(t, issues, 1)
assert.EqualValues(t, 2, issues[0].Number)
- assert.EqualValues(t, "vpn unlimited errors", issues[0].Title)
+ assert.Equal(t, "vpn unlimited errors", issues[0].Title)
prs, _, err := downloader.GetPullRequests(1, 10)
require.NoError(t, err)
@@ -362,7 +361,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) {
// pull request 3 in Forgejo
assert.Len(t, prs, 1)
assert.EqualValues(t, 3, prs[0].Number)
- assert.EqualValues(t, "Review", prs[0].Title)
+ assert.Equal(t, "Review", prs[0].Title)
}
func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
@@ -454,7 +453,7 @@ func TestGitlabGetReviews(t *testing.T) {
repoID := 1324
downloader := &GitlabDownloader{
- ctx: context.Background(),
+ ctx: t.Context(),
client: client,
repoID: repoID,
}
@@ -532,7 +531,7 @@ func TestAwardsToReactions(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(testResponse), &awards))
reactions := downloader.awardsToReactions(awards)
- assert.EqualValues(t, []*base.Reaction{
+ assert.Equal(t, []*base.Reaction{
{
UserName: "lafriks",
UserID: 1241334,
@@ -624,7 +623,7 @@ func TestNoteToComment(t *testing.T) {
for i, note := range notes {
actualComment := *downloader.convertNoteToComment(17, ¬e)
- assert.EqualValues(t, actualComment, comments[i])
+ assert.Equal(t, actualComment, comments[i])
}
}
diff --git a/services/migrations/gogs.go b/services/migrations/gogs.go
index b31d05fa73..b6fb8cef0a 100644
--- a/services/migrations/gogs.go
+++ b/services/migrations/gogs.go
@@ -11,10 +11,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/structs"
"github.com/gogs/go-gogs-client"
)
@@ -151,6 +151,7 @@ func (g *GogsDownloader) GetRepoInfo() (*base.Repository, error) {
CloneURL: gr.CloneURL,
OriginalURL: gr.HTMLURL,
DefaultBranch: gr.DefaultBranch,
+ Website: gr.Website,
}, nil
}
diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go
index 6c511a2bb5..bf0d063ca4 100644
--- a/services/migrations/gogs_test.go
+++ b/services/migrations/gogs_test.go
@@ -4,13 +4,12 @@
package migrations
import (
- "context"
"net/http"
"os"
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,11 +25,11 @@ func TestGogsDownloadRepo(t *testing.T) {
resp, err := http.Get("https://try.gogs.io/lunnytest/TESTREPO")
if err != nil || resp.StatusCode/100 != 2 {
// skip and don't run test
- t.Skipf("visit test repo failed, ignored")
+ t.Skip("visit test repo failed, ignored")
return
}
- downloader := NewGogsDownloader(context.Background(), "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO")
+ downloader := NewGogsDownloader(t.Context(), "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO")
repo, err := downloader.GetRepoInfo()
require.NoError(t, err)
@@ -207,7 +206,7 @@ func TestGogsDownloaderFactory_New(t *testing.T) {
AuthPassword: tt.args.AuthPassword,
AuthToken: tt.args.AuthToken,
}
- got, err := f.New(context.Background(), opts)
+ got, err := f.New(t.Context(), opts)
if (err != nil) != tt.wantErr {
t.Errorf("GogsDownloaderFactory.New() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -216,9 +215,9 @@ func TestGogsDownloaderFactory_New(t *testing.T) {
}
assert.IsType(t, &GogsDownloader{}, got)
- assert.EqualValues(t, tt.baseURL, got.(*GogsDownloader).baseURL)
- assert.EqualValues(t, tt.repoOwner, got.(*GogsDownloader).repoOwner)
- assert.EqualValues(t, tt.repoName, got.(*GogsDownloader).repoName)
+ assert.Equal(t, tt.baseURL, got.(*GogsDownloader).baseURL)
+ assert.Equal(t, tt.repoOwner, got.(*GogsDownloader).repoOwner)
+ assert.Equal(t, tt.repoName, got.(*GogsDownloader).repoName)
})
}
}
diff --git a/services/migrations/http_client.go b/services/migrations/http_client.go
index 0b997e08f4..26962f2976 100644
--- a/services/migrations/http_client.go
+++ b/services/migrations/http_client.go
@@ -7,9 +7,9 @@ import (
"crypto/tls"
"net/http"
- "code.gitea.io/gitea/modules/hostmatcher"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/hostmatcher"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/setting"
)
// NewMigrationHTTPClient returns a HTTP client for migration
diff --git a/services/migrations/main_test.go b/services/migrations/main_test.go
index d0ec6a3f8d..d543bd6d9c 100644
--- a/services/migrations/main_test.go
+++ b/services/migrations/main_test.go
@@ -8,8 +8,8 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/unittest"
- base "code.gitea.io/gitea/modules/migration"
+ "forgejo.org/models/unittest"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
)
@@ -136,6 +136,7 @@ func assertPullRequestEqual(t *testing.T, expected, actual *base.PullRequest) {
assert.ElementsMatch(t, expected.Assignees, actual.Assignees)
assert.Equal(t, expected.IsLocked, actual.IsLocked)
assertReactionsEqual(t, expected.Reactions, actual.Reactions)
+ assert.Equal(t, expected.Flow, actual.Flow)
}
func assertPullRequestsEqual(t *testing.T, expected, actual []*base.PullRequest) {
@@ -219,6 +220,7 @@ func assertRepositoryEqual(t *testing.T, expected, actual *base.Repository) {
assert.Equal(t, expected.CloneURL, actual.CloneURL)
assert.Equal(t, expected.OriginalURL, actual.OriginalURL)
assert.Equal(t, expected.DefaultBranch, actual.DefaultBranch)
+ assert.Equal(t, expected.Website, actual.Website)
}
func assertReviewEqual(t *testing.T, expected, actual *base.Review) {
diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go
index ccb9cb7e98..61630d9c6d 100644
--- a/services/migrations/migrate.go
+++ b/services/migrations/migrate.go
@@ -6,22 +6,23 @@ package migrations
import (
"context"
+ "errors"
"fmt"
"net"
"net/url"
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/hostmatcher"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/hostmatcher"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
)
// MigrateOptions is equal to base.MigrateOptions
@@ -227,7 +228,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if cloneURL.Scheme == "file" || cloneURL.Scheme == "" {
if cloneAddrURL.Scheme != "file" && cloneAddrURL.Scheme != "" {
- return fmt.Errorf("repo info has changed from external to local filesystem")
+ return errors.New("repo info has changed from external to local filesystem")
}
}
diff --git a/services/migrations/migrate_test.go b/services/migrations/migrate_test.go
index 6e45cbd906..804d01df7a 100644
--- a/services/migrations/migrate_test.go
+++ b/services/migrations/migrate_test.go
@@ -8,9 +8,9 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/require"
)
diff --git a/services/migrations/onedev.go b/services/migrations/onedev.go
index e2f7b771f3..a553a4d8f5 100644
--- a/services/migrations/onedev.go
+++ b/services/migrations/onedev.go
@@ -12,10 +12,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/structs"
)
var (
diff --git a/services/migrations/onedev_test.go b/services/migrations/onedev_test.go
index 80c26130cc..5bb2e2bb5c 100644
--- a/services/migrations/onedev_test.go
+++ b/services/migrations/onedev_test.go
@@ -4,13 +4,12 @@
package migrations
import (
- "context"
"net/http"
"net/url"
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -23,7 +22,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
}
u, _ := url.Parse("https://code.onedev.io")
- downloader := NewOneDevDownloader(context.Background(), u, "", "", "go-gitea-test_repo")
+ downloader := NewOneDevDownloader(t.Context(), u, "", "", "go-gitea-test_repo")
if err != nil {
t.Fatalf("NewOneDevDownloader is nil: %v", err)
}
diff --git a/services/migrations/restore.go b/services/migrations/restore.go
index fd337b22c7..fe2628da52 100644
--- a/services/migrations/restore.go
+++ b/services/migrations/restore.go
@@ -10,7 +10,7 @@ import (
"path/filepath"
"strconv"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"gopkg.in/yaml.v3"
)
@@ -85,6 +85,7 @@ func (r *RepositoryRestorer) GetRepoInfo() (*base.Repository, error) {
OriginalURL: opts["original_url"],
CloneURL: filepath.Join(r.baseDir, "git"),
DefaultBranch: opts["default_branch"],
+ Website: opts["website"],
}, nil
}
diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all
new file mode 100644
index 0000000000..87095d9e24
--- /dev/null
+++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all
@@ -0,0 +1,8 @@
+Access-Control-Expose-Headers: X-Total-Count
+Cache-Control: max-age=0, private, must-revalidate, no-transform
+Content-Type: application/json;charset=utf-8
+X-Content-Type-Options: nosniff
+X-Frame-Options: SAMEORIGIN
+X-Total-Count: 1
+
+[{"id":4980,"url":"https://code.forgejo.org/Gusted/agit-test/pulls/1","number":1,"user":{"id":63,"login":"Gusted","login_name":"26734","source_id":1,"full_name":"","email":"postmaster@gusted.xyz","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"en-US","is_admin":false,"last_login":"2025-04-01T16:35:18Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":true,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"title":"Add extra information","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":[],"requested_reviewers_teams":[],"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":0,"html_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1","diff_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1.diff","patch_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"main","ref":"main","sha":"79ebb873a6497c8847141ba9706b3f757196a1e6","repo_id":1414,"repo":{"id":1414,"owner":{"id":63,"login":"Gusted","login_name":"","source_id":0,"full_name":"","email":"gusted@noreply.code.forgejo.org","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":false,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"name":"agit-test","full_name":"Gusted/agit-test","description":"USED FOR FORGEJO UNIT TESTING","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":36,"language":"","languages_url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test/languages","html_url":"https://code.forgejo.org/Gusted/agit-test","url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test","link":"","ssh_url":"ssh://git@code.forgejo.org/Gusted/agit-test.git","clone_url":"https://code.forgejo.org/Gusted/agit-test.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":1,"release_counter":0,"default_branch":"main","archived":false,"created_at":"2025-04-01T20:25:03Z","updated_at":"2025-04-01T20:25:03Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":true,"push":true,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"wiki_branch":"main","globally_editable_wiki":false,"has_pull_requests":true,"has_projects":true,"has_releases":true,"has_packages":true,"has_actions":true,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":true,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"default_update_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":null}},"head":{"label":"","ref":"refs/pull/1/head","sha":"667e9317ec37b977e6d3d7d43e3440636970563c","repo_id":1414,"repo":{"id":1414,"owner":{"id":63,"login":"Gusted","login_name":"","source_id":0,"full_name":"","email":"gusted@noreply.code.forgejo.org","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":false,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"name":"agit-test","full_name":"Gusted/agit-test","description":"USED FOR FORGEJO UNIT TESTING","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":36,"language":"","languages_url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test/languages","html_url":"https://code.forgejo.org/Gusted/agit-test","url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test","link":"","ssh_url":"ssh://git@code.forgejo.org/Gusted/agit-test.git","clone_url":"https://code.forgejo.org/Gusted/agit-test.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":1,"release_counter":0,"default_branch":"main","archived":false,"created_at":"2025-04-01T20:25:03Z","updated_at":"2025-04-01T20:25:03Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":true,"push":true,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"wiki_branch":"main","globally_editable_wiki":false,"has_pull_requests":true,"has_projects":true,"has_releases":true,"has_packages":true,"has_actions":true,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":true,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"default_update_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":null}},"merge_base":"79ebb873a6497c8847141ba9706b3f757196a1e6","due_date":null,"created_at":"2025-04-01T20:28:45Z","updated_at":"2025-04-01T20:28:45Z","closed_at":null,"pin_order":0,"flow":1}]
diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi
new file mode 100644
index 0000000000..11c4e7b8ba
--- /dev/null
+++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi
@@ -0,0 +1,7 @@
+Content-Length: 117
+Cache-Control: max-age=0, private, must-revalidate, no-transform
+Content-Type: application/json;charset=utf-8
+X-Content-Type-Options: nosniff
+X-Frame-Options: SAMEORIGIN
+
+{"max_response_items":50,"default_paging_num":30,"default_git_trees_per_page":1000,"default_max_blob_size":10485760}
diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion
new file mode 100644
index 0000000000..411ed84e24
--- /dev/null
+++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion
@@ -0,0 +1,7 @@
+Cache-Control: max-age=0, private, must-revalidate, no-transform
+Content-Type: application/json;charset=utf-8
+X-Content-Type-Options: nosniff
+X-Frame-Options: SAMEORIGIN
+Content-Length: 53
+
+{"version":"11.0.0-dev-617-1d1e0ced3e+gitea-1.22.0"}
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo
index df201d9072..7b4441ceae 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo
@@ -4,4 +4,4 @@ Cache-Control: max-age=0, private, must-revalidate, no-transform
Vary: Origin
X-Content-Type-Options: nosniff
-{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}
+{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions
index 5a0d95e0c7..d7b453f63b 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions
@@ -7,4 +7,4 @@ X-Frame-Options: SAMEORIGIN
Content-Type: application/json;charset=utf-8
Content-Length: 1293
-[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"gitea","created_at":"2020-09-01T00:15:14Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"confused","created_at":"2020-09-01T00:15:19Z"}]
+[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"gitea","created_at":"2020-09-01T00:15:14Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"confused","created_at":"2020-09-01T00:15:19Z"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit=50&page=1 b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit=50&page=1
index bf66eb26e3..294aa749cb 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit=50&page=1
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit=50&page=1
@@ -7,4 +7,4 @@ Content-Length: 1824
Cache-Control: max-age=0, private, must-revalidate, no-transform
Vary: Origin
-[{"id":116550,"html_url":"https://gitea.com/gitea/test_repo/issues/4#issuecomment-116550","pull_request_url":"","issue_url":"https://gitea.com/gitea/test_repo/issues/4","user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"body":"a really good question!\n\nIt is the used as TESTSET for gitea2gitea repo migration function","assets":[],"created_at":"2020-09-01T15:49:30Z","updated_at":"2020-09-02T18:21:05Z"},{"id":116552,"html_url":"https://gitea.com/gitea/test_repo/issues/4#issuecomment-116552","pull_request_url":"","issue_url":"https://gitea.com/gitea/test_repo/issues/4","user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"ghost@noreply.gitea.com","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"body":"Oh!","assets":[],"created_at":"2020-09-01T15:49:53Z","updated_at":"2020-09-01T15:49:53Z"}]
+[{"id":116550,"html_url":"https://gitea.com/gitea/test_repo/issues/4#issuecomment-116550","pull_request_url":"","issue_url":"https://gitea.com/gitea/test_repo/issues/4","user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"body":"a really good question!\n\nIt is the used as TESTSET for gitea2gitea repo migration function","assets":[],"created_at":"2020-09-01T15:49:30Z","updated_at":"2020-09-02T18:21:05Z"},{"id":116552,"html_url":"https://gitea.com/gitea/test_repo/issues/4#issuecomment-116552","pull_request_url":"","issue_url":"https://gitea.com/gitea/test_repo/issues/4","user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"ghost@noreply.gitea.com","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"body":"Oh!","assets":[],"created_at":"2020-09-01T15:49:53Z","updated_at":"2020-09-01T15:49:53Z"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions
index 25cefa8da8..0300a779cb 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions
@@ -7,4 +7,4 @@ X-Content-Type-Options: nosniff
Content-Type: application/json;charset=utf-8
Content-Length: 1290
-[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"gitea","created_at":"2020-09-01T19:36:40Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"laugh","created_at":"2020-09-01T19:36:45Z"}]
+[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"gitea","created_at":"2020-09-01T19:36:40Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"laugh","created_at":"2020-09-01T19:36:45Z"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions
index 7d62b318a9..39606a50cf 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions
@@ -7,4 +7,4 @@ Content-Type: application/json;charset=utf-8
Cache-Control: max-age=0, private, must-revalidate, no-transform
X-Total-Count: 2
-[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"+1","created_at":"2020-09-01T16:07:06Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"hooray","created_at":"2020-09-01T16:07:11Z"}]
+[{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"+1","created_at":"2020-09-01T16:07:06Z"},{"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"content":"hooray","created_at":"2020-09-01T16:07:11Z"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=2&page=3&state=all&type=issues b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=2&page=3&state=all&type=issues
index 9133152709..1e552b22d4 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=2&page=3&state=all&type=issues
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=2&page=3&state=all&type=issues
@@ -7,4 +7,4 @@ X-Total-Count: 7
Content-Type: application/json;charset=utf-8
Vary: Origin
-[{"id":30475,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/4","html_url":"https://gitea.com/gitea/test_repo/issues/4","number":4,"user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"title":"what is this repo about?","body":"","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"state":"closed","is_locked":true,"comments":2,"created_at":"2020-09-01T15:48:41Z","updated_at":"2020-09-01T15:50:00Z","closed_at":"2020-09-01T15:49:34Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30471,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/2","html_url":"https://gitea.com/gitea/test_repo/issues/2","number":2,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Spam","body":":(","ref":"","assets":[],"labels":[{"id":3732,"name":"Invalid","exclusive":false,"is_archived":false,"color":"d4c5f9","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3732"}],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":2,"created_at":"2020-09-01T00:23:00Z","updated_at":"2020-09-01T14:11:37Z","closed_at":"2020-09-01T14:11:37Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0}]
+[{"id":30475,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/4","html_url":"https://gitea.com/gitea/test_repo/issues/4","number":4,"user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"title":"what is this repo about?","body":"","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"state":"closed","is_locked":true,"comments":2,"created_at":"2020-09-01T15:48:41Z","updated_at":"2020-09-01T15:50:00Z","closed_at":"2020-09-01T15:49:34Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30471,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/2","html_url":"https://gitea.com/gitea/test_repo/issues/2","number":2,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Spam","body":":(","ref":"","assets":[],"labels":[{"id":3732,"name":"Invalid","exclusive":false,"is_archived":false,"color":"d4c5f9","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3732"}],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":2,"created_at":"2020-09-01T00:23:00Z","updated_at":"2020-09-01T14:11:37Z","closed_at":"2020-09-01T14:11:37Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=50&page=1&state=all&type=issues b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=50&page=1&state=all&type=issues
index 47ff443ac1..6b0a6135ac 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=50&page=1&state=all&type=issues
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit=50&page=1&state=all&type=issues
@@ -6,4 +6,4 @@ Access-Control-Expose-Headers: X-Total-Count
Cache-Control: max-age=0, private, must-revalidate, no-transform
Vary: Origin
-[{"id":30481,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/10","html_url":"https://gitea.com/gitea/test_repo/issues/10","number":10,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"A'm I allowed to fork it?","body":"yes but do not create pull requests anymore","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":null,"assignee":null,"assignees":null,"state":"open","is_locked":false,"comments":0,"created_at":"2020-09-01T17:48:14Z","updated_at":"2020-09-01T17:48:14Z","closed_at":null,"due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30480,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/9","html_url":"https://gitea.com/gitea/test_repo/issues/9","number":9,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Idears","body":"this is an example for an open issue - they just cant be all closed ;)","ref":"","assets":[],"labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":null,"assignees":null,"state":"open","is_locked":false,"comments":0,"created_at":"2020-09-01T17:47:11Z","updated_at":"2020-09-01T17:47:17Z","closed_at":null,"due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30477,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/6","html_url":"https://gitea.com/gitea/test_repo/issues/6","number":6,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"original_author":"","original_author_id":0,"title":"Please add a tag (or a release)","body":"","ref":"","assets":[],"labels":[],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":1,"created_at":"2020-09-01T16:07:01Z","updated_at":"2020-09-01T17:26:02Z","closed_at":"2020-09-01T17:26:02Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30476,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/5","html_url":"https://gitea.com/gitea/test_repo/issues/5","number":5,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"original_author":"","original_author_id":0,"title":"Need more contributors to this repo","body":"I volunteer as one","ref":"","assets":[],"labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":1,"created_at":"2020-09-01T16:06:30Z","updated_at":"2020-09-01T17:46:09Z","closed_at":"2020-09-01T17:46:09Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30475,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/4","html_url":"https://gitea.com/gitea/test_repo/issues/4","number":4,"user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"title":"what is this repo about?","body":"","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"state":"closed","is_locked":true,"comments":2,"created_at":"2020-09-01T15:48:41Z","updated_at":"2020-09-01T15:50:00Z","closed_at":"2020-09-01T15:49:34Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30471,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/2","html_url":"https://gitea.com/gitea/test_repo/issues/2","number":2,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Spam","body":":(","ref":"","assets":[],"labels":[{"id":3732,"name":"Invalid","exclusive":false,"is_archived":false,"color":"d4c5f9","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3732"}],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":2,"created_at":"2020-09-01T00:23:00Z","updated_at":"2020-09-01T14:11:37Z","closed_at":"2020-09-01T14:11:37Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30470,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/1","html_url":"https://gitea.com/gitea/test_repo/issues/1","number":1,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Here Is no content!","body":"","ref":"","assets":[],"labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assignees":[{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"}],"state":"closed","is_locked":false,"comments":0,"created_at":"2020-09-01T00:15:11Z","updated_at":"2020-09-01T17:26:25Z","closed_at":"2020-09-01T17:26:25Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0}]
+[{"id":30481,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/10","html_url":"https://gitea.com/gitea/test_repo/issues/10","number":10,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"A'm I allowed to fork it?","body":"yes but do not create pull requests anymore","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":null,"assignee":null,"assignees":null,"state":"open","is_locked":false,"comments":0,"created_at":"2020-09-01T17:48:14Z","updated_at":"2020-09-01T17:48:14Z","closed_at":null,"due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30480,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/9","html_url":"https://gitea.com/gitea/test_repo/issues/9","number":9,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Idears","body":"this is an example for an open issue - they just cant be all closed ;)","ref":"","assets":[],"labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":null,"assignees":null,"state":"open","is_locked":false,"comments":0,"created_at":"2020-09-01T17:47:11Z","updated_at":"2020-09-01T17:47:17Z","closed_at":null,"due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30477,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/6","html_url":"https://gitea.com/gitea/test_repo/issues/6","number":6,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"original_author":"","original_author_id":0,"title":"Please add a tag (or a release)","body":"","ref":"","assets":[],"labels":[],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":1,"created_at":"2020-09-01T16:07:01Z","updated_at":"2020-09-01T17:26:02Z","closed_at":"2020-09-01T17:26:02Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30476,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/5","html_url":"https://gitea.com/gitea/test_repo/issues/5","number":5,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"original_author":"","original_author_id":0,"title":"Need more contributors to this repo","body":"I volunteer as one","ref":"","assets":[],"labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":1,"created_at":"2020-09-01T16:06:30Z","updated_at":"2020-09-01T17:46:09Z","closed_at":"2020-09-01T17:46:09Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30475,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/4","html_url":"https://gitea.com/gitea/test_repo/issues/4","number":4,"user":{"id":-1,"login":"Ghost","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/assets/img/avatar_default.png","html_url":"https://gitea.com/Ghost","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"1970-01-01T00:00:00Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"Ghost"},"original_author":"","original_author_id":0,"title":"what is this repo about?","body":"","ref":"","assets":[],"labels":[{"id":3733,"name":"Question","exclusive":false,"is_archived":false,"color":"fbca04","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3733"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"state":"closed","is_locked":true,"comments":2,"created_at":"2020-09-01T15:48:41Z","updated_at":"2020-09-01T15:50:00Z","closed_at":"2020-09-01T15:49:34Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30471,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/2","html_url":"https://gitea.com/gitea/test_repo/issues/2","number":2,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Spam","body":":(","ref":"","assets":[],"labels":[{"id":3732,"name":"Invalid","exclusive":false,"is_archived":false,"color":"d4c5f9","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3732"}],"milestone":null,"assignee":null,"assignees":null,"state":"closed","is_locked":false,"comments":2,"created_at":"2020-09-01T00:23:00Z","updated_at":"2020-09-01T14:11:37Z","closed_at":"2020-09-01T14:11:37Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0},{"id":30470,"url":"https://gitea.com/api/v1/repos/gitea/test_repo/issues/1","html_url":"https://gitea.com/gitea/test_repo/issues/1","number":1,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"original_author":"","original_author_id":0,"title":"Here Is no content!","body":"","ref":"","assets":[],"labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assignees":[{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"}],"state":"closed","is_locked":false,"comments":0,"created_at":"2020-09-01T00:15:11Z","updated_at":"2020-09-01T17:26:25Z","closed_at":"2020-09-01T17:26:25Z","due_date":null,"pull_request":null,"repository":{"id":16268,"name":"test_repo","owner":"gitea","full_name":"gitea/test_repo"},"pin_order":0}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1770%2Fcomments b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1770%2Fcomments
index 81e4dcfd7f..c8cb26c4d1 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1770%2Fcomments
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1770%2Fcomments
@@ -5,4 +5,4 @@ Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
-[{"id":116561,"body":"is one `\\newline` to less?","user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"resolver":null,"pull_request_review_id":1770,"created_at":"2020-09-01T16:12:58Z","updated_at":"2024-06-03T01:18:36Z","path":"README.md","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","original_commit_id":"","diff_hunk":"@@ -2,3 +2,3 @@\n \n-Test repository for testing migration from gitea 2 gitea\n\\ No newline at end of file\n+Test repository for testing migration from gitea 2 gitea","position":4,"original_position":0,"html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116561","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"}]
+[{"id":116561,"body":"is one `\\newline` to less?","user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"resolver":null,"pull_request_review_id":1770,"created_at":"2020-09-01T16:12:58Z","updated_at":"2024-06-03T01:18:36Z","path":"README.md","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","original_commit_id":"","diff_hunk":"@@ -2,3 +2,3 @@\n \n-Test repository for testing migration from gitea 2 gitea\n\\ No newline at end of file\n+Test repository for testing migration from gitea 2 gitea","position":4,"original_position":0,"html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116561","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%3Flimit=50&page=1 b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%3Flimit=50&page=1
index 8e54635dfc..6aa650bd9c 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%3Flimit=50&page=1
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%3Flimit=50&page=1
@@ -6,4 +6,4 @@ Vary: Origin
X-Frame-Options: SAMEORIGIN
X-Total-Count: 3
-[{"id":1770,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"team":null,"state":"COMMENT","body":"","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":false,"dismissed":true,"comments_count":1,"submitted_at":"2020-09-01T16:12:58Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116562","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"},{"id":1771,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"team":null,"state":"REQUEST_CHANGES","body":"I think this needs some changes","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":false,"dismissed":true,"comments_count":0,"submitted_at":"2020-09-01T17:06:47Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116563","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"},{"id":1772,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"team":null,"state":"APPROVED","body":"looks good","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":true,"dismissed":true,"comments_count":0,"submitted_at":"2020-09-01T17:19:51Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116564","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"}]
+[{"id":1770,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"team":null,"state":"COMMENT","body":"","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":false,"dismissed":true,"comments_count":1,"submitted_at":"2020-09-01T16:12:58Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116562","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"},{"id":1771,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"team":null,"state":"REQUEST_CHANGES","body":"I think this needs some changes","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":false,"dismissed":true,"comments_count":0,"submitted_at":"2020-09-01T17:06:47Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116563","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"},{"id":1772,"user":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"team":null,"state":"APPROVED","body":"looks good","commit_id":"187ece0cb6631e2858a6872e5733433bb3ca3b03","stale":false,"official":true,"dismissed":true,"comments_count":0,"submitted_at":"2020-09-01T17:19:51Z","updated_at":"2021-04-18T22:00:49Z","html_url":"https://gitea.com/gitea/test_repo/pulls/7#issuecomment-116564","pull_request_url":"https://gitea.com/gitea/test_repo/pulls/7"}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=3&page=1&state=all b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=3&page=1&state=all
index 56417b0e90..256c67cc1a 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=3&page=1&state=all
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=3&page=1&state=all
@@ -7,4 +7,4 @@ X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Total-Count: 6
-[{"id":4955,"url":"https://gitea.com/gitea/test_repo/pulls/13","number":13,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"extend","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":true,"comments":1,"review_comments":0,"additions":1,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/13","diff_url":"https://gitea.com/gitea/test_repo/pulls/13.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/13.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"6543-patch-1","ref":"6543-patch-1","sha":"0ba7693bfd50d26df7f1b7414e937786c5efb05d","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","due_date":null,"created_at":"2020-09-01T18:03:54Z","updated_at":"2020-09-01T18:04:26Z","closed_at":null,"pin_order":0},{"id":4954,"url":"https://gitea.com/gitea/test_repo/pulls/12","number":12,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Dont Touch","body":"\r\nadd dont touch note","labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"assignees":[{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"}],"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":3,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/12","diff_url":"https://gitea.com/gitea/test_repo/pulls/12.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/12.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:55:34Z","merge_commit_sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"Add-Dont-Touch-Note","ref":"refs/pull/12/head","sha":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:52:39Z","updated_at":"2020-09-02T05:10:25Z","closed_at":"2020-09-01T17:55:33Z","pin_order":0},{"id":4953,"url":"https://gitea.com/gitea/test_repo/pulls/11","number":11,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add-xkcd-2199","body":"","labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/11","diff_url":"https://gitea.com/gitea/test_repo/pulls/11.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/11.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"add-xkcd-2199","ref":"add-xkcd-2199","sha":"6bbd02573205288faa95d25e917812b2815a37e5","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","due_date":null,"created_at":"2020-09-01T17:52:28Z","updated_at":"2020-09-01T17:52:29Z","closed_at":null,"pin_order":0}]
+[{"id":4955,"url":"https://gitea.com/gitea/test_repo/pulls/13","number":13,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"extend","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":true,"comments":1,"review_comments":0,"additions":1,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/13","diff_url":"https://gitea.com/gitea/test_repo/pulls/13.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/13.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"6543-patch-1","ref":"6543-patch-1","sha":"0ba7693bfd50d26df7f1b7414e937786c5efb05d","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","due_date":null,"created_at":"2020-09-01T18:03:54Z","updated_at":"2020-09-01T18:04:26Z","closed_at":null,"pin_order":0},{"id":4954,"url":"https://gitea.com/gitea/test_repo/pulls/12","number":12,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Dont Touch","body":"\r\nadd dont touch note","labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"assignees":[{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"}],"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":3,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/12","diff_url":"https://gitea.com/gitea/test_repo/pulls/12.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/12.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:55:34Z","merge_commit_sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"Add-Dont-Touch-Note","ref":"refs/pull/12/head","sha":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:52:39Z","updated_at":"2020-09-02T05:10:25Z","closed_at":"2020-09-01T17:55:33Z","pin_order":0},{"id":4953,"url":"https://gitea.com/gitea/test_repo/pulls/11","number":11,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add-xkcd-2199","body":"","labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/11","diff_url":"https://gitea.com/gitea/test_repo/pulls/11.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/11.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"add-xkcd-2199","ref":"add-xkcd-2199","sha":"6bbd02573205288faa95d25e917812b2815a37e5","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","due_date":null,"created_at":"2020-09-01T17:52:28Z","updated_at":"2020-09-01T17:52:29Z","closed_at":null,"pin_order":0}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=50&page=1&state=all b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=50&page=1&state=all
index 91b4c96fa2..e3a86c5b54 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=50&page=1&state=all
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit=50&page=1&state=all
@@ -6,4 +6,4 @@ X-Total-Count: 6
Content-Type: application/json;charset=utf-8
X-Frame-Options: SAMEORIGIN
-[{"id":4955,"url":"https://gitea.com/gitea/test_repo/pulls/13","number":13,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"extend","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":true,"comments":1,"review_comments":0,"additions":1,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/13","diff_url":"https://gitea.com/gitea/test_repo/pulls/13.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/13.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"6543-patch-1","ref":"6543-patch-1","sha":"0ba7693bfd50d26df7f1b7414e937786c5efb05d","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","due_date":null,"created_at":"2020-09-01T18:03:54Z","updated_at":"2020-09-01T18:04:26Z","closed_at":null,"pin_order":0},{"id":4954,"url":"https://gitea.com/gitea/test_repo/pulls/12","number":12,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Dont Touch","body":"\r\nadd dont touch note","labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"assignees":[{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://techknowlogick.com","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"}],"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":3,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/12","diff_url":"https://gitea.com/gitea/test_repo/pulls/12.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/12.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:55:34Z","merge_commit_sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"Add-Dont-Touch-Note","ref":"refs/pull/12/head","sha":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:52:39Z","updated_at":"2020-09-02T05:10:25Z","closed_at":"2020-09-01T17:55:33Z","pin_order":0},{"id":4953,"url":"https://gitea.com/gitea/test_repo/pulls/11","number":11,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add-xkcd-2199","body":"","labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/11","diff_url":"https://gitea.com/gitea/test_repo/pulls/11.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/11.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"add-xkcd-2199","ref":"add-xkcd-2199","sha":"6bbd02573205288faa95d25e917812b2815a37e5","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","due_date":null,"created_at":"2020-09-01T17:52:28Z","updated_at":"2020-09-01T17:52:29Z","closed_at":null,"pin_order":0},{"id":4952,"url":"https://gitea.com/gitea/test_repo/pulls/8","number":8,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add garbage for close pull","body":"well you'll see","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/8","diff_url":"https://gitea.com/gitea/test_repo/pulls/8.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/8.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"garbage-patch","ref":"refs/pull/8/head","sha":"a3427235639a33d2d749e76f076e7619acc75341","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:43:20Z","updated_at":"2020-09-01T17:48:41Z","closed_at":"2020-09-01T17:48:29Z","pin_order":0},{"id":4951,"url":"https://gitea.com/gitea/test_repo/pulls/7","number":7,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Prepare for Release V1","body":"@techknowlogick you might have a look at it?\n\nclose #6","labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":1,"review_comments":3,"additions":3,"deletions":1,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/7","diff_url":"https://gitea.com/gitea/test_repo/pulls/7.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/7.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:26:02Z","merge_commit_sha":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"prepare-v1","ref":"refs/pull/7/head","sha":"187ece0cb6631e2858a6872e5733433bb3ca3b03","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"9396b697d905d1bcb5380befdf4d7e52c6a7ceb2","due_date":null,"created_at":"2020-09-01T16:10:04Z","updated_at":"2020-09-01T17:26:08Z","closed_at":"2020-09-01T17:26:02Z","pin_order":0},{"id":4949,"url":"https://gitea.com/gitea/test_repo/pulls/3","number":3,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Readme: use '2'","body":"","labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":1,"deletions":1,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/3","diff_url":"https://gitea.com/gitea/test_repo/pulls/3.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/3.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T00:27:14Z","merge_commit_sha":"9396b697d905d1bcb5380befdf4d7e52c6a7ceb2","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"readme_nit","ref":"refs/pull/3/head","sha":"c273a16d4c3b2d745df690005dabe79cc6504ac3","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://about.gitea.com","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"a016fd754759b2cdfe5cad1cdf638c7e6b281940","due_date":null,"created_at":"2020-09-01T00:27:03Z","updated_at":"2020-09-01T15:54:30Z","closed_at":"2020-09-01T00:27:14Z","pin_order":0}]
+[{"id":4955,"url":"https://gitea.com/gitea/test_repo/pulls/13","number":13,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"extend","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":true,"comments":1,"review_comments":0,"additions":1,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/13","diff_url":"https://gitea.com/gitea/test_repo/pulls/13.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/13.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"6543-patch-1","ref":"6543-patch-1","sha":"0ba7693bfd50d26df7f1b7414e937786c5efb05d","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","due_date":null,"created_at":"2020-09-01T18:03:54Z","updated_at":"2020-09-01T18:04:26Z","closed_at":null,"pin_order":0},{"id":4954,"url":"https://gitea.com/gitea/test_repo/pulls/12","number":12,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Dont Touch","body":"\r\nadd dont touch note","labels":[],"milestone":{"id":1301,"title":"V2 Finalize","description":"","state":"open","open_issues":1,"closed_issues":2,"created_at":"1970-01-01T00:00:00Z","updated_at":"2022-11-13T05:29:15Z","closed_at":null,"due_on":"2020-09-04T23:59:59Z"},"assignee":{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"},"assignees":[{"id":9,"login":"techknowlogick","login_name":"","source_id":0,"full_name":"","email":"techknowlogick@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/9b588dd0b384d6f6ae841c5d62302033","html_url":"https://gitea.com/techknowlogick","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-01-14T06:48:35Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"","visibility":"public","followers_count":11,"following_count":1,"starred_repos_count":4,"username":"techknowlogick"}],"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":3,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/12","diff_url":"https://gitea.com/gitea/test_repo/pulls/12.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/12.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:55:34Z","merge_commit_sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"Add-Dont-Touch-Note","ref":"refs/pull/12/head","sha":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:52:39Z","updated_at":"2020-09-02T05:10:25Z","closed_at":"2020-09-01T17:55:33Z","pin_order":0},{"id":4953,"url":"https://gitea.com/gitea/test_repo/pulls/11","number":11,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add-xkcd-2199","body":"","labels":[{"id":3734,"name":"Valid","exclusive":false,"is_archived":false,"color":"53e917","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3734"}],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/11","diff_url":"https://gitea.com/gitea/test_repo/pulls/11.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/11.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"add-xkcd-2199","ref":"add-xkcd-2199","sha":"6bbd02573205288faa95d25e917812b2815a37e5","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"b6ab5d9ae000b579a5fff03f92c486da4ddf48b6","due_date":null,"created_at":"2020-09-01T17:52:28Z","updated_at":"2020-09-01T17:52:29Z","closed_at":null,"pin_order":0},{"id":4952,"url":"https://gitea.com/gitea/test_repo/pulls/8","number":8,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"add garbage for close pull","body":"well you'll see","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":1,"deletions":2,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/8","diff_url":"https://gitea.com/gitea/test_repo/pulls/8.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/8.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"garbage-patch","ref":"refs/pull/8/head","sha":"a3427235639a33d2d749e76f076e7619acc75341","repo_id":16280,"repo":{"id":16280,"owner":{"id":9756,"login":"6543-forks","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/c3948ab3b9b62e070e87a22681909dee","html_url":"https://gitea.com/6543-forks","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2020-09-01T17:33:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"https://codeberg.org/forgejo/forgejo/","description":"@6543's fork org","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"6543-forks"},"name":"test_repo","full_name":"6543-forks/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":true,"template":false,"parent":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null},"mirror":false,"size":67,"language":"","languages_url":"https://gitea.com/api/v1/repos/6543-forks/test_repo/languages","html_url":"https://gitea.com/6543-forks/test_repo","url":"https://gitea.com/api/v1/repos/6543-forks/test_repo","link":"","ssh_url":"git@gitea.com:6543-forks/test_repo.git","clone_url":"https://gitea.com/6543-forks/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-09-01T17:39:26Z","updated_at":"2020-09-01T17:57:07Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":false,"has_wiki":false,"has_pull_requests":false,"has_projects":false,"projects_mode":"all","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_squash_merge":false,"allow_fast_forward_only_merge":false,"allow_rebase_update":false,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":[],"licenses":null}},"merge_base":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","due_date":null,"created_at":"2020-09-01T17:43:20Z","updated_at":"2020-09-01T17:48:41Z","closed_at":"2020-09-01T17:48:29Z","pin_order":0},{"id":4951,"url":"https://gitea.com/gitea/test_repo/pulls/7","number":7,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Prepare for Release V1","body":"@techknowlogick you might have a look at it?\n\nclose #6","labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":1,"review_comments":3,"additions":3,"deletions":1,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/7","diff_url":"https://gitea.com/gitea/test_repo/pulls/7.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/7.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T17:26:02Z","merge_commit_sha":"d9e165e4c7ab6b701f0205d0ffb637e5d2856297","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"prepare-v1","ref":"refs/pull/7/head","sha":"187ece0cb6631e2858a6872e5733433bb3ca3b03","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"9396b697d905d1bcb5380befdf4d7e52c6a7ceb2","due_date":null,"created_at":"2020-09-01T16:10:04Z","updated_at":"2020-09-01T17:26:08Z","closed_at":"2020-09-01T17:26:02Z","pin_order":0},{"id":4949,"url":"https://gitea.com/gitea/test_repo/pulls/3","number":3,"user":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@obermui.de","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"title":"Readme: use '2'","body":"","labels":[{"id":3735,"name":"Enhancement","exclusive":false,"is_archived":false,"color":"207de5","description":"","url":"https://gitea.com/api/v1/repos/gitea/test_repo/labels/3735"}],"milestone":{"id":1300,"title":"V1","description":"Generate Content","state":"closed","open_issues":0,"closed_issues":4,"created_at":"1970-01-01T00:00:00Z","updated_at":"1970-01-01T00:00:00Z","closed_at":"2020-09-01T18:36:46Z","due_on":null},"assignee":null,"assignees":null,"requested_reviewers":null,"requested_reviewers_teams":null,"state":"closed","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":1,"deletions":1,"changed_files":1,"html_url":"https://gitea.com/gitea/test_repo/pulls/3","diff_url":"https://gitea.com/gitea/test_repo/pulls/3.diff","patch_url":"https://gitea.com/gitea/test_repo/pulls/3.patch","mergeable":true,"merged":true,"merged_at":"2020-09-01T00:27:14Z","merge_commit_sha":"9396b697d905d1bcb5380befdf4d7e52c6a7ceb2","merged_by":null,"allow_maintainer_edit":false,"base":{"label":"master","ref":"master","sha":"827aa28a907853e5ddfa40c8f9bc52471a2685fd","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"head":{"label":"readme_nit","ref":"refs/pull/3/head","sha":"c273a16d4c3b2d745df690005dabe79cc6504ac3","repo_id":16268,"repo":{"id":16268,"owner":{"id":3,"login":"gitea","login_name":"","source_id":0,"full_name":"","email":"","avatar_url":"https://gitea.com/avatars/35dea380390772b3130aafbac7ca49e6","html_url":"https://gitea.com/gitea","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-11-29T03:16:17Z","restricted":false,"active":false,"prohibit_login":false,"location":"Git Universe","website":"https://codeberg.org/forgejo/forgejo/","description":"Git with a cup of tea","visibility":"public","followers_count":53,"following_count":0,"starred_repos_count":0,"username":"gitea"},"name":"test_repo","full_name":"gitea/test_repo","description":"Test repository for testing migration from gitea to gitea","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":68,"language":"","languages_url":"https://gitea.com/api/v1/repos/gitea/test_repo/languages","html_url":"https://gitea.com/gitea/test_repo","url":"https://gitea.com/api/v1/repos/gitea/test_repo","link":"","ssh_url":"git@gitea.com:gitea/test_repo.git","clone_url":"https://gitea.com/gitea/test_repo.git","original_url":"","website":"https://codeberg.org/forgejo/forgejo/","stars_count":1,"forks_count":2,"watchers_count":9,"open_issues_count":2,"open_pr_counter":2,"release_counter":2,"default_branch":"master","archived":false,"created_at":"2020-09-01T00:12:27Z","updated_at":"2020-09-01T18:03:41Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":true,"projects_mode":"","has_releases":true,"has_packages":false,"has_actions":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":false,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":["gitea","test","migration","ci"],"licenses":null}},"merge_base":"a016fd754759b2cdfe5cad1cdf638c7e6b281940","due_date":null,"created_at":"2020-09-01T00:27:03Z","updated_at":"2020-09-01T15:54:30Z","closed_at":"2020-09-01T00:27:14Z","pin_order":0}]
diff --git a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit=50&page=1 b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit=50&page=1
index 13f5706fc1..bfd164ac2a 100644
--- a/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit=50&page=1
+++ b/services/migrations/testdata/gitea/full_download/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit=50&page=1
@@ -6,4 +6,4 @@ Cache-Control: max-age=0, private, must-revalidate, no-transform
X-Content-Type-Options: nosniff
X-Total-Count: 2
-[{"id":167250,"tag_name":"v2-rc1","target_commitish":"master","name":"Second Release","body":"this repo has:\r\n* reactions\r\n* wiki\r\n* issues (open/closed)\r\n* pulls (open/closed/merged) (external/internal)\r\n* pull reviews\r\n* projects\r\n* milestones\r\n* labels\r\n* releases\r\n\r\nto test migration against","url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167250","html_url":"https://gitea.com/gitea/test_repo/releases/tag/v2-rc1","tarball_url":"https://gitea.com/gitea/test_repo/archive/v2-rc1.tar.gz","zipball_url":"https://gitea.com/gitea/test_repo/archive/v2-rc1.zip","upload_url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167250/assets","draft":false,"prerelease":true,"created_at":"2020-09-01T18:02:43Z","published_at":"2020-09-01T18:02:43Z","author":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assets":[]},{"id":167249,"tag_name":"V1","target_commitish":"master","name":"First Release","body":"as title","url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167249","html_url":"https://gitea.com/gitea/test_repo/releases/tag/V1","tarball_url":"https://gitea.com/gitea/test_repo/archive/V1.tar.gz","zipball_url":"https://gitea.com/gitea/test_repo/archive/V1.zip","upload_url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167249/assets","draft":false,"prerelease":false,"created_at":"2020-09-01T17:30:32Z","published_at":"2020-09-01T17:30:32Z","author":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://mh.obermui.de","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assets":[]}]
+[{"id":167250,"tag_name":"v2-rc1","target_commitish":"master","name":"Second Release","body":"this repo has:\r\n* reactions\r\n* wiki\r\n* issues (open/closed)\r\n* pulls (open/closed/merged) (external/internal)\r\n* pull reviews\r\n* projects\r\n* milestones\r\n* labels\r\n* releases\r\n\r\nto test migration against","url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167250","html_url":"https://gitea.com/gitea/test_repo/releases/tag/v2-rc1","tarball_url":"https://gitea.com/gitea/test_repo/archive/v2-rc1.tar.gz","zipball_url":"https://gitea.com/gitea/test_repo/archive/v2-rc1.zip","upload_url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167250/assets","draft":false,"prerelease":true,"created_at":"2020-09-01T18:02:43Z","published_at":"2020-09-01T18:02:43Z","author":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assets":[]},{"id":167249,"tag_name":"V1","target_commitish":"master","name":"First Release","body":"as title","url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167249","html_url":"https://gitea.com/gitea/test_repo/releases/tag/V1","tarball_url":"https://gitea.com/gitea/test_repo/archive/V1.tar.gz","zipball_url":"https://gitea.com/gitea/test_repo/archive/V1.zip","upload_url":"https://gitea.com/api/v1/repos/gitea/test_repo/releases/167249/assets","draft":false,"prerelease":false,"created_at":"2020-09-01T17:30:32Z","published_at":"2020-09-01T17:30:32Z","author":{"id":689,"login":"6543","login_name":"","source_id":0,"full_name":"","email":"6543@noreply.gitea.com","avatar_url":"https://gitea.com/avatars/aeb6c290f1988daefa7421c5409e80dc","html_url":"https://gitea.com/6543","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2019-07-17T21:08:41Z","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","website":"https://codeberg.org/forgejo/forgejo/","description":"gitea instance: https://code.obermui.de","visibility":"public","followers_count":10,"following_count":7,"starred_repos_count":18,"username":"6543"},"assets":[]}]
diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo
index 261a153a32..78fde4d424 100644
--- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo
+++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo
@@ -22,4 +22,4 @@ X-Ratelimit-Used: 72
X-Frame-Options: deny
Content-Security-Policy: default-src 'none'
-{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":null,"size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master","permissions":{"admin":false,"maintain":false,"push":false,"triage":false,"pull":true},"temp_clone_token":"","custom_properties":{},"organization":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"network_count":6,"subscribers_count":6}
\ No newline at end of file
+{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master","permissions":{"admin":false,"maintain":false,"push":false,"triage":false,"pull":true},"temp_clone_token":"","custom_properties":{},"organization":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"network_count":6,"subscribers_count":6}
diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all
index ff05f4da0f..30883cc283 100644
--- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all
+++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all
@@ -21,4 +21,4 @@ Etag: W/"fed37ab8ce0b78e713030ff3e601f540b7f71243ab788b477f6885119bd691c5"
X-Accepted-Oauth-Scopes:
X-Content-Type-Options: nosniff
-[{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","id":340118745,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTE4NzQ1","html_url":"https://github.com/go-gitea/test_repo/pull/3","diff_url":"https://github.com/go-gitea/test_repo/pull/3.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/3.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3","number":3,"state":"closed","locked":false,"title":"Update README.md","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"add warning to readme","created_at":"2019-11-12T21:21:43Z","updated_at":"2019-11-12T21:39:28Z","closed_at":"2019-11-12T21:39:27Z","merged_at":"2019-11-12T21:39:27Z","merge_commit_sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254254,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU0","url":"https://api.github.com/repos/go-gitea/test_repo/labels/documentation","name":"documentation","color":"0075ca","default":true,"description":"Improvements or additions to documentation"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2","html_url":"https://github.com/go-gitea/test_repo/milestone/2","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2/labels","id":4839942,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0Mg==","number":2,"title":"1.1.0","description":"Milestone 1.1.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":0,"closed_issues":2,"state":"closed","created_at":"2019-11-12T19:37:25Z","updated_at":"2019-11-12T21:39:27Z","due_on":"2019-11-12T08:00:00Z","closed_at":"2019-11-12T19:45:46Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b","head":{"label":"mrsdizzie:master","ref":"master","sha":"076160cf0b039f13e5eff19619932d181269414b","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":null,"size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"72866af952e98d02a73003501836074b286a78f6","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":null,"size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/3"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null},{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","id":340131577,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTMxNTc3","html_url":"https://github.com/go-gitea/test_repo/pull/4","diff_url":"https://github.com/go-gitea/test_repo/pull/4.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/4.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4","number":4,"state":"open","locked":false,"title":"Test branch","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"do not merge this PR","created_at":"2019-11-12T21:54:18Z","updated_at":"2020-01-04T11:30:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254252,"node_id":"MDU6TGFiZWwxNjY3MjU0MjUy","url":"https://api.github.com/repos/go-gitea/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1","html_url":"https://github.com/go-gitea/test_repo/milestone/1","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1/labels","id":4839941,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0MQ==","number":1,"title":"1.0.0","description":"Milestone 1.0.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":1,"state":"closed","created_at":"2019-11-12T19:37:08Z","updated_at":"2019-11-12T21:56:17Z","due_on":"2019-11-11T08:00:00Z","closed_at":"2019-11-12T19:45:49Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d","head":{"label":"mrsdizzie:test-branch","ref":"test-branch","sha":"2be9101c543658591222acbee3eb799edfc3853d","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":null,"size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":null,"size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/4"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null}]
\ No newline at end of file
+[{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","id":340118745,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTE4NzQ1","html_url":"https://github.com/go-gitea/test_repo/pull/3","diff_url":"https://github.com/go-gitea/test_repo/pull/3.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/3.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3","number":3,"state":"closed","locked":false,"title":"Update README.md","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"add warning to readme","created_at":"2019-11-12T21:21:43Z","updated_at":"2019-11-12T21:39:28Z","closed_at":"2019-11-12T21:39:27Z","merged_at":"2019-11-12T21:39:27Z","merge_commit_sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254254,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU0","url":"https://api.github.com/repos/go-gitea/test_repo/labels/documentation","name":"documentation","color":"0075ca","default":true,"description":"Improvements or additions to documentation"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2","html_url":"https://github.com/go-gitea/test_repo/milestone/2","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2/labels","id":4839942,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0Mg==","number":2,"title":"1.1.0","description":"Milestone 1.1.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":0,"closed_issues":2,"state":"closed","created_at":"2019-11-12T19:37:25Z","updated_at":"2019-11-12T21:39:27Z","due_on":"2019-11-12T08:00:00Z","closed_at":"2019-11-12T19:45:46Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b","head":{"label":"mrsdizzie:master","ref":"master","sha":"076160cf0b039f13e5eff19619932d181269414b","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"72866af952e98d02a73003501836074b286a78f6","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/3"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null},{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","id":340131577,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTMxNTc3","html_url":"https://github.com/go-gitea/test_repo/pull/4","diff_url":"https://github.com/go-gitea/test_repo/pull/4.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/4.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4","number":4,"state":"open","locked":false,"title":"Test branch","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"do not merge this PR","created_at":"2019-11-12T21:54:18Z","updated_at":"2020-01-04T11:30:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254252,"node_id":"MDU6TGFiZWwxNjY3MjU0MjUy","url":"https://api.github.com/repos/go-gitea/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1","html_url":"https://github.com/go-gitea/test_repo/milestone/1","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1/labels","id":4839941,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0MQ==","number":1,"title":"1.0.0","description":"Milestone 1.0.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":1,"state":"closed","created_at":"2019-11-12T19:37:08Z","updated_at":"2019-11-12T21:56:17Z","due_on":"2019-11-11T08:00:00Z","closed_at":"2019-11-12T19:45:49Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d","head":{"label":"mrsdizzie:test-branch","ref":"test-branch","sha":"2be9101c543658591222acbee3eb799edfc3853d","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/4"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null}]
diff --git a/services/migrations/update.go b/services/migrations/update.go
index 4a49206f82..4d497c1e2e 100644
--- a/services/migrations/update.go
+++ b/services/migrations/update.go
@@ -6,11 +6,11 @@ package migrations
import (
"context"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/externalaccount"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/externalaccount"
)
// UpdateMigrationPosterID updates all migrated repositories' issues and comments posterID
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index bc2d6711cf..514b7c3969 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -5,14 +5,14 @@ package mirror
import (
"context"
- "fmt"
+ "errors"
- quota_model "code.gitea.io/gitea/models/quota"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
+ quota_model "forgejo.org/models/quota"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
)
// doMirrorSync causes this request to mirror itself
@@ -31,7 +31,7 @@ func doMirrorSync(ctx context.Context, req *SyncRequest) {
}
}
-var errLimit = fmt.Errorf("reached limit")
+var errLimit = errors.New("reached limit")
// Update checks and updates mirror repositories.
func Update(ctx context.Context, pullLimit, pushLimit int) error {
@@ -70,7 +70,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
// Check we've not been cancelled
select {
case <-ctx.Done():
- return fmt.Errorf("aborted")
+ return errors.New("aborted")
default:
}
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 2d128919f9..c46323f283 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -9,21 +9,21 @@ import (
"strings"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- giturl "code.gitea.io/gitea/modules/git/url"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/proxy"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ giturl "forgejo.org/modules/git/url"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/proxy"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
)
// gitShortEmptySha Git short empty SHA
@@ -40,7 +40,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
repoPath := m.GetRepository(ctx).RepoPath()
// Remove old remote
_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
@@ -51,7 +51,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath))
}
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
@@ -60,7 +60,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr)
// Remove old remote of wiki
_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
@@ -71,7 +71,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath))
}
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
}
@@ -96,6 +96,7 @@ type mirrorSyncResult struct {
/*
// * [new tag] v0.1.8 -> v0.1.8
// * [new branch] master -> origin/master
+// * [new ref] refs/pull/2/head -> refs/pull/2/head"
// - [deleted] (none) -> origin/test // delete a branch
// - [deleted] (none) -> 1 // delete a tag
// 957a993..a87ba5f test -> origin/test
@@ -126,10 +127,17 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
refName: git.RefNameFromBranch(refName),
oldCommitID: gitShortEmptySha,
})
+ case strings.HasPrefix(lines[i], " * [new ref]"): // new reference
+ results = append(results, &mirrorSyncResult{
+ refName: git.RefName(refName),
+ oldCommitID: gitShortEmptySha,
+ })
case strings.HasPrefix(lines[i], " - "): // Delete reference
isTag := !strings.HasPrefix(refName, remoteName+"/")
var refFullName git.RefName
- if isTag {
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else if isTag {
refFullName = git.RefNameFromTag(refName)
} else {
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
@@ -152,8 +160,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
log.Error("Expect two SHAs but not what found: %q", lines[i])
continue
}
+ var refFullName git.RefName
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else {
+ refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
+ }
+
results = append(results, &mirrorSyncResult{
- refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")),
+ refName: refFullName,
oldCommitID: shas[0],
newCommitID: shas[1],
})
@@ -168,8 +183,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
log.Error("Expect two SHAs but not what found: %q", lines[i])
continue
}
+ var refFullName git.RefName
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else {
+ refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
+ }
+
results = append(results, &mirrorSyncResult{
- refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")),
+ refName: refFullName,
oldCommitID: shas[0],
newCommitID: shas[1],
})
@@ -222,6 +244,24 @@ func pruneBrokenReferences(ctx context.Context,
return pruneErr
}
+// checkRecoverableSyncError takes an error message from a git fetch command and returns false if it should be a fatal/blocking error
+func checkRecoverableSyncError(stderrMessage string) bool {
+ switch {
+ case strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken"):
+ return true
+ case strings.Contains(stderrMessage, "remote error") && strings.Contains(stderrMessage, "not our ref"):
+ return true
+ case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "but expected"):
+ return true
+ case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "unable to resolve reference"):
+ return true
+ case strings.Contains(stderrMessage, "Unable to create") && strings.Contains(stderrMessage, ".lock"):
+ return true
+ default:
+ return false
+ }
+}
+
// runSync returns true if sync finished without error.
func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) {
repoPath := m.Repo.RepoPath()
@@ -264,7 +304,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutMessage := util.SanitizeCredentialURLs(stdout)
// Now check if the error is a resolve reference due to broken reference
- if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") {
+ if checkRecoverableSyncError(stderr) {
log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
err = nil
@@ -315,6 +355,15 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
return nil, false
}
+ if m.LFS && setting.LFS.StartServer {
+ log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
+ endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
+ lfsClient := lfs.NewClient(endpoint, nil)
+ if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
+ log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
+ }
+ }
+
log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo)
if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err)
@@ -324,15 +373,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err)
}
-
- if m.LFS && setting.LFS.StartServer {
- log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
- endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
- lfsClient := lfs.NewClient(endpoint, nil)
- if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
- log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
- }
- }
gitRepo.Close()
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
@@ -360,7 +400,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutMessage := util.SanitizeCredentialURLs(stdout)
// Now check if the error is a resolve reference due to broken reference
- if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") {
+ if checkRecoverableSyncError(stderrMessage) {
log.Warn("SyncMirrors [repo: %-v Wiki]: failed to update mirror wiki repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
err = nil
diff --git a/services/mirror/mirror_pull_test.go b/services/mirror/mirror_pull_test.go
new file mode 100644
index 0000000000..97859be5b0
--- /dev/null
+++ b/services/mirror/mirror_pull_test.go
@@ -0,0 +1,94 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mirror
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_parseRemoteUpdateOutput(t *testing.T) {
+ output := `
+ * [new tag] v0.1.8 -> v0.1.8
+ * [new branch] master -> origin/master
+ - [deleted] (none) -> origin/test1
+ - [deleted] (none) -> tag1
+ + f895a1e...957a993 test2 -> origin/test2 (forced update)
+ 957a993..a87ba5f test3 -> origin/test3
+ * [new ref] refs/pull/26595/head -> refs/pull/26595/head
+ * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge
+ e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head
+ + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update)
+`
+ results := parseRemoteUpdateOutput(output, "origin")
+ assert.Len(t, results, 10)
+ assert.Equal(t, "refs/tags/v0.1.8", results[0].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[0].oldCommitID)
+ assert.Empty(t, results[0].newCommitID)
+
+ assert.Equal(t, "refs/heads/master", results[1].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[1].oldCommitID)
+ assert.Empty(t, results[1].newCommitID)
+
+ assert.Equal(t, "refs/heads/test1", results[2].refName.String())
+ assert.Empty(t, results[2].oldCommitID)
+ assert.Equal(t, gitShortEmptySha, results[2].newCommitID)
+
+ assert.Equal(t, "refs/tags/tag1", results[3].refName.String())
+ assert.Empty(t, results[3].oldCommitID)
+ assert.Equal(t, gitShortEmptySha, results[3].newCommitID)
+
+ assert.Equal(t, "refs/heads/test2", results[4].refName.String())
+ assert.Equal(t, "f895a1e", results[4].oldCommitID)
+ assert.Equal(t, "957a993", results[4].newCommitID)
+
+ assert.Equal(t, "refs/heads/test3", results[5].refName.String())
+ assert.Equal(t, "957a993", results[5].oldCommitID)
+ assert.Equal(t, "a87ba5f", results[5].newCommitID)
+
+ assert.Equal(t, "refs/pull/26595/head", results[6].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[6].oldCommitID)
+ assert.Empty(t, results[6].newCommitID)
+
+ assert.Equal(t, "refs/pull/26595/merge", results[7].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[7].oldCommitID)
+ assert.Empty(t, results[7].newCommitID)
+
+ assert.Equal(t, "refs/pull/25873/head", results[8].refName.String())
+ assert.Equal(t, "e0639e38fb", results[8].oldCommitID)
+ assert.Equal(t, "6db2410489", results[8].newCommitID)
+
+ assert.Equal(t, "refs/pull/25873/merge", results[9].refName.String())
+ assert.Equal(t, "1c97ebc746", results[9].oldCommitID)
+ assert.Equal(t, "976d27d52f", results[9].newCommitID)
+}
+
+func Test_checkRecoverableSyncError(t *testing.T) {
+ cases := []struct {
+ recoverable bool
+ message string
+ }{
+ // A race condition in http git-fetch where certain refs were listed on the remote and are no longer there, would exit status 128
+ {true, "fatal: remote error: upload-pack: not our ref 988881adc9fc3655077dc2d4d757d480b5ea0e11"},
+ // A race condition where a local gc/prune removes a named ref during a git-fetch would exit status 1
+ {true, "cannot lock ref 'refs/pull/123456/merge': unable to resolve reference 'refs/pull/134153/merge'"},
+ // A race condition in http git-fetch where named refs were listed on the remote and are no longer there
+ {true, "error: cannot lock ref 'refs/remotes/origin/foo': unable to resolve reference 'refs/remotes/origin/foo': reference broken"},
+ // A race condition in http git-fetch where named refs were force-pushed during the update, would exit status 128
+ {true, "error: cannot lock ref 'refs/pull/123456/merge': is at 988881adc9fc3655077dc2d4d757d480b5ea0e11 but expected 7f894307ffc9553edbd0b671cab829786866f7b2"},
+ // A race condition with other local git operations, such as git-maintenance, would exit status 128 (well, "Unable" the "U" is uppercase)
+ {true, "fatal: Unable to create '/data/gitea-repositories/foo-org/bar-repo.git/./objects/info/commit-graphs/commit-graph-chain.lock': File exists."},
+ // Missing or unauthorized credentials, would exit status 128
+ {false, "fatal: Authentication failed for 'https://example.com/foo-does-not-exist/bar.git/'"},
+ // A non-existent remote repository, would exit status 128
+ {false, "fatal: Could not read from remote repository."},
+ // A non-functioning proxy, would exit status 128
+ {false, "fatal: unable to access 'https://example.com/foo-does-not-exist/bar.git/': Failed to connect to configured-https-proxy port 1080 after 0 ms: Couldn't connect to server"},
+ }
+
+ for _, c := range cases {
+ assert.Equal(t, c.recoverable, checkRecoverableSyncError(c.message), "test case: %s", c.message)
+ }
+}
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index 3a9644c3a1..11b8ad459a 100644
--- a/services/mirror/mirror_push.go
+++ b/services/mirror/mirror_push.go
@@ -13,17 +13,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
)
var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
@@ -172,7 +172,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
// OpenSSH isn't very intuitive when you want to specify a specific keypair.
// Therefore, we need to create a temporary file that stores the private key, so that OpenSSH can use it.
- // We delete the the temporary file afterwards.
+ // We delete the temporary file afterwards.
privateKeyPath := ""
if m.PublicKey != "" {
f, err := os.CreateTemp(os.TempDir(), m.RemoteName)
diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go
deleted file mode 100644
index 8ad524b608..0000000000
--- a/services/mirror/mirror_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package mirror
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func Test_parseRemoteUpdateOutput(t *testing.T) {
- output := `
- * [new tag] v0.1.8 -> v0.1.8
- * [new branch] master -> origin/master
- - [deleted] (none) -> origin/test1
- - [deleted] (none) -> tag1
- + f895a1e...957a993 test2 -> origin/test2 (forced update)
- 957a993..a87ba5f test3 -> origin/test3
-`
- results := parseRemoteUpdateOutput(output, "origin")
- assert.Len(t, results, 6)
- assert.EqualValues(t, "refs/tags/v0.1.8", results[0].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[0].oldCommitID)
- assert.EqualValues(t, "", results[0].newCommitID)
-
- assert.EqualValues(t, "refs/heads/master", results[1].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[1].oldCommitID)
- assert.EqualValues(t, "", results[1].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test1", results[2].refName.String())
- assert.EqualValues(t, "", results[2].oldCommitID)
- assert.EqualValues(t, gitShortEmptySha, results[2].newCommitID)
-
- assert.EqualValues(t, "refs/tags/tag1", results[3].refName.String())
- assert.EqualValues(t, "", results[3].oldCommitID)
- assert.EqualValues(t, gitShortEmptySha, results[3].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test2", results[4].refName.String())
- assert.EqualValues(t, "f895a1e", results[4].oldCommitID)
- assert.EqualValues(t, "957a993", results[4].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test3", results[5].refName.String())
- assert.EqualValues(t, "957a993", results[5].oldCommitID)
- assert.EqualValues(t, "a87ba5f", results[5].newCommitID)
-}
diff --git a/services/mirror/notifier.go b/services/mirror/notifier.go
index 93d904470d..8f8552f419 100644
--- a/services/mirror/notifier.go
+++ b/services/mirror/notifier.go
@@ -6,10 +6,10 @@ package mirror
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/repository"
- notify_service "code.gitea.io/gitea/services/notify"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/repository"
+ notify_service "forgejo.org/services/notify"
)
func init() {
diff --git a/services/mirror/queue.go b/services/mirror/queue.go
index 0d9a624730..b4869cf8c0 100644
--- a/services/mirror/queue.go
+++ b/services/mirror/queue.go
@@ -4,10 +4,10 @@
package mirror
import (
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
)
var mirrorQueue *queue.WorkerPoolQueue[*SyncRequest]
diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go
new file mode 100644
index 0000000000..e01156dc11
--- /dev/null
+++ b/services/moderation/reporting.go
@@ -0,0 +1,129 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ "errors"
+
+ "forgejo.org/models/issues"
+ "forgejo.org/models/moderation"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
+)
+
+var (
+ ErrContentDoesNotExist = errors.New("the content to be reported does not exist")
+ ErrDoerNotAllowed = errors.New("doer not allowed to access the content to be reported")
+)
+
+// CanReport checks if doer has access to the content they are reporting
+// (user, organization, repository, issue, pull request or comment).
+// When reporting repositories the user should have at least read access to any repo unit type.
+// When reporting issues, pull requests or comments the user should have at least read access
+// to 'TypeIssues', respectively 'TypePullRequests' unit for the repository where the content belongs.
+// When reporting users or organizations doer should be able to view the reported entity.
+func CanReport(ctx context.Context, doer *user.User, contentType moderation.ReportedContentType, contentID int64) (bool, error) {
+ hasAccess := false
+ var issueID int64
+ var repoID int64
+ unitType := unit.TypeInvalid // used when checking access for issues, pull requests or comments
+
+ if contentType == moderation.ReportedContentTypeUser {
+ reportedUser, err := user.GetUserByID(ctx, contentID)
+ if err != nil {
+ if user.IsErrUserNotExist(err) {
+ log.Warn("User #%d wanted to report user #%d but it does not exist.", doer.ID, contentID)
+ return false, ErrContentDoesNotExist
+ }
+ return false, err
+ }
+
+ hasAccess = user.IsUserVisibleToViewer(ctx, reportedUser, ctx.Doer)
+ if !hasAccess {
+ log.Warn("User #%d wanted to report user/org #%d but they are not able to see that profile.", doer.ID, contentID)
+ return false, ErrDoerNotAllowed
+ }
+ } else {
+ // for comments and issues/pulls we need to get the parent repository
+ switch contentType {
+ case moderation.ReportedContentTypeComment:
+ comment, err := issues.GetCommentByID(ctx, contentID)
+ if err != nil {
+ if issues.IsErrCommentNotExist(err) {
+ log.Warn("User #%d wanted to report comment #%d but it does not exist.", doer.ID, contentID)
+ return false, ErrContentDoesNotExist
+ }
+ return false, err
+ }
+ if !comment.Type.HasContentSupport() {
+ // this is not a comment with text and/or attachments
+ log.Warn("User #%d wanted to report comment #%d but it is not a comment with content.", doer.ID, contentID)
+ return false, nil
+ }
+ issueID = comment.IssueID
+ case moderation.ReportedContentTypeIssue:
+ issueID = contentID
+ case moderation.ReportedContentTypeRepository:
+ repoID = contentID
+ }
+
+ if issueID > 0 {
+ issue, err := issues.GetIssueByID(ctx, issueID)
+ if err != nil {
+ if issues.IsErrIssueNotExist(err) {
+ log.Warn("User #%d wanted to report issue #%d (or one of its comments) but it does not exist.", doer.ID, issueID)
+ return false, ErrContentDoesNotExist
+ }
+ return false, err
+ }
+
+ repoID = issue.RepoID
+ if issue.IsPull {
+ unitType = unit.TypePullRequests
+ } else {
+ unitType = unit.TypeIssues
+ }
+ }
+
+ if repoID > 0 {
+ repo, err := repo_model.GetRepositoryByID(ctx, repoID)
+ if err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ log.Warn("User #%d wanted to report repository #%d (or one of its issues / comments) but it does not exist.", doer.ID, repoID)
+ return false, ErrContentDoesNotExist
+ }
+ return false, err
+ }
+
+ if issueID > 0 {
+ // for comments and issues/pulls doer should have at least read access to the corresponding repo unit (issues, respectively pull requests)
+ hasAccess, err = access_model.HasAccessUnit(ctx, doer, repo, unitType, perm.AccessModeRead)
+ if err != nil {
+ return false, err
+ } else if !hasAccess {
+ log.Warn("User #%d wanted to report issue #%d or one of its comments from repository #%d but they don't have access to it.", doer.ID, issueID, repoID)
+ return false, ErrDoerNotAllowed
+ }
+ } else {
+ // for repositories doer should have at least read access to at least one repo unit
+ perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return false, err
+ }
+ hasAccess = perm.CanReadAny(unit.AllRepoUnitTypes...)
+ if !hasAccess {
+ log.Warn("User #%d wanted to report repository #%d but they don't have access to it.", doer.ID, repoID)
+ return false, ErrDoerNotAllowed
+ }
+ }
+ }
+ }
+
+ return hasAccess, nil
+}
diff --git a/services/notify/notifier.go b/services/notify/notifier.go
index 3230a5e5f5..4d88a7ab95 100644
--- a/services/notify/notifier.go
+++ b/services/notify/notifier.go
@@ -6,12 +6,13 @@ package notify
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/repository"
+ actions_model "forgejo.org/models/actions"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/repository"
)
// Notifier defines an interface to notify receiver
@@ -76,4 +77,6 @@ type Notifier interface {
PackageDelete(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor)
ChangeDefaultBranch(ctx context.Context, repo *repo_model.Repository)
+
+ ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun)
}
diff --git a/services/notify/notify.go b/services/notify/notify.go
index 5ed63646aa..02c18272cb 100644
--- a/services/notify/notify.go
+++ b/services/notify/notify.go
@@ -6,13 +6,14 @@ package notify
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
+ actions_model "forgejo.org/models/actions"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
)
var notifiers []Notifier
@@ -374,3 +375,15 @@ func ChangeDefaultBranch(ctx context.Context, repo *repo_model.Repository) {
notifier.ChangeDefaultBranch(ctx, repo)
}
}
+
+// ActionRunNowDone notifies that the old status priorStatus with (priorStatus.isDone() == false) of an ActionRun changed to run.Status with (run.Status.isDone() == true)
+// run represents the new state of the ActionRun.
+// lastRun represents the ActionRun of the same workflow that finished before run.
+// lastRun might be nil (e.g. when the run is the first for this workflow). It is the last run of the same workflow for the same repo.
+// It can be used to figure out if a successful run follows a failed one.
+// Both run and lastRun need their attributes loaded.
+func ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) {
+ for _, notifier := range notifiers {
+ notifier.ActionRunNowDone(ctx, run, priorStatus, lastRun)
+ }
+}
diff --git a/services/notify/null.go b/services/notify/null.go
index 894d118eac..9c76e5cbd3 100644
--- a/services/notify/null.go
+++ b/services/notify/null.go
@@ -6,12 +6,13 @@ package notify
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/repository"
+ actions_model "forgejo.org/models/actions"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/repository"
)
// NullNotifier implements a blank notifier
@@ -211,3 +212,7 @@ func (*NullNotifier) PackageDelete(ctx context.Context, doer *user_model.User, p
// ChangeDefaultBranch places a place holder function
func (*NullNotifier) ChangeDefaultBranch(ctx context.Context, repo *repo_model.Repository) {
}
+
+// ActionRunNowDone places a place holder function
+func (*NullNotifier) ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) {
+}
diff --git a/services/org/org.go b/services/org/org.go
index dca7794b47..b1bbe43046 100644
--- a/services/org/org.go
+++ b/services/org/org.go
@@ -7,15 +7,15 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- org_model "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ org_model "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ repo_service "forgejo.org/services/repository"
)
// DeleteOrganization completely and permanently deletes everything of organization.
diff --git a/services/org/org_test.go b/services/org/org_test.go
index 07358438f6..b0f591c745 100644
--- a/services/org/org_test.go
+++ b/services/org/org_test.go
@@ -6,11 +6,11 @@ package org
import (
"testing"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/org/repo.go b/services/org/repo.go
index 78a829ef25..33f55b1191 100644
--- a/services/org/repo.go
+++ b/services/org/repo.go
@@ -7,10 +7,10 @@ import (
"context"
"errors"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
)
// TeamAddRepository adds new repository to team of organization.
diff --git a/services/org/repo_test.go b/services/org/repo_test.go
index 2ddb8f9045..c51cbf4c28 100644
--- a/services/org/repo_test.go
+++ b/services/org/repo_test.go
@@ -6,10 +6,10 @@ package org
import (
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/require"
)
diff --git a/services/org/team_invite.go b/services/org/team_invite.go
index 3f28044dbf..9c5da25522 100644
--- a/services/org/team_invite.go
+++ b/services/org/team_invite.go
@@ -6,9 +6,9 @@ package org
import (
"context"
- org_model "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/services/mailer"
+ org_model "forgejo.org/models/organization"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/services/mailer"
)
// CreateTeamInvite make a persistent invite in db and mail it
diff --git a/services/packages/alpine/repository.go b/services/packages/alpine/repository.go
index 92f475bb7b..dd66c7d74e 100644
--- a/services/packages/alpine/repository.go
+++ b/services/packages/alpine/repository.go
@@ -20,14 +20,14 @@ import (
"io"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- alpine_model "code.gitea.io/gitea/models/packages/alpine"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- packages_module "code.gitea.io/gitea/modules/packages"
- alpine_module "code.gitea.io/gitea/modules/packages/alpine"
- "code.gitea.io/gitea/modules/util"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ alpine_model "forgejo.org/models/packages/alpine"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ packages_module "forgejo.org/modules/packages"
+ alpine_module "forgejo.org/modules/packages/alpine"
+ "forgejo.org/modules/util"
+ packages_service "forgejo.org/services/packages"
)
const (
@@ -258,7 +258,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
privPem, _ := pem.Decode([]byte(priv))
if privPem == nil {
- return fmt.Errorf("failed to decode private key pem")
+ return errors.New("failed to decode private key pem")
}
privKey, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
diff --git a/services/packages/alt/repository.go b/services/packages/alt/repository.go
new file mode 100644
index 0000000000..317862da9d
--- /dev/null
+++ b/services/packages/alt/repository.go
@@ -0,0 +1,937 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package alt
+
+import (
+ "bytes"
+ "context"
+ "crypto/sha256"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "path"
+ "time"
+
+ packages_model "forgejo.org/models/packages"
+ alt_model "forgejo.org/models/packages/alt"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/json"
+ packages_module "forgejo.org/modules/packages"
+ rpm_module "forgejo.org/modules/packages/rpm"
+ "forgejo.org/modules/setting"
+ packages_service "forgejo.org/services/packages"
+
+ "github.com/ulikunitz/xz"
+)
+
+// GetOrCreateRepositoryVersion gets or creates the internal repository package
+// The RPM registry needs multiple metadata files which are stored in this package.
+func GetOrCreateRepositoryVersion(ctx context.Context, ownerID int64) (*packages_model.PackageVersion, error) {
+ return packages_service.GetOrCreateInternalPackageVersion(ctx, ownerID, packages_model.TypeAlt, rpm_module.RepositoryPackage, rpm_module.RepositoryVersion)
+}
+
+// BuildAllRepositoryFiles (re)builds all repository files for every available group
+func BuildAllRepositoryFiles(ctx context.Context, ownerID int64) error {
+ pv, err := GetOrCreateRepositoryVersion(ctx, ownerID)
+ if err != nil {
+ return err
+ }
+
+ // 1. Delete all existing repository files
+ pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
+ if err != nil {
+ return err
+ }
+
+ for _, pf := range pfs {
+ if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
+ return err
+ }
+ }
+
+ // 2. (Re)Build repository files for existing packages
+ groups, err := alt_model.GetGroups(ctx, ownerID)
+ if err != nil {
+ return err
+ }
+ for _, group := range groups {
+ if err := BuildSpecificRepositoryFiles(ctx, ownerID, group); err != nil {
+ return fmt.Errorf("failed to build repository files [%s]: %w", group, err)
+ }
+ }
+
+ return nil
+}
+
+type repoChecksum struct {
+ Value string `xml:",chardata"`
+ Type string `xml:"type,attr"`
+}
+
+type repoLocation struct {
+ Href string `xml:"href,attr"`
+}
+
+type repoData struct {
+ Type string `xml:"type,attr"`
+ Checksum repoChecksum `xml:"checksum"`
+ MD5Checksum repoChecksum `xml:"md5checksum"`
+ Blake2bHash repoChecksum `xml:"blake2bHash"`
+ OpenChecksum repoChecksum `xml:"open-checksum"`
+ Location repoLocation `xml:"location"`
+ Timestamp int64 `xml:"timestamp"`
+ Size int64 `xml:"size"`
+ OpenSize int64 `xml:"open-size"`
+}
+
+type packageData struct {
+ Package *packages_model.Package
+ Version *packages_model.PackageVersion
+ Blob *packages_model.PackageBlob
+ VersionMetadata *rpm_module.VersionMetadata
+ FileMetadata *rpm_module.FileMetadata
+}
+
+type packageCache = map[*packages_model.PackageFile]*packageData
+
+// BuildSpecificRepositoryFiles builds metadata files for the repository
+func BuildSpecificRepositoryFiles(ctx context.Context, ownerID int64, group string) error {
+ pv, err := GetOrCreateRepositoryVersion(ctx, ownerID)
+ if err != nil {
+ return err
+ }
+
+ pfs, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{
+ OwnerID: ownerID,
+ PackageType: packages_model.TypeAlt,
+ Query: "%.rpm",
+ CompositeKey: group,
+ })
+ if err != nil {
+ return err
+ }
+
+ // Delete the repository files if there are no packages
+ if len(pfs) == 0 {
+ pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
+ if err != nil {
+ return err
+ }
+ for _, pf := range pfs {
+ if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+
+ // Cache data needed for all repository files
+ cache := make(packageCache)
+ for _, pf := range pfs {
+ pv, err := packages_model.GetVersionByID(ctx, pf.VersionID)
+ if err != nil {
+ return err
+ }
+ p, err := packages_model.GetPackageByID(ctx, pv.PackageID)
+ if err != nil {
+ return err
+ }
+ pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
+ if err != nil {
+ return err
+ }
+ pps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, pf.ID, rpm_module.PropertyMetadata)
+ if err != nil {
+ return err
+ }
+
+ pd := &packageData{
+ Package: p,
+ Version: pv,
+ Blob: pb,
+ }
+
+ if err := json.Unmarshal([]byte(pv.MetadataJSON), &pd.VersionMetadata); err != nil {
+ return err
+ }
+ if len(pps) > 0 {
+ if err := json.Unmarshal([]byte(pps[0].Value), &pd.FileMetadata); err != nil {
+ return err
+ }
+ }
+
+ cache[pf] = pd
+ }
+
+ pkglist, err := buildPackageLists(ctx, pv, pfs, cache, group)
+ if err != nil {
+ return err
+ }
+
+ err = buildRelease(ctx, pv, pfs, cache, group, pkglist)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type RPMHeader struct {
+ Magic [4]byte
+ Reserved [4]byte
+ NIndex uint32
+ HSize uint32
+}
+
+type RPMHdrIndex struct {
+ Tag uint32
+ Type uint32
+ Offset uint32
+ Count uint32
+}
+
+type indexWithData struct {
+ index *RPMHdrIndex
+ data []any
+}
+
+type headerWithIndexes struct {
+ header *RPMHeader
+ indexes []indexWithData
+}
+
+// https://refspecs.linuxbase.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html
+func buildPackageLists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (map[string][]*repoData, error) {
+ packagesByArch := map[string][]*packages_model.PackageFile{}
+
+ for _, pf := range pfs {
+ pd := c[pf]
+
+ packageArch := pd.FileMetadata.Architecture
+ if packages, ok := packagesByArch[packageArch]; ok {
+ packagesByArch[packageArch] = append(packages, pf)
+ } else {
+ packagesByArch[packageArch] = []*packages_model.PackageFile{pf}
+ }
+ }
+
+ repoDataListByArch := make(map[string][]*repoData)
+
+ for architecture, pfs := range packagesByArch {
+ repoDataList := []*repoData{}
+ orderedHeaders := []headerWithIndexes{}
+
+ for _, pf := range pfs {
+ pd := c[pf]
+
+ var requireNames []any
+ var requireVersions []any
+ var requireFlags []any
+ requireNamesSize := 0
+ requireVersionsSize := 0
+ requireFlagsSize := 0
+
+ for _, entry := range pd.FileMetadata.Requires {
+ if entry != nil {
+ requireNames = append(requireNames, entry.Name)
+ requireVersions = append(requireVersions, entry.Version)
+ requireFlags = append(requireFlags, entry.AltFlags)
+ requireNamesSize += len(entry.Name) + 1
+ requireVersionsSize += len(entry.Version) + 1
+ requireFlagsSize += 4
+ }
+ }
+
+ var conflictNames []any
+ var conflictVersions []any
+ var conflictFlags []any
+ conflictNamesSize := 0
+ conflictVersionsSize := 0
+ conflictFlagsSize := 0
+
+ for _, entry := range pd.FileMetadata.Conflicts {
+ if entry != nil {
+ conflictNames = append(conflictNames, entry.Name)
+ conflictVersions = append(conflictVersions, entry.Version)
+ conflictFlags = append(conflictFlags, entry.AltFlags)
+ conflictNamesSize += len(entry.Name) + 1
+ conflictVersionsSize += len(entry.Version) + 1
+ conflictFlagsSize += 4
+ }
+ }
+
+ var baseNames []any
+ var dirNames []any
+ baseNamesSize := 0
+ dirNamesSize := 0
+
+ for _, entry := range pd.FileMetadata.Files {
+ if entry != nil {
+ dir, file := path.Split(entry.Path)
+
+ baseNames = append(baseNames, file)
+ dirNames = append(dirNames, dir)
+ baseNamesSize += len(file) + 1
+ dirNamesSize += len(dir) + 1
+ }
+ }
+
+ var provideNames []any
+ var provideVersions []any
+ var provideFlags []any
+ provideNamesSize := 0
+ provideVersionsSize := 0
+ provideFlagsSize := 0
+
+ for _, entry := range pd.FileMetadata.Provides {
+ if entry != nil {
+ provideNames = append(provideNames, entry.Name)
+ provideVersions = append(provideVersions, entry.Version)
+ provideFlags = append(provideFlags, entry.AltFlags)
+ provideNamesSize += len(entry.Name) + 1
+ provideVersionsSize += len(entry.Version) + 1
+ provideFlagsSize += 4
+ }
+ }
+
+ var obsoleteNames []any
+ var obsoleteVersions []any
+ var obsoleteFlags []any
+ obsoleteNamesSize := 0
+ obsoleteVersionsSize := 0
+ obsoleteFlagsSize := 0
+
+ for _, entry := range pd.FileMetadata.Obsoletes {
+ if entry != nil {
+ obsoleteNames = append(obsoleteNames, entry.Name)
+ obsoleteVersions = append(obsoleteVersions, entry.Version)
+ obsoleteFlags = append(obsoleteFlags, entry.AltFlags)
+ obsoleteNamesSize += len(entry.Name) + 1
+ obsoleteVersionsSize += len(entry.Version) + 1
+ obsoleteFlagsSize += 4
+ }
+ }
+
+ var changeLogTimes []any
+ var changeLogNames []any
+ var changeLogTexts []any
+ changeLogTimesSize := 0
+ changeLogNamesSize := 0
+ changeLogTextsSize := 0
+
+ for _, entry := range pd.FileMetadata.Changelogs {
+ if entry != nil {
+ changeLogNames = append(changeLogNames, entry.Author)
+ changeLogTexts = append(changeLogTexts, entry.Text)
+ changeLogTimes = append(changeLogTimes, uint32(int64(entry.Date)))
+ changeLogNamesSize += len(entry.Author) + 1
+ changeLogTextsSize += len(entry.Text) + 1
+ changeLogTimesSize += 4
+ }
+ }
+
+ /*Header*/
+ hdr := &RPMHeader{
+ Magic: [4]byte{0x8E, 0xAD, 0xE8, 0x01},
+ Reserved: [4]byte{0, 0, 0, 0},
+ NIndex: binary.BigEndian.Uint32([]byte{0, 0, 0, 0}),
+ HSize: binary.BigEndian.Uint32([]byte{0, 0, 0, 0}),
+ }
+ orderedHeader := headerWithIndexes{hdr, []indexWithData{}}
+
+ /*Tags: */
+ nameInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 232}),
+ Type: 6,
+ Offset: 0,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &nameInd,
+ data: []any{pd.Package.Name},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.Package.Name) + 1)
+
+ // Индекс для версии пакета
+ versionInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 233}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &versionInd,
+ data: []any{pd.FileMetadata.Version},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.FileMetadata.Version) + 1)
+
+ summaryInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 236}),
+ Type: 9,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &summaryInd,
+ data: []any{pd.VersionMetadata.Summary},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.VersionMetadata.Summary) + 1)
+
+ descriptionInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 237}),
+ Type: 9,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &descriptionInd,
+ data: []any{pd.VersionMetadata.Description},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.VersionMetadata.Description) + 1)
+
+ releaseInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 234}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &releaseInd,
+ data: []any{pd.FileMetadata.Release},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.FileMetadata.Release) + 1)
+
+ alignPadding(hdr, orderedHeader.indexes)
+
+ sizeInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 241}),
+ Type: 4,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &sizeInd,
+ data: []any{int32(pd.FileMetadata.InstalledSize)},
+ })
+ hdr.NIndex++
+ hdr.HSize += 4
+
+ buildTimeInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 238}),
+ Type: 4,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &buildTimeInd,
+ data: []any{int32(pd.FileMetadata.BuildTime)},
+ })
+ hdr.NIndex++
+ hdr.HSize += 4
+
+ licenseInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 246}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &licenseInd,
+ data: []any{pd.VersionMetadata.License},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.VersionMetadata.License) + 1)
+
+ packagerInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 247}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &packagerInd,
+ data: []any{pd.FileMetadata.Packager},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.FileMetadata.Packager) + 1)
+
+ groupInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 248}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &groupInd,
+ data: []any{pd.FileMetadata.Group},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.FileMetadata.Group) + 1)
+
+ urlInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 252}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &urlInd,
+ data: []any{pd.VersionMetadata.ProjectURL},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.VersionMetadata.ProjectURL) + 1)
+
+ if len(changeLogNames) != 0 && len(changeLogTexts) != 0 && len(changeLogTimes) != 0 {
+ alignPadding(hdr, orderedHeader.indexes)
+
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x38}, 4, changeLogTimes, changeLogTimesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x39}, 8, changeLogNames, changeLogNamesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x3A}, 8, changeLogTexts, changeLogTextsSize)
+ }
+
+ archInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0, 0, 3, 254}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &archInd,
+ data: []any{pd.FileMetadata.Architecture},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.FileMetadata.Architecture) + 1)
+
+ if len(provideNames) != 0 && len(provideVersions) != 0 && len(provideFlags) != 0 {
+ alignPadding(hdr, orderedHeader.indexes)
+
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x58}, 4, provideFlags, provideFlagsSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x17}, 8, provideNames, provideNamesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x59}, 8, provideVersions, provideVersionsSize)
+ }
+
+ sourceRpmInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x04, 0x14}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &sourceRpmInd,
+ data: []any{pd.FileMetadata.SourceRpm},
+ })
+ hdr.NIndex++
+ hdr.HSize += binary.BigEndian.Uint32([]byte{0, 0, 0, uint8(len(pd.FileMetadata.SourceRpm) + 1)})
+
+ if len(requireNames) != 0 && len(requireVersions) != 0 && len(requireFlags) != 0 {
+ alignPadding(hdr, orderedHeader.indexes)
+
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x18}, 4, requireFlags, requireFlagsSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0, 0, 4, 25}, 8, requireNames, requireNamesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x1A}, 8, requireVersions, requireVersionsSize)
+ }
+
+ if len(baseNames) != 0 {
+ baseNamesInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x04, 0x5D}),
+ Type: 8,
+ Offset: hdr.HSize,
+ Count: uint32(len(baseNames)),
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &baseNamesInd,
+ data: baseNames,
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(baseNamesSize)
+ }
+
+ if len(dirNames) != 0 {
+ dirnamesInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x04, 0x5E}),
+ Type: 8,
+ Offset: hdr.HSize,
+ Count: uint32(len(dirNames)),
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &dirnamesInd,
+ data: dirNames,
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(dirNamesSize)
+ }
+
+ filenameInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x0F, 0x42, 0x40}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &filenameInd,
+ data: []any{pf.Name},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pf.Name) + 1)
+
+ alignPadding(hdr, orderedHeader.indexes)
+
+ filesizeInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x0F, 0x42, 0x41}),
+ Type: 4,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &filesizeInd,
+ data: []any{int32(pd.Blob.Size)},
+ })
+ hdr.NIndex++
+ hdr.HSize += 4
+
+ md5Ind := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x0F, 0x42, 0x45}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &md5Ind,
+ data: []any{pd.Blob.HashMD5},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.Blob.HashMD5) + 1)
+
+ blake2bInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x0F, 0x42, 0x49}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &blake2bInd,
+ data: []any{pd.Blob.HashBlake2b},
+ })
+ hdr.NIndex++
+ hdr.HSize += uint32(len(pd.Blob.HashBlake2b) + 1)
+
+ if len(conflictNames) != 0 && len(conflictVersions) != 0 && len(conflictFlags) != 0 {
+ alignPadding(hdr, orderedHeader.indexes)
+
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x1D}, 4, conflictFlags, conflictFlagsSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x1E}, 8, conflictNames, conflictNamesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x1F}, 8, conflictVersions, conflictVersionsSize)
+ }
+
+ directoryInd := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32([]byte{0x00, 0x0F, 0x42, 0x4A}),
+ Type: 6,
+ Offset: hdr.HSize,
+ Count: 1,
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &directoryInd,
+ data: []any{"RPMS.classic"},
+ })
+ hdr.NIndex++
+ hdr.HSize += binary.BigEndian.Uint32([]byte{0, 0, 0, uint8(len("RPMS.classic") + 1)})
+
+ if len(obsoleteNames) != 0 && len(obsoleteVersions) != 0 && len(obsoleteFlags) != 0 {
+ alignPadding(hdr, orderedHeader.indexes)
+
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x5A}, 4, obsoleteFlags, obsoleteFlagsSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x42}, 8, obsoleteNames, obsoleteNamesSize)
+ addRPMHdrIndex(&orderedHeader, []byte{0x00, 0x00, 0x04, 0x5B}, 8, obsoleteVersions, obsoleteVersionsSize)
+ }
+
+ orderedHeaders = append(orderedHeaders, orderedHeader)
+ }
+
+ files := []string{"pkglist.classic", "pkglist.classic.xz"}
+ for file := range files {
+ fileInfo, err := addPkglistAsFileToRepo(ctx, pv, files[file], orderedHeaders, group, architecture)
+ if err != nil {
+ return nil, err
+ }
+ repoDataList = append(repoDataList, fileInfo)
+ }
+ repoDataListByArch[architecture] = repoDataList
+ }
+ return repoDataListByArch, nil
+}
+
+func alignPadding(hdr *RPMHeader, indexes []indexWithData) {
+ /* Align to 4-bytes to add a 4-byte element. */
+ padding := (4 - (hdr.HSize % 4)) % 4
+ if padding == 4 {
+ padding = 0
+ }
+ hdr.HSize += binary.BigEndian.Uint32([]byte{0, 0, 0, uint8(padding)})
+
+ lastIndex := len(indexes) - 1
+ for i := uint32(0); i < padding; i++ {
+ for _, elem := range indexes[lastIndex].data {
+ if str, ok := elem.(string); ok {
+ indexes[lastIndex].data[len(indexes[lastIndex].data)-1] = str + "\x00"
+ }
+ }
+ }
+}
+
+func addRPMHdrIndex(orderedHeader *headerWithIndexes, tag []byte, typeVal uint32, data []any, dataSize int) {
+ index := RPMHdrIndex{
+ Tag: binary.BigEndian.Uint32(tag),
+ Type: typeVal,
+ Offset: orderedHeader.header.HSize,
+ Count: uint32(len(data)),
+ }
+ orderedHeader.indexes = append(orderedHeader.indexes, indexWithData{
+ index: &index,
+ data: data,
+ })
+ orderedHeader.header.NIndex++
+ orderedHeader.header.HSize += uint32(dataSize)
+}
+
+// https://www.altlinux.org/APT_в_ALT_Linux/CreateRepository
+func buildRelease(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string, pkglist map[string][]*repoData) error {
+ architectures := make(container.Set[string])
+
+ for _, pf := range pfs {
+ pd := c[pf]
+ architectures.Add(pd.FileMetadata.Architecture)
+ }
+
+ for architecture := range architectures.Seq() {
+ version := time.Now().Unix()
+ label := setting.AppName
+ data := fmt.Sprintf(`Archive: Alt Linux Team
+Component: classic
+Version: %d
+Origin: Alt Linux Team
+Label: %s
+Architecture: %s
+NotAutomatic: false
+`,
+ version, label, architecture)
+ fileInfo, err := addReleaseAsFileToRepo(ctx, pv, "release.classic", data, group, architecture)
+ if err != nil {
+ return err
+ }
+
+ origin := setting.AppName
+ codename := time.Now().Unix()
+ date := time.Now().UTC().Format(time.RFC1123)
+
+ var md5Sum string
+ var blake2b string
+
+ for _, pkglistByArch := range pkglist[architecture] {
+ md5Sum += fmt.Sprintf(" %s %d %s\n", pkglistByArch.MD5Checksum.Value, pkglistByArch.Size, "base/"+pkglistByArch.Type)
+ blake2b += fmt.Sprintf(" %s %d %s\n", pkglistByArch.Blake2bHash.Value, pkglistByArch.Size, "base/"+pkglistByArch.Type)
+ }
+ md5Sum += fmt.Sprintf(" %s %d %s\n", fileInfo.MD5Checksum.Value, fileInfo.Size, "base/"+fileInfo.Type)
+ blake2b += fmt.Sprintf(" %s %d %s\n", fileInfo.Blake2bHash.Value, fileInfo.Size, "base/"+fileInfo.Type)
+
+ data = fmt.Sprintf(`Origin: %s
+Label: %s
+Suite: Sisyphus
+Codename: %d
+Date: %s
+Architectures: %s
+MD5Sum:
+%sBLAKE2b:
+%s
+
+`,
+ origin, label, codename, date, architecture, md5Sum, blake2b)
+ _, err = addReleaseAsFileToRepo(ctx, pv, "release", data, group, architecture)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addReleaseAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filename, obj, group, arch string) (*repoData, error) {
+ content, _ := packages_module.NewHashedBuffer()
+ defer content.Close()
+
+ h := sha256.New()
+
+ w := io.MultiWriter(content, h)
+ if _, err := w.Write([]byte(obj)); err != nil {
+ return nil, err
+ }
+
+ _, err := packages_service.AddFileToPackageVersionInternal(
+ ctx,
+ pv,
+ &packages_service.PackageFileCreationInfo{
+ PackageFileInfo: packages_service.PackageFileInfo{
+ Filename: filename,
+ CompositeKey: arch + "__" + group,
+ },
+ Creator: user_model.NewGhostUser(),
+ Data: content,
+ IsLead: false,
+ OverwriteExisting: true,
+ },
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ hashMD5, _, hashSHA256, _, hashBlake2b := content.Sums()
+
+ if group == "" {
+ group = "alt"
+ }
+
+ return &repoData{
+ Type: filename,
+ Checksum: repoChecksum{
+ Type: "sha256",
+ Value: hex.EncodeToString(hashSHA256),
+ },
+ MD5Checksum: repoChecksum{
+ Type: "md5",
+ Value: hex.EncodeToString(hashMD5),
+ },
+ OpenChecksum: repoChecksum{
+ Type: "sha256",
+ Value: hex.EncodeToString(h.Sum(nil)),
+ },
+ Blake2bHash: repoChecksum{
+ Type: "blake2b",
+ Value: hex.EncodeToString(hashBlake2b),
+ },
+ Location: repoLocation{
+ Href: group + ".repo/" + arch + "/base/" + filename,
+ },
+ Size: content.Size(),
+ }, nil
+}
+
+func addPkglistAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filename string, orderedHeaders []headerWithIndexes, group, arch string) (*repoData, error) {
+ content, _ := packages_module.NewHashedBuffer()
+ defer content.Close()
+
+ h := sha256.New()
+ w := io.MultiWriter(content, h)
+ buf := &bytes.Buffer{}
+
+ for _, hdr := range orderedHeaders {
+ if err := binary.Write(buf, binary.BigEndian, *hdr.header); err != nil {
+ return nil, err
+ }
+
+ for _, index := range hdr.indexes {
+ if err := binary.Write(buf, binary.BigEndian, *index.index); err != nil {
+ return nil, err
+ }
+ }
+
+ for _, index := range hdr.indexes {
+ for _, indexValue := range index.data {
+ switch v := indexValue.(type) {
+ case string:
+ if _, err := buf.WriteString(v + "\x00"); err != nil {
+ return nil, err
+ }
+ case int, int32, int64, uint32:
+ if err := binary.Write(buf, binary.BigEndian, v); err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+ }
+
+ if path.Ext(filename) == ".xz" {
+ xzContent, err := compressXZ(buf.Bytes())
+ if err != nil {
+ return nil, err
+ }
+ if _, err := w.Write(xzContent); err != nil {
+ return nil, err
+ }
+ } else {
+ if _, err := w.Write(buf.Bytes()); err != nil {
+ return nil, err
+ }
+ }
+
+ _, err := packages_service.AddFileToPackageVersionInternal(
+ ctx,
+ pv,
+ &packages_service.PackageFileCreationInfo{
+ PackageFileInfo: packages_service.PackageFileInfo{
+ Filename: filename,
+ CompositeKey: arch + "__" + group,
+ },
+ Creator: user_model.NewGhostUser(),
+ Data: content,
+ IsLead: false,
+ OverwriteExisting: true,
+ },
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ hashMD5, _, hashSHA256, _, hashBlake2b := content.Sums()
+
+ if group == "" {
+ group = "alt"
+ }
+
+ return &repoData{
+ Type: filename,
+ Checksum: repoChecksum{
+ Type: "sha256",
+ Value: hex.EncodeToString(hashSHA256),
+ },
+ MD5Checksum: repoChecksum{
+ Type: "md5",
+ Value: hex.EncodeToString(hashMD5),
+ },
+ OpenChecksum: repoChecksum{
+ Type: "sha256",
+ Value: hex.EncodeToString(h.Sum(nil)),
+ },
+ Blake2bHash: repoChecksum{
+ Type: "blake2b",
+ Value: hex.EncodeToString(hashBlake2b),
+ },
+ Location: repoLocation{
+ Href: group + ".repo/" + arch + "/base/" + filename,
+ },
+ Size: content.Size(),
+ }, nil
+}
+
+func compressXZ(data []byte) ([]byte, error) {
+ var xzContent bytes.Buffer
+ xzWriter, err := xz.NewWriter(&xzContent)
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = xzWriter.Write(data)
+ xzWriter.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ return xzContent.Bytes(), nil
+}
diff --git a/services/packages/arch/repository.go b/services/packages/arch/repository.go
index e681f24561..2a865e6dbd 100644
--- a/services/packages/arch/repository.go
+++ b/services/packages/arch/repository.go
@@ -16,14 +16,14 @@ import (
"sort"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- user_model "code.gitea.io/gitea/models/user"
- packages_module "code.gitea.io/gitea/modules/packages"
- arch_module "code.gitea.io/gitea/modules/packages/arch"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/sync"
- "code.gitea.io/gitea/modules/util"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ user_model "forgejo.org/models/user"
+ packages_module "forgejo.org/modules/packages"
+ arch_module "forgejo.org/modules/packages/arch"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/sync"
+ "forgejo.org/modules/util"
+ packages_service "forgejo.org/services/packages"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
diff --git a/services/packages/auth.go b/services/packages/auth.go
index c5bf5af532..205125cf8b 100644
--- a/services/packages/auth.go
+++ b/services/packages/auth.go
@@ -4,15 +4,16 @@
package packages
import (
+ "errors"
"fmt"
"net/http"
"strings"
"time"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
"github.com/golang-jwt/jwt/v5"
)
@@ -53,7 +54,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, auth_model.AccessTokenSc
parts := strings.SplitN(h, " ", 2)
if len(parts) != 2 {
log.Error("split token failed: %s", h)
- return 0, "", fmt.Errorf("split token failed")
+ return 0, "", errors.New("split token failed")
}
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) {
@@ -68,7 +69,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, auth_model.AccessTokenSc
c, ok := token.Claims.(*packageClaims)
if !token.Valid || !ok {
- return 0, "", fmt.Errorf("invalid token claim")
+ return 0, "", errors.New("invalid token claim")
}
return c.UserID, c.Scope, nil
diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go
index 59823cd3de..9afcd79571 100644
--- a/services/packages/cargo/index.go
+++ b/services/packages/cargo/index.go
@@ -13,17 +13,17 @@ import (
"strconv"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- cargo_module "code.gitea.io/gitea/modules/packages/cargo"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- repo_service "code.gitea.io/gitea/services/repository"
- files_service "code.gitea.io/gitea/services/repository/files"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ cargo_module "forgejo.org/modules/packages/cargo"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ repo_service "forgejo.org/services/repository"
+ files_service "forgejo.org/services/repository/files"
)
const (
diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go
index ab419a9a5a..f8b08a0b59 100644
--- a/services/packages/cleanup/cleanup.go
+++ b/services/packages/cleanup/cleanup.go
@@ -8,19 +8,20 @@ import (
"fmt"
"time"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- packages_service "code.gitea.io/gitea/services/packages"
- alpine_service "code.gitea.io/gitea/services/packages/alpine"
- arch_service "code.gitea.io/gitea/services/packages/arch"
- cargo_service "code.gitea.io/gitea/services/packages/cargo"
- container_service "code.gitea.io/gitea/services/packages/container"
- debian_service "code.gitea.io/gitea/services/packages/debian"
- rpm_service "code.gitea.io/gitea/services/packages/rpm"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ packages_service "forgejo.org/services/packages"
+ alpine_service "forgejo.org/services/packages/alpine"
+ alt_service "forgejo.org/services/packages/alt"
+ arch_service "forgejo.org/services/packages/arch"
+ cargo_service "forgejo.org/services/packages/cargo"
+ container_service "forgejo.org/services/packages/container"
+ debian_service "forgejo.org/services/packages/debian"
+ rpm_service "forgejo.org/services/packages/rpm"
)
// Task method to execute cleanup rules and cleanup expired package data
@@ -121,22 +122,27 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
}
if anyVersionDeleted {
- if pcr.Type == packages_model.TypeDebian {
+ switch pcr.Type {
+ case packages_model.TypeDebian:
if err := debian_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: debian.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
- } else if pcr.Type == packages_model.TypeAlpine {
+ case packages_model.TypeAlpine:
if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
- } else if pcr.Type == packages_model.TypeRpm {
+ case packages_model.TypeRpm:
if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
- } else if pcr.Type == packages_model.TypeArch {
+ case packages_model.TypeArch:
if err := arch_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: arch.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
+ case packages_model.TypeAlt:
+ if err := alt_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: alt.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
+ }
}
}
return nil
diff --git a/services/packages/cleanup/cleanup_sha256_test.go b/services/packages/cleanup/cleanup_sha256_test.go
index 41dde28248..efa254fc68 100644
--- a/services/packages/cleanup/cleanup_sha256_test.go
+++ b/services/packages/cleanup/cleanup_sha256_test.go
@@ -7,15 +7,15 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- container_module "code.gitea.io/gitea/modules/packages/container"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/timeutil"
- container_service "code.gitea.io/gitea/services/packages/container"
+ "forgejo.org/models/db"
+ "forgejo.org/models/packages"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ container_module "forgejo.org/modules/packages/container"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/timeutil"
+ container_service "forgejo.org/services/packages/container"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -78,7 +78,7 @@ func TestCleanupSHA256(t *testing.T) {
for range expected {
filtered = append(filtered, true)
}
- assert.EqualValues(t, filtered, logFiltered, expected)
+ assert.Equal(t, filtered, logFiltered, expected)
}
ancient := 1 * time.Hour
diff --git a/services/packages/cleanup/main_test.go b/services/packages/cleanup/main_test.go
index ded3d76c83..e9135c147a 100644
--- a/services/packages/cleanup/main_test.go
+++ b/services/packages/cleanup/main_test.go
@@ -6,7 +6,7 @@ package container
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/services/packages/container/blob_uploader.go b/services/packages/container/blob_uploader.go
index bae2e2d6af..ffc47f3853 100644
--- a/services/packages/container/blob_uploader.go
+++ b/services/packages/container/blob_uploader.go
@@ -9,10 +9,10 @@ import (
"io"
"os"
- packages_model "code.gitea.io/gitea/models/packages"
- packages_module "code.gitea.io/gitea/modules/packages"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ packages_model "forgejo.org/models/packages"
+ packages_module "forgejo.org/modules/packages"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
)
var (
@@ -92,7 +92,7 @@ func (u *BlobUploader) Append(ctx context.Context, r io.Reader) error {
u.BytesReceived += n
- u.HashStateBytes, err = u.MultiHasher.MarshalBinary()
+ u.HashStateBytes, err = u.MarshalBinary()
if err != nil {
return err
}
diff --git a/services/packages/container/cleanup.go b/services/packages/container/cleanup.go
index b5563c688f..a4ae7118f5 100644
--- a/services/packages/container/cleanup.go
+++ b/services/packages/container/cleanup.go
@@ -7,11 +7,11 @@ import (
"context"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
- "code.gitea.io/gitea/modules/optional"
- container_module "code.gitea.io/gitea/modules/packages/container"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ container_model "forgejo.org/models/packages/container"
+ "forgejo.org/modules/optional"
+ container_module "forgejo.org/modules/packages/container"
+ packages_service "forgejo.org/services/packages"
digest "github.com/opencontainers/go-digest"
)
diff --git a/services/packages/container/cleanup_sha256.go b/services/packages/container/cleanup_sha256.go
index 16afc74b18..5d0d02c22d 100644
--- a/services/packages/container/cleanup_sha256.go
+++ b/services/packages/container/cleanup_sha256.go
@@ -8,12 +8,12 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- container_module "code.gitea.io/gitea/modules/packages/container"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models/db"
+ "forgejo.org/models/packages"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ container_module "forgejo.org/modules/packages/container"
+ "forgejo.org/modules/timeutil"
)
var (
diff --git a/services/packages/container/common.go b/services/packages/container/common.go
index 5a14ed5b7a..758ef51721 100644
--- a/services/packages/container/common.go
+++ b/services/packages/container/common.go
@@ -7,9 +7,9 @@ import (
"context"
"strings"
- packages_model "code.gitea.io/gitea/models/packages"
- user_model "code.gitea.io/gitea/models/user"
- container_module "code.gitea.io/gitea/modules/packages/container"
+ packages_model "forgejo.org/models/packages"
+ user_model "forgejo.org/models/user"
+ container_module "forgejo.org/modules/packages/container"
)
// UpdateRepositoryNames updates the repository name property for all packages of the specific owner
diff --git a/services/packages/debian/repository.go b/services/packages/debian/repository.go
index e400f1e924..a8a401662e 100644
--- a/services/packages/debian/repository.go
+++ b/services/packages/debian/repository.go
@@ -14,14 +14,14 @@ import (
"strings"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- debian_model "code.gitea.io/gitea/models/packages/debian"
- user_model "code.gitea.io/gitea/models/user"
- packages_module "code.gitea.io/gitea/modules/packages"
- debian_module "code.gitea.io/gitea/modules/packages/debian"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ debian_model "forgejo.org/models/packages/debian"
+ user_model "forgejo.org/models/user"
+ packages_module "forgejo.org/modules/packages"
+ debian_module "forgejo.org/modules/packages/debian"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ packages_service "forgejo.org/services/packages"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
diff --git a/services/packages/package_update.go b/services/packages/package_update.go
new file mode 100644
index 0000000000..7fa938e260
--- /dev/null
+++ b/services/packages/package_update.go
@@ -0,0 +1,79 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package packages
+
+import (
+ "context"
+ "fmt"
+
+ org_model "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/util"
+)
+
+func LinkToRepository(ctx context.Context, pkg *packages_model.Package, repo *repo_model.Repository, doer *user_model.User) error {
+ if pkg.OwnerID != repo.OwnerID {
+ return util.ErrPermissionDenied
+ }
+ if pkg.RepoID > 0 {
+ return util.ErrInvalidArgument
+ }
+
+ perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
+ }
+ if !perms.CanWrite(unit.TypePackages) {
+ return util.ErrPermissionDenied
+ }
+
+ if err := packages_model.SetRepositoryLink(ctx, pkg.ID, repo.ID); err != nil {
+ return fmt.Errorf("error while linking package '%v' to repo '%v' : %w", pkg.Name, repo.FullName(), err)
+ }
+ return nil
+}
+
+func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer *user_model.User) error {
+ if pkg.RepoID == 0 {
+ return util.ErrInvalidArgument
+ }
+
+ repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID)
+ if err != nil && !repo_model.IsErrRepoNotExist(err) {
+ return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err)
+ }
+ if err == nil {
+ perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
+ }
+ if !perms.CanWrite(unit.TypePackages) {
+ return util.ErrPermissionDenied
+ }
+ }
+
+ user, err := user_model.GetUserByID(ctx, pkg.OwnerID)
+ if err != nil {
+ return err
+ }
+ if !doer.IsAdmin {
+ if !user.IsOrganization() {
+ if doer.ID != pkg.OwnerID {
+ return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
+ }
+ } else {
+ isOrgAdmin, err := org_model.OrgFromUser(user).IsOrgAdmin(ctx, doer.ID)
+ if err != nil {
+ return err
+ } else if !isOrgAdmin {
+ return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
+ }
+ }
+ }
+ return packages_model.UnlinkRepository(ctx, pkg.ID)
+}
diff --git a/services/packages/packages.go b/services/packages/packages.go
index 72ab19ee27..418ceab798 100644
--- a/services/packages/packages.go
+++ b/services/packages/packages.go
@@ -12,17 +12,17 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models/db"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- packages_module "code.gitea.io/gitea/modules/packages"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ packages_module "forgejo.org/modules/packages"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ notify_service "forgejo.org/services/notify"
)
var (
@@ -127,12 +127,12 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
OwnerID: pvci.Owner.ID,
Type: pvci.PackageType,
Name: pvci.Name,
- LowerName: strings.ToLower(pvci.Name),
+ LowerName: packages_model.ResolvePackageName(pvci.Name, pvci.PackageType),
SemverCompatible: pvci.SemverCompatible,
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err == packages_model.ErrDuplicatePackage {
+ if errors.Is(err, packages_model.ErrDuplicatePackage) {
packageCreated = false
} else {
log.Error("Error inserting package: %v", err)
@@ -208,7 +208,7 @@ func AddFileToExistingPackage(ctx context.Context, pvi *PackageInfo, pfci *Packa
// This method skips quota checks and should only be used for system-managed packages.
func AddFileToPackageVersionInternal(ctx context.Context, pv *packages_model.PackageVersion, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, error) {
return addFileToPackageWrapper(ctx, func(ctx context.Context) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
- return addFileToPackageVersionUnchecked(ctx, pv, pfci)
+ return addFileToPackageVersionUnchecked(ctx, pv, pfci, "")
})
}
@@ -244,14 +244,15 @@ func addFileToPackageWrapper(ctx context.Context, fn func(ctx context.Context) (
// NewPackageBlob creates a package blob instance
func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.PackageBlob {
- hashMD5, hashSHA1, hashSHA256, hashSHA512 := hsr.Sums()
+ hashMD5, hashSHA1, hashSHA256, hashSHA512, hashBlake2b := hsr.Sums()
return &packages_model.PackageBlob{
- Size: hsr.Size(),
- HashMD5: hex.EncodeToString(hashMD5),
- HashSHA1: hex.EncodeToString(hashSHA1),
- HashSHA256: hex.EncodeToString(hashSHA256),
- HashSHA512: hex.EncodeToString(hashSHA512),
+ Size: hsr.Size(),
+ HashMD5: hex.EncodeToString(hashMD5),
+ HashSHA1: hex.EncodeToString(hashSHA1),
+ HashSHA256: hex.EncodeToString(hashSHA256),
+ HashSHA512: hex.EncodeToString(hashSHA512),
+ HashBlake2b: hex.EncodeToString(hashBlake2b),
}
}
@@ -260,10 +261,10 @@ func addFileToPackageVersion(ctx context.Context, pv *packages_model.PackageVers
return nil, nil, false, err
}
- return addFileToPackageVersionUnchecked(ctx, pv, pfci)
+ return addFileToPackageVersionUnchecked(ctx, pv, pfci, pvi.PackageType)
}
-func addFileToPackageVersionUnchecked(ctx context.Context, pv *packages_model.PackageVersion, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
+func addFileToPackageVersionUnchecked(ctx context.Context, pv *packages_model.PackageVersion, pfci *PackageFileCreationInfo, packageType packages_model.Type) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
log.Trace("Adding package file: %v, %s", pv.ID, pfci.Filename)
pb, exists, err := packages_model.GetOrInsertBlob(ctx, NewPackageBlob(pfci.Data))
@@ -303,7 +304,7 @@ func addFileToPackageVersionUnchecked(ctx context.Context, pv *packages_model.Pa
VersionID: pv.ID,
BlobID: pb.ID,
Name: pfci.Filename,
- LowerName: strings.ToLower(pfci.Filename),
+ LowerName: packages_model.ResolvePackageName(pfci.Filename, packageType),
CompositeKey: pfci.CompositeKey,
IsLead: pfci.IsLead,
}
@@ -395,6 +396,8 @@ func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p
typeSpecificSize = setting.Packages.LimitSizePyPI
case packages_model.TypeRpm:
typeSpecificSize = setting.Packages.LimitSizeRpm
+ case packages_model.TypeAlt:
+ typeSpecificSize = setting.Packages.LimitSizeAlt
case packages_model.TypeRubyGems:
typeSpecificSize = setting.Packages.LimitSizeRubyGems
case packages_model.TypeSwift:
diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go
index 2cea04212a..26f34be2bc 100644
--- a/services/packages/rpm/repository.go
+++ b/services/packages/rpm/repository.go
@@ -16,20 +16,20 @@ import (
"strings"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- rpm_model "code.gitea.io/gitea/models/packages/rpm"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- packages_module "code.gitea.io/gitea/modules/packages"
- rpm_module "code.gitea.io/gitea/modules/packages/rpm"
- "code.gitea.io/gitea/modules/util"
- packages_service "code.gitea.io/gitea/services/packages"
+ packages_model "forgejo.org/models/packages"
+ rpm_model "forgejo.org/models/packages/rpm"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ packages_module "forgejo.org/modules/packages"
+ rpm_module "forgejo.org/modules/packages/rpm"
+ "forgejo.org/modules/util"
+ packages_service "forgejo.org/services/packages"
+ "code.forgejo.org/forgejo/go-rpmutils"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
- "github.com/sassoftware/go-rpmutils"
)
// GetOrCreateRepositoryVersion gets or creates the internal repository package
@@ -410,7 +410,6 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
files = append(files, f)
}
}
- packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release)
packages = append(packages, &Package{
Type: "rpm",
Name: pd.Package.Name,
@@ -439,7 +438,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
Archive: pd.FileMetadata.ArchiveSize,
},
Location: Location{
- Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture),
+ Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture, pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture),
},
Format: Format{
License: pd.VersionMetadata.License,
@@ -622,7 +621,7 @@ func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion,
return nil, err
}
- _, _, hashSHA256, _ := content.Sums()
+ _, _, hashSHA256, _, _ := content.Sums()
return &repoData{
Type: filetype,
diff --git a/services/pull/check.go b/services/pull/check.go
index 2d91ed07f5..6002e2ae26 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -11,23 +11,24 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/timeutil"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/timeutil"
+ asymkey_service "forgejo.org/services/asymkey"
+ notify_service "forgejo.org/services/notify"
+ shared_automerge "forgejo.org/services/shared/automerge"
)
// prPatchCheckerQueue represents a queue to handle update pull request tests
@@ -170,7 +171,7 @@ func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer
// checkAndUpdateStatus checks if pull request is possible to leaving checking status,
// and set to be either conflict or mergeable.
-func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) {
+func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) bool {
// If status has not been changed to conflict by testPatch then we are mergeable
if pr.Status == issues_model.PullRequestStatusChecking {
pr.Status = issues_model.PullRequestStatusMergeable
@@ -184,12 +185,15 @@ func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) {
if has {
log.Trace("Not updating status for %-v as it is due to be rechecked", pr)
- return
+ return false
}
if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files"); err != nil {
log.Error("Update[%-v]: %v", pr, err)
+ return false
}
+
+ return true
}
// getMergeCommit checks if a pull request has been merged
@@ -339,15 +343,22 @@ func handler(items ...string) []string {
}
func testPR(id int64) {
- pullWorkingPool.CheckIn(fmt.Sprint(id))
- defer pullWorkingPool.CheckOut(fmt.Sprint(id))
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id))
defer finished()
+ if pr, updated := testPRProtected(ctx, id); pr != nil && updated {
+ shared_automerge.AddToQueueIfMergeable(ctx, pr)
+ }
+}
+
+func testPRProtected(ctx context.Context, id int64) (*issues_model.PullRequest, bool) {
+ pullWorkingPool.CheckIn(fmt.Sprint(id))
+ defer pullWorkingPool.CheckOut(fmt.Sprint(id))
+
pr, err := issues_model.GetPullRequestByID(ctx, id)
if err != nil {
log.Error("Unable to GetPullRequestByID[%d] for testPR: %v", id, err)
- return
+ return nil, false
}
log.Trace("Testing %-v", pr)
@@ -357,12 +368,12 @@ func testPR(id int64) {
if pr.HasMerged {
log.Trace("%-v is already merged (status: %s, merge commit: %s)", pr, pr.Status, pr.MergedCommitID)
- return
+ return nil, false
}
if manuallyMerged(ctx, pr) {
log.Trace("%-v is manually merged (status: %s, merge commit: %s)", pr, pr.Status, pr.MergedCommitID)
- return
+ return nil, false
}
if err := TestPatch(pr); err != nil {
@@ -371,9 +382,10 @@ func testPR(id int64) {
if err := pr.UpdateCols(ctx, "status"); err != nil {
log.Error("update pr [%-v] status to PullRequestStatusError failed: %v", pr, err)
}
- return
+ return nil, false
}
- checkAndUpdateStatus(ctx, pr)
+
+ return pr, checkAndUpdateStatus(ctx, pr)
}
// CheckPRsForBaseBranch check all pulls with baseBrannch
@@ -395,7 +407,7 @@ func Init() error {
prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", handler)
if prPatchCheckerQueue == nil {
- return fmt.Errorf("unable to create pr_patch_checker queue")
+ return errors.New("unable to create pr_patch_checker queue")
}
go graceful.GetManager().RunWithCancel(prPatchCheckerQueue)
diff --git a/services/pull/check_test.go b/services/pull/check_test.go
index b99cf01ee1..9b7e1660bc 100644
--- a/services/pull/check_test.go
+++ b/services/pull/check_test.go
@@ -5,16 +5,15 @@
package pull
import (
- "context"
"strconv"
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -34,7 +33,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
cfg, err := setting.GetQueueSettings(setting.CfgProvider, "pr_patch_checker")
require.NoError(t, err)
- prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(context.Background(), "pr_patch_checker", cfg, testHandler, true)
+ prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(t.Context(), "pr_patch_checker", cfg, testHandler, true)
require.NoError(t, err)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
@@ -53,7 +52,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
select {
case id := <-idChan:
- assert.EqualValues(t, pr.ID, id)
+ assert.Equal(t, pr.ID, id)
case <-time.After(time.Second):
assert.FailNow(t, "Timeout: nothing was added to pullRequestQueue")
}
diff --git a/services/pull/comment.go b/services/pull/comment.go
index 53587d4f54..25542daa9f 100644
--- a/services/pull/comment.go
+++ b/services/pull/comment.go
@@ -6,11 +6,11 @@ package pull
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/json"
)
// getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID
diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go
index 0d4763a838..3c864c8ef2 100644
--- a/services/pull/commit_status.go
+++ b/services/pull/commit_status.go
@@ -9,13 +9,13 @@ import (
"errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
"github.com/gobwas/glob"
)
@@ -71,36 +71,6 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus,
return returnedStatus
}
-// IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
-func IsCommitStatusContextSuccess(commitStatuses []*git_model.CommitStatus, requiredContexts []string) bool {
- // If no specific context is required, require that last commit status is a success
- if len(requiredContexts) == 0 {
- status := git_model.CalcCommitStatus(commitStatuses)
- if status == nil || status.State != structs.CommitStatusSuccess {
- return false
- }
- return true
- }
-
- for _, ctx := range requiredContexts {
- var found bool
- for _, commitStatus := range commitStatuses {
- if commitStatus.Context == ctx {
- if commitStatus.State != structs.CommitStatusSuccess {
- return false
- }
-
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
- return true
-}
-
// IsPullCommitStatusPass returns if all required status checks PASS
func IsPullCommitStatusPass(ctx context.Context, pr *issues_model.PullRequest) (bool, error) {
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
diff --git a/services/pull/commit_status_test.go b/services/pull/commit_status_test.go
index 592acdd55c..593c88fb19 100644
--- a/services/pull/commit_status_test.go
+++ b/services/pull/commit_status_test.go
@@ -7,8 +7,8 @@ package pull
import (
"testing"
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/structs"
+ git_model "forgejo.org/models/git"
+ "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
)
diff --git a/services/pull/edits.go b/services/pull/edits.go
index c7550dcb07..dbf08e6851 100644
--- a/services/pull/edits.go
+++ b/services/pull/edits.go
@@ -8,10 +8,10 @@ import (
"context"
"errors"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ unit_model "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
)
var ErrUserHasNoPermissionForAction = errors.New("user not allowed to do this action")
diff --git a/services/pull/lfs.go b/services/pull/lfs.go
index ed03583d4f..8d9f401641 100644
--- a/services/pull/lfs.go
+++ b/services/pull/lfs.go
@@ -11,12 +11,12 @@ import (
"strconv"
"sync"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/git/pipeline"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/git/pipeline"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
)
// LFSPush pushes lfs objects referred to in new commits in the head repository from the base repository
diff --git a/services/pull/main_test.go b/services/pull/main_test.go
index 4bcb50fb96..5262b5be50 100644
--- a/services/pull/main_test.go
+++ b/services/pull/main_test.go
@@ -7,10 +7,10 @@ package pull
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/pull/merge.go b/services/pull/merge.go
index a1585e64ab..f69f8a87b4 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -6,6 +6,7 @@ package pull
import (
"context"
+ "errors"
"fmt"
"net/url"
"os"
@@ -13,24 +14,25 @@ import (
"regexp"
"strconv"
"strings"
+ "unicode"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/references"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- issue_service "code.gitea.io/gitea/services/issue"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/references"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ issue_service "forgejo.org/services/issue"
+ notify_service "forgejo.org/services/notify"
)
// getMergeMessage composes the message used when merging a pull request.
@@ -168,6 +170,41 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil)
}
+func AddCommitMessageTrailer(message, tailerKey, tailerValue string) string {
+ trailerLine := tailerKey + ": " + tailerValue
+ message = strings.ReplaceAll(message, "\r\n", "\n")
+ message = strings.ReplaceAll(message, "\r", "\n")
+ if strings.Contains(message, "\n"+trailerLine+"\n") || strings.HasSuffix(message, "\n"+trailerLine) {
+ return message
+ }
+
+ if !strings.HasSuffix(message, "\n") {
+ message += "\n"
+ }
+ lastNewLine := strings.LastIndexByte(message[:len(message)-1], '\n')
+ keyEnd := -1
+ if lastNewLine != -1 {
+ keyEnd = strings.IndexByte(message[lastNewLine:], ':')
+ if keyEnd != -1 {
+ keyEnd += lastNewLine
+ }
+ }
+ var lastLineKey string
+ if lastNewLine != -1 && keyEnd != -1 {
+ lastLineKey = message[lastNewLine+1 : keyEnd]
+ }
+
+ isLikelyTrailerLine := lastLineKey != "" && unicode.IsUpper(rune(lastLineKey[0])) && strings.Contains(message, "-")
+ for i := 0; isLikelyTrailerLine && i < len(lastLineKey); i++ {
+ r := rune(lastLineKey[i])
+ isLikelyTrailerLine = unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-'
+ }
+ if !strings.HasSuffix(message, "\n\n") && !isLikelyTrailerLine {
+ message += "\n"
+ }
+ return message + trailerLine
+}
+
// Merge merges pull request to base repository.
// Caller should check PR is ready to be merged (review and status checks)
func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error {
@@ -518,13 +555,13 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
if len(commitID) != objectFormat.FullLength() {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
commit, err := baseGitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
return err
}
@@ -535,7 +572,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
return err
}
if !ok {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
pr.MergedCommitID = commitID
@@ -548,7 +585,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
if merged, err = pr.SetMerged(ctx); err != nil {
return err
} else if !merged {
- return fmt.Errorf("SetMerged failed")
+ return errors.New("SetMerged failed")
}
return nil
}); err != nil {
diff --git a/services/pull/merge_ff_only.go b/services/pull/merge_ff_only.go
index f57c732104..1e1c337889 100644
--- a/services/pull/merge_ff_only.go
+++ b/services/pull/merge_ff_only.go
@@ -4,9 +4,9 @@
package pull
import (
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
// doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch)
diff --git a/services/pull/merge_merge.go b/services/pull/merge_merge.go
index bf56c071db..713e1f175d 100644
--- a/services/pull/merge_merge.go
+++ b/services/pull/merge_merge.go
@@ -4,9 +4,9 @@
package pull
import (
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
// doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch)
diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go
index 88f6c037eb..fc70da10a4 100644
--- a/services/pull/merge_prepare.go
+++ b/services/pull/merge_prepare.go
@@ -14,13 +14,13 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "forgejo.org/models"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ asymkey_service "forgejo.org/services/asymkey"
)
type mergeContext struct {
@@ -236,10 +236,72 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
// if there is a conflict it will return a models.ErrRebaseConflicts
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
- // Checkout head branch
- if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
+ // Create staging branch
+ if err := git.NewCommand(ctx, "branch").AddDynamicArguments(stagingBranch, trackingBranch).
Run(ctx.RunOpts()); err != nil {
- return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
+ return fmt.Errorf(
+ "unable to git branch tracking as staging in temp repo for %v: %w\n%s\n%s",
+ ctx.pr, err,
+ ctx.outbuf.String(),
+ ctx.errbuf.String(),
+ )
+ }
+ ctx.outbuf.Reset()
+ ctx.errbuf.Reset()
+
+ // Check git version for availability of git-replay. If it is available, we use
+ // it for performance and to preserve unknown commit headers like the
+ // "change-id" header used by Jujutsu and GitButler to track changes across
+ // rebase, amend etc.
+ if err := git.CheckGitVersionAtLeast("2.44"); err == nil {
+ // Use git-replay for performance and to preserve unknown headers,
+ // like the "change-id" header used by Jujutsu and GitButler.
+ if err := git.NewCommand(ctx, "replay", "--onto").AddDynamicArguments(baseBranch).
+ AddDynamicArguments(fmt.Sprintf("%s..%s", baseBranch, stagingBranch)).
+ Run(ctx.RunOpts()); err != nil {
+ // git-replay doesn't tell us which commit first created a merge conflict.
+ // In order to preserve the quality of our error messages, fall back to
+ // regular git-rebase.
+ goto regular_rebase
+ }
+ // git-replay worked, stdout contains the instructions for update-ref
+ updateRefInstructions := ctx.outbuf.String()
+ opts := ctx.RunOpts()
+ opts.Stdin = strings.NewReader(updateRefInstructions)
+ if err := git.NewCommand(ctx, "update-ref", "--stdin").Run(opts); err != nil {
+ return fmt.Errorf(
+ "Failed to update ref for %v: %w\n%s\n%s",
+ ctx.pr,
+ err,
+ ctx.outbuf.String(),
+ ctx.errbuf.String(),
+ )
+ }
+ // Checkout staging branch
+ if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(stagingBranch).
+ Run(ctx.RunOpts()); err != nil {
+ return fmt.Errorf(
+ "unable to git checkout staging in temp repo for %v: %w\n%s\n%s",
+ ctx.pr,
+ err,
+ ctx.outbuf.String(),
+ ctx.errbuf.String(),
+ )
+ }
+ ctx.outbuf.Reset()
+ ctx.errbuf.Reset()
+ return nil
+ }
+
+ // The available git version is too old to support git-replay, or git-replay
+ // failed and we want to determine the first commit that produced a
+ // merge-conflict. Fall back to regular rebase.
+regular_rebase:
+
+ // Checkout head branch
+ if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(stagingBranch).
+ Run(ctx.RunOpts()); err != nil {
+ return fmt.Errorf("unable to git checkout staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
ctx.outbuf.Reset()
ctx.errbuf.Reset()
diff --git a/services/pull/merge_rebase.go b/services/pull/merge_rebase.go
index ecf376220e..088934cdfb 100644
--- a/services/pull/merge_rebase.go
+++ b/services/pull/merge_rebase.go
@@ -7,10 +7,10 @@ import (
"fmt"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
)
// getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
diff --git a/services/pull/merge_squash.go b/services/pull/merge_squash.go
index 197d8102dd..f655224c5e 100644
--- a/services/pull/merge_squash.go
+++ b/services/pull/merge_squash.go
@@ -6,13 +6,13 @@ package pull
import (
"fmt"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
)
// doMergeStyleSquash gets a commit author signature for squash commits
@@ -66,7 +66,8 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
if setting.Repository.PullRequest.AddCoCommitterTrailers && ctx.committer.String() != sig.String() {
// add trailer
- message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String())
+ message = AddCommitMessageTrailer(message, "Co-authored-by", sig.String())
+ message = AddCommitMessageTrailer(message, "Co-committed-by", sig.String()) // FIXME: this one should be removed, it is not really used or widely used
}
cmdCommit := git.NewCommand(ctx, "commit").
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
diff --git a/services/pull/merge_test.go b/services/pull/merge_test.go
index 6df6f55d46..2a26759956 100644
--- a/services/pull/merge_test.go
+++ b/services/pull/merge_test.go
@@ -65,3 +65,28 @@ func Test_expandDefaultMergeMessage(t *testing.T) {
})
}
}
+
+func TestAddCommitMessageTailer(t *testing.T) {
+ // add tailer for empty message
+ assert.Equal(t, "\n\nTest-tailer: TestValue", AddCommitMessageTrailer("", "Test-tailer", "TestValue"))
+
+ // add tailer for message without newlines
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nNot tailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n\nNot tailer: xxx", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nNotTailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n\nNotTailer: xxx", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nnot-tailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n\nnot-tailer: xxx", "Test-tailer", "TestValue"))
+
+ // add tailer for message with one EOL
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with two EOLs
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with existing tailer (won't duplicate)
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTrailer("title\n\nTest-tailer: TestValue", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nTest-tailer: TestValue\n", AddCommitMessageTrailer("title\n\nTest-tailer: TestValue\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with existing tailer and different value (will append)
+ assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1", "Test-tailer", "v2"))
+ assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1\n", "Test-tailer", "v2"))
+}
diff --git a/services/pull/patch.go b/services/pull/patch.go
index e90b4bdbbe..37a0f818e9 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -5,7 +5,7 @@
package pull
import (
- "bufio"
+ "bytes"
"context"
"fmt"
"io"
@@ -13,18 +13,15 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/util"
"github.com/gobwas/glob"
)
@@ -49,66 +46,159 @@ func DownloadDiffOrPatch(ctx context.Context, pr *issues_model.PullRequest, w io
return nil
}
-var patchErrorSuffices = []string{
- ": already exists in index",
- ": patch does not apply",
- ": already exists in working directory",
- "unrecognized input",
- ": No such file or directory",
- ": does not exist in index",
-}
-
// TestPatch will test whether a simple patch will apply
func TestPatch(pr *issues_model.PullRequest) error {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("TestPatch: %s", pr))
defer finished()
- prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
- if err != nil {
- if !git_model.IsErrBranchNotExist(err) {
- log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
- }
- return err
- }
- defer cancel()
-
- return testPatch(ctx, prCtx, pr)
+ testPatchCtx, err := testPatch(ctx, pr)
+ testPatchCtx.close()
+ return err
}
-func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullRequest) error {
- gitRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
- if err != nil {
- return fmt.Errorf("OpenRepository: %w", err)
- }
- defer gitRepo.Close()
+type testPatchContext struct {
+ headRev string
+ headIsCommitID bool
+ baseRev string
+ env []string
+ gitRepo *git.Repository
+ close func()
+}
- // 1. update merge base
- pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base", "--", "base", "tracking").RunStdString(&git.RunOpts{Dir: prCtx.tmpBasePath})
- if err != nil {
- var err2 error
- pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base")
- if err2 != nil {
- return fmt.Errorf("GetMergeBase: %v and can't find commit ID for base: %w", err, err2)
+// LoadHeadRevision loads the necessary information to access the head revision.
+func (t *testPatchContext) LoadHeadRevision(ctx context.Context, pr *issues_model.PullRequest) error {
+ // If AGit, then use HeadCommitID if set (AGit flow creates pull request),
+ // otherwise use the pull request reference.
+ if pr.Flow == issues_model.PullRequestFlowAGit {
+ if len(pr.HeadCommitID) > 0 {
+ t.headRev = pr.HeadCommitID
+ t.headIsCommitID = true
+ return nil
}
- }
- pr.MergeBase = strings.TrimSpace(pr.MergeBase)
- if pr.HeadCommitID, err = gitRepo.GetRefCommitID(git.BranchPrefix + "tracking"); err != nil {
- return fmt.Errorf("GetBranchCommitID: can't find commit ID for head: %w", err)
- }
-
- if pr.HeadCommitID == pr.MergeBase {
- pr.Status = issues_model.PullRequestStatusAncestor
+ t.headRev = pr.GetGitRefName()
return nil
}
- // 2. Check for conflicts
- if conflicts, err := checkConflicts(ctx, pr, gitRepo, prCtx.tmpBasePath); err != nil || conflicts || pr.Status == issues_model.PullRequestStatusEmpty {
+ // If it is within the same repository, simply return the branch name.
+ if pr.BaseRepoID == pr.HeadRepoID {
+ t.headRev = pr.GetGitHeadBranchRefName()
+ return nil
+ }
+
+ // We are in Github flow, head and base repository are different.
+ // Resolve the head branch to a commitID and return a Git alternate
+ // environment for the head repository.
+ gitRepo, err := git.OpenRepository(ctx, pr.HeadRepo.RepoPath())
+ if err != nil {
+ return err
+ }
+ defer gitRepo.Close()
+
+ headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitHeadBranchRefName())
+ if err != nil {
return err
}
+ t.headRev = headCommitID
+ t.headIsCommitID = true
+ t.env = append(os.Environ(), `GIT_ALTERNATE_OBJECT_DIRECTORIES=`+pr.HeadRepo.RepoPath()+"/objects")
+ return nil
+}
+
+// getTestPatchCtx constructs a new testpatch context for the given pull request.
+func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) {
+ testPatchCtx := &testPatchContext{
+ close: func() {},
+ }
+
+ if git.SupportGitMergeTree {
+ if err := pr.LoadBaseRepo(ctx); err != nil {
+ return testPatchCtx, fmt.Errorf("LoadBaseRepo: %w", err)
+ }
+ if err := pr.LoadHeadRepo(ctx); err != nil {
+ return testPatchCtx, fmt.Errorf("LoadHeadRepo: %w", err)
+ }
+
+ if err := testPatchCtx.LoadHeadRevision(ctx, pr); err != nil {
+ return testPatchCtx, fmt.Errorf("LoadHeadRevision: %w", err)
+ }
+
+ gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
+ if err != nil {
+ return testPatchCtx, fmt.Errorf("OpenRepository: %w", err)
+ }
+
+ testPatchCtx.baseRev = git.BranchPrefix + pr.BaseBranch
+ testPatchCtx.gitRepo = gitRepo
+ testPatchCtx.close = func() {
+ gitRepo.Close()
+ }
+ } else {
+ prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
+ if err != nil {
+ return testPatchCtx, fmt.Errorf("createTemporaryRepoForPR: %w", err)
+ }
+ testPatchCtx.close = cancel
+
+ gitRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
+ if err != nil {
+ return testPatchCtx, fmt.Errorf("OpenRepository: %w", err)
+ }
+
+ testPatchCtx.baseRev = git.BranchPrefix + baseBranch
+ testPatchCtx.headRev = git.BranchPrefix + trackingBranch
+ testPatchCtx.gitRepo = gitRepo
+ testPatchCtx.close = func() {
+ cancel()
+ gitRepo.Close()
+ }
+ }
+ return testPatchCtx, nil
+}
+
+func testPatch(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) {
+ testPatchCtx, err := getTestPatchCtx(ctx, pr)
+ if err != nil {
+ return testPatchCtx, fmt.Errorf("getTestPatchCtx: %w", err)
+ }
+
+ // 1. update merge base
+ pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base").AddDashesAndList(testPatchCtx.baseRev, testPatchCtx.headRev).RunStdString(&git.RunOpts{Dir: testPatchCtx.gitRepo.Path, Env: testPatchCtx.env})
+ if err != nil {
+ var err2 error
+ pr.MergeBase, err2 = testPatchCtx.gitRepo.GetRefCommitID(testPatchCtx.baseRev)
+ if err2 != nil {
+ return testPatchCtx, fmt.Errorf("GetMergeBase: %v and can't find commit ID for base: %w", err, err2)
+ }
+ }
+ pr.MergeBase = strings.TrimSpace(pr.MergeBase)
+
+ if testPatchCtx.headIsCommitID {
+ pr.HeadCommitID = testPatchCtx.headRev
+ } else {
+ if pr.HeadCommitID, err = testPatchCtx.gitRepo.GetRefCommitID(testPatchCtx.headRev); err != nil {
+ return testPatchCtx, fmt.Errorf("GetRefCommitID: can't find commit ID for head: %w", err)
+ }
+ }
+
+ // If the head commit is equal to the merge base it roughly means that the
+ // head commit is a parent of the base commit.
+ if pr.HeadCommitID == pr.MergeBase {
+ pr.Status = issues_model.PullRequestStatusAncestor
+ return testPatchCtx, nil
+ }
+
+ // 2. Check for conflicts
+ if conflicts, err := checkConflicts(ctx, pr, testPatchCtx); err != nil || conflicts || pr.Status == issues_model.PullRequestStatusEmpty {
+ if err != nil {
+ return testPatchCtx, fmt.Errorf("checkConflicts: %w", err)
+ }
+ return testPatchCtx, nil
+ }
+
// 3. Check for protected files changes
- if err = checkPullFilesProtection(ctx, pr, gitRepo); err != nil {
- return fmt.Errorf("pr.CheckPullFilesProtection(): %v", err)
+ if err = checkPullFilesProtection(ctx, pr, testPatchCtx); err != nil {
+ return testPatchCtx, fmt.Errorf("checkPullFilesProtection: %v", err)
}
if len(pr.ChangedProtectedFiles) > 0 {
@@ -117,7 +207,7 @@ func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullReque
pr.Status = issues_model.PullRequestStatusMergeable
- return nil
+ return testPatchCtx, nil
}
type errMergeConflict struct {
@@ -236,12 +326,12 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}
// AttemptThreeWayMerge will attempt to three way merge using git read-tree and then follow the git merge-one-file algorithm to attempt to resolve basic conflicts
-func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repository, base, ours, theirs, description string) (bool, []string, error) {
+func AttemptThreeWayMerge(ctx context.Context, gitRepo *git.Repository, base, ours, theirs, description string) (bool, []string, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// First we use read-tree to do a simple three-way merge
- if _, _, err := git.NewCommand(ctx, "read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitPath}); err != nil {
+ if _, _, err := git.NewCommand(ctx, "read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitRepo.Path}); err != nil {
log.Error("Unable to run read-tree -m! Error: %v", err)
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
}
@@ -251,7 +341,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
unmerged := make(chan *unmergedFile)
- go unmergedFiles(ctx, gitPath, unmerged)
+ go unmergedFiles(ctx, gitRepo.Path, unmerged)
defer func() {
cancel()
@@ -274,7 +364,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
}
// OK now we have the unmerged file triplet attempt to merge it
- if err := attemptMerge(ctx, file, gitPath, &filesToRemove, &filesToAdd); err != nil {
+ if err := attemptMerge(ctx, file, gitRepo.Path, &filesToRemove, &filesToAdd); err != nil {
if conflictErr, ok := err.(*errMergeConflict); ok {
log.Trace("Conflict: %s in %s", conflictErr.filename, description)
conflict = true
@@ -299,14 +389,82 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
return conflict, conflictedFiles, nil
}
-func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository, tmpBasePath string) (bool, error) {
- // 1. checkConflicts resets the conflict status - therefore - reset the conflict status
+// MergeTree runs a 3-way merge between `ours` and `theirs` with
+// `base` as the merge base.
+//
+// It uses git-merge-tree(1) to do this merge without requiring a work-tree and
+// can run in a base repository. It returns the object ID of the merge tree, if
+// there are any conflicts and conflicted files.
+func MergeTree(ctx context.Context, gitRepo *git.Repository, base, ours, theirs string, env []string) (string, bool, []string, error) {
+ cmd := git.NewCommand(ctx, "merge-tree", "--write-tree", "-z", "--name-only", "--no-messages")
+ if git.CheckGitVersionAtLeast("2.40") == nil {
+ cmd.AddOptionFormat("--merge-base=%s", base)
+ }
+
+ stdout := &bytes.Buffer{}
+ gitErr := cmd.AddDynamicArguments(ours, theirs).Run(&git.RunOpts{Dir: gitRepo.Path, Stdout: stdout, Env: env})
+ if gitErr != nil && !git.IsErrorExitCode(gitErr, 1) {
+ log.Error("Unable to run merge-tree: %v", gitErr)
+ return "", false, nil, fmt.Errorf("unable to run merge-tree: %w", gitErr)
+ }
+
+ // There are two situations that we consider for the output:
+ // 1. Clean merge and the output is NUL
+ // 2. Merge conflict and the output is NULNUL
+ treeOID, conflictedFileInfo, _ := strings.Cut(stdout.String(), "\x00")
+ if len(conflictedFileInfo) == 0 {
+ return treeOID, git.IsErrorExitCode(gitErr, 1), nil, nil
+ }
+
+ // Remove last NULL-byte from conflicted file info, then split with NULL byte as seperator.
+ return treeOID, true, strings.Split(conflictedFileInfo[:len(conflictedFileInfo)-1], "\x00"), nil
+}
+
+// checkConflicts takes a pull request and checks if merging it would result in
+// merge conflicts and checks if the diff is empty; the status is set accordingly.
+func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, testPatchCtx *testPatchContext) (bool, error) {
+ // Resets the conflict status.
pr.ConflictedFiles = nil
+ if git.SupportGitMergeTree {
+ // Check for conflicts via a merge-tree.
+ treeHash, conflict, conflictFiles, err := MergeTree(ctx, testPatchCtx.gitRepo, pr.MergeBase, testPatchCtx.baseRev, testPatchCtx.headRev, testPatchCtx.env)
+ if err != nil {
+ return false, fmt.Errorf("MergeTree: %w", err)
+ }
+
+ if !conflict {
+ // No conflicts were detected, now check if the pull request actually
+ // contains anything useful via a diff. git-diff-tree(1) with --quiet
+ // will return exit code 0 if there's no diff and exit code 1 if there's
+ // a diff.
+ err := git.NewCommand(ctx, "diff-tree", "--quiet").AddDynamicArguments(treeHash, pr.MergeBase).Run(&git.RunOpts{Dir: testPatchCtx.gitRepo.Path, Env: testPatchCtx.env})
+ isEmpty := true
+ if err != nil {
+ if git.IsErrorExitCode(err, 1) {
+ isEmpty = false
+ } else {
+ return false, fmt.Errorf("DiffTree: %w", err)
+ }
+ }
+
+ if isEmpty {
+ log.Debug("PullRequest[%d]: Patch is empty - ignoring", pr.ID)
+ pr.Status = issues_model.PullRequestStatusEmpty
+ }
+ return false, nil
+ }
+
+ pr.Status = issues_model.PullRequestStatusConflict
+ pr.ConflictedFiles = conflictFiles
+
+ log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
+ return true, nil
+ }
+
// 2. AttemptThreeWayMerge first - this is much quicker than plain patch to base
description := fmt.Sprintf("PR[%d] %s/%s#%d", pr.ID, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Index)
- conflict, conflictFiles, err := AttemptThreeWayMerge(ctx,
- tmpBasePath, gitRepo, pr.MergeBase, "base", "tracking", description)
+ conflict, conflictFiles, err := AttemptThreeWayMerge(ctx, testPatchCtx.gitRepo, pr.MergeBase, testPatchCtx.baseRev, testPatchCtx.headRev, description)
if err != nil {
return false, err
}
@@ -315,13 +473,13 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// No conflicts detected so we need to check if the patch is empty...
// a. Write the newly merged tree and check the new tree-hash
var treeHash string
- treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: testPatchCtx.gitRepo.Path})
if err != nil {
- lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: testPatchCtx.gitRepo.Path})
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
}
treeHash = strings.TrimSpace(treeHash)
- baseTree, err := gitRepo.GetTree("base")
+ baseTree, err := testPatchCtx.gitRepo.GetTree(testPatchCtx.baseRev)
if err != nil {
return false, err
}
@@ -335,171 +493,11 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
return false, nil
}
- // 3. OK the three-way merge method has detected conflicts
- // 3a. Are still testing with GitApply? If not set the conflict status and move on
- if !setting.Repository.PullRequest.TestConflictingPatchesWithGitApply {
- pr.Status = issues_model.PullRequestStatusConflict
- pr.ConflictedFiles = conflictFiles
+ pr.Status = issues_model.PullRequestStatusConflict
+ pr.ConflictedFiles = conflictFiles
- log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
- return true, nil
- }
-
- // 3b. Create a plain patch from head to base
- tmpPatchFile, err := os.CreateTemp("", "patch")
- if err != nil {
- log.Error("Unable to create temporary patch file! Error: %v", err)
- return false, fmt.Errorf("unable to create temporary patch file! Error: %w", err)
- }
- defer func() {
- _ = util.Remove(tmpPatchFile.Name())
- }()
-
- if err := gitRepo.GetDiffBinary(pr.MergeBase, "tracking", tmpPatchFile); err != nil {
- tmpPatchFile.Close()
- log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
- return false, fmt.Errorf("unable to get patch file from %s to %s in %s Error: %w", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
- }
- stat, err := tmpPatchFile.Stat()
- if err != nil {
- tmpPatchFile.Close()
- return false, fmt.Errorf("unable to stat patch file: %w", err)
- }
- patchPath := tmpPatchFile.Name()
- tmpPatchFile.Close()
-
- // 3c. if the size of that patch is 0 - there can be no conflicts!
- if stat.Size() == 0 {
- log.Debug("PullRequest[%d]: Patch is empty - ignoring", pr.ID)
- pr.Status = issues_model.PullRequestStatusEmpty
- return false, nil
- }
-
- log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
-
- // 4. Read the base branch in to the index of the temporary repository
- _, _, err = git.NewCommand(gitRepo.Ctx, "read-tree", "base").RunStdString(&git.RunOpts{Dir: tmpBasePath})
- if err != nil {
- return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err)
- }
-
- // 5. Now get the pull request configuration to check if we need to ignore whitespace
- prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
- if err != nil {
- return false, err
- }
- prConfig := prUnit.PullRequestsConfig()
-
- // 6. Prepare the arguments to apply the patch against the index
- cmdApply := git.NewCommand(gitRepo.Ctx, "apply", "--check", "--cached")
- if prConfig.IgnoreWhitespaceConflicts {
- cmdApply.AddArguments("--ignore-whitespace")
- }
- is3way := false
- if git.CheckGitVersionAtLeast("2.32.0") == nil {
- cmdApply.AddArguments("--3way")
- is3way = true
- }
- cmdApply.AddDynamicArguments(patchPath)
-
- // 7. Prep the pipe:
- // - Here we could do the equivalent of:
- // `git apply --check --cached patch_file > conflicts`
- // Then iterate through the conflicts. However, that means storing all the conflicts
- // in memory - which is very wasteful.
- // - alternatively we can do the equivalent of:
- // `git apply --check ... | grep ...`
- // meaning we don't store all of the conflicts unnecessarily.
- stderrReader, stderrWriter, err := os.Pipe()
- if err != nil {
- log.Error("Unable to open stderr pipe: %v", err)
- return false, fmt.Errorf("unable to open stderr pipe: %w", err)
- }
- defer func() {
- _ = stderrReader.Close()
- _ = stderrWriter.Close()
- }()
-
- // 8. Run the check command
- conflict = false
- err = cmdApply.Run(&git.RunOpts{
- Dir: tmpBasePath,
- Stderr: stderrWriter,
- PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
- // Close the writer end of the pipe to begin processing
- _ = stderrWriter.Close()
- defer func() {
- // Close the reader on return to terminate the git command if necessary
- _ = stderrReader.Close()
- }()
-
- const prefix = "error: patch failed:"
- const errorPrefix = "error: "
- const threewayFailed = "Failed to perform three-way merge..."
- const appliedPatchPrefix = "Applied patch to '"
- const withConflicts = "' with conflicts."
-
- conflicts := make(container.Set[string])
-
- // Now scan the output from the command
- scanner := bufio.NewScanner(stderrReader)
- for scanner.Scan() {
- line := scanner.Text()
- log.Trace("PullRequest[%d].testPatch: stderr: %s", pr.ID, line)
- if strings.HasPrefix(line, prefix) {
- conflict = true
- filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
- conflicts.Add(filepath)
- } else if is3way && line == threewayFailed {
- conflict = true
- } else if strings.HasPrefix(line, errorPrefix) {
- conflict = true
- for _, suffix := range patchErrorSuffices {
- if strings.HasSuffix(line, suffix) {
- filepath := strings.TrimSpace(strings.TrimSuffix(line[len(errorPrefix):], suffix))
- if filepath != "" {
- conflicts.Add(filepath)
- }
- break
- }
- }
- } else if is3way && strings.HasPrefix(line, appliedPatchPrefix) && strings.HasSuffix(line, withConflicts) {
- conflict = true
- filepath := strings.TrimPrefix(strings.TrimSuffix(line, withConflicts), appliedPatchPrefix)
- if filepath != "" {
- conflicts.Add(filepath)
- }
- }
- // only list 10 conflicted files
- if len(conflicts) >= 10 {
- break
- }
- }
-
- if len(conflicts) > 0 {
- pr.ConflictedFiles = make([]string, 0, len(conflicts))
- for key := range conflicts {
- pr.ConflictedFiles = append(pr.ConflictedFiles, key)
- }
- }
-
- return nil
- },
- })
-
- // 9. Check if the found conflictedfiles is non-zero, "err" could be non-nil, so we should ignore it if we found conflicts.
- // Note: `"err" could be non-nil` is due that if enable 3-way merge, it doesn't return any error on found conflicts.
- if len(pr.ConflictedFiles) > 0 {
- if conflict {
- pr.Status = issues_model.PullRequestStatusConflict
- log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
-
- return true, nil
- }
- } else if err != nil {
- return false, fmt.Errorf("git apply --check: %w", err)
- }
- return false, nil
+ log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
+ return true, nil
}
// CheckFileProtection check file Protection
@@ -558,7 +556,7 @@ func CheckUnprotectedFiles(repo *git.Repository, oldCommitID, newCommitID string
}
// checkPullFilesProtection check if pr changed protected files and save results
-func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) error {
+func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest, testPatchCtx *testPatchContext) error {
if pr.Status == issues_model.PullRequestStatusEmpty {
pr.ChangedProtectedFiles = nil
return nil
@@ -574,7 +572,7 @@ func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest,
return nil
}
- pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.MergeBase, "tracking", pb.GetProtectedFilePatterns(), 10, os.Environ())
+ pr.ChangedProtectedFiles, err = CheckFileProtection(testPatchCtx.gitRepo, pr.MergeBase, testPatchCtx.headRev, pb.GetProtectedFilePatterns(), 10, testPatchCtx.env)
if err != nil && !models.IsErrFilePathProtected(err) {
return err
}
diff --git a/services/pull/patch_test.go b/services/pull/patch_test.go
new file mode 100644
index 0000000000..bcab19fc58
--- /dev/null
+++ b/services/pull/patch_test.go
@@ -0,0 +1,62 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package pull
+
+import (
+ "fmt"
+ "testing"
+
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoadHeadRevision(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ t.Run("AGit", func(t *testing.T) {
+ t.Run("New", func(t *testing.T) {
+ ctx := &testPatchContext{}
+ require.NoError(t, ctx.LoadHeadRevision(t.Context(), &issues_model.PullRequest{Flow: issues_model.PullRequestFlowAGit, HeadCommitID: "Commit!"}))
+
+ assert.Empty(t, ctx.env)
+ assert.Equal(t, "Commit!", ctx.headRev)
+ assert.True(t, ctx.headIsCommitID)
+ })
+ t.Run("Existing", func(t *testing.T) {
+ ctx := &testPatchContext{}
+ require.NoError(t, ctx.LoadHeadRevision(t.Context(), &issues_model.PullRequest{Flow: issues_model.PullRequestFlowAGit, Index: 371}))
+
+ assert.Empty(t, ctx.env)
+ assert.Equal(t, "refs/pull/371/head", ctx.headRev)
+ assert.False(t, ctx.headIsCommitID)
+ })
+ })
+
+ t.Run("Same repository", func(t *testing.T) {
+ ctx := &testPatchContext{}
+ require.NoError(t, ctx.LoadHeadRevision(t.Context(), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})))
+
+ assert.Empty(t, ctx.env)
+ assert.Equal(t, "refs/heads/branch1", ctx.headRev)
+ assert.False(t, ctx.headIsCommitID)
+ })
+
+ t.Run("Across repository", func(t *testing.T) {
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3})
+ require.NoError(t, pr.LoadHeadRepo(t.Context()))
+
+ ctx := &testPatchContext{}
+ require.NoError(t, ctx.LoadHeadRevision(t.Context(), pr))
+
+ if assert.NotEmpty(t, ctx.env) {
+ assert.Equal(t, fmt.Sprintf("GIT_ALTERNATE_OBJECT_DIRECTORIES=%s/user13/repo11.git/objects", setting.RepoRootPath), ctx.env[len(ctx.env)-1])
+ }
+ assert.Equal(t, "0abcb056019adb8336cf9db3ad9d9cf80cd4b141", ctx.headRev)
+ assert.True(t, ctx.headIsCommitID)
+ })
+}
diff --git a/services/pull/patch_unmerged.go b/services/pull/patch_unmerged.go
index c60c48d923..caa0318c48 100644
--- a/services/pull/patch_unmerged.go
+++ b/services/pull/patch_unmerged.go
@@ -13,8 +13,8 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
// lsFileLine is a Quadruplet struct (+error) representing a partially parsed line from ls-files
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 6af7d8ba0c..26210f7156 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -13,27 +13,27 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- 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"
- gitea_context "code.gitea.io/gitea/services/context"
- issue_service "code.gitea.io/gitea/services/issue"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/sync"
+ "forgejo.org/modules/util"
+ gitea_context "forgejo.org/services/context"
+ issue_service "forgejo.org/services/issue"
+ notify_service "forgejo.org/services/notify"
)
// TODO: use clustered lock (unique queue? or *abuse* cache)
@@ -46,22 +46,15 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
return user_model.ErrBlockedByUser
}
- prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
+ testPatchCtx, err := testPatch(ctx, pr)
+ defer testPatchCtx.close()
if err != nil {
- if !git_model.IsErrBranchNotExist(err) {
- log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
- }
- return err
- }
- defer cancel()
-
- if err := testPatch(ctx, prCtx, pr); err != nil {
- return err
+ return fmt.Errorf("testPatch: %w", err)
}
- divergence, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
+ divergence, err := git.GetDivergingCommits(ctx, testPatchCtx.gitRepo.Path, testPatchCtx.baseRev, testPatchCtx.headRev, testPatchCtx.env)
if err != nil {
- return err
+ return fmt.Errorf("GetDivergingCommits: %w", err)
}
pr.CommitsAhead = divergence.Ahead
pr.CommitsBehind = divergence.Behind
diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go
index c51619e7f6..010b7a6404 100644
--- a/services/pull/pull_test.go
+++ b/services/pull/pull_test.go
@@ -7,13 +7,13 @@ package pull
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/pull/review.go b/services/pull/review.go
index 927c43150b..c740328e4c 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -6,22 +6,23 @@ package pull
import (
"context"
+ "errors"
"fmt"
"io"
"regexp"
"strings"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
)
var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`)
@@ -232,7 +233,7 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
commit, err := gitRepo.LineBlame(head, gitRepo.Path, treePath, uint(line))
if err == nil {
commitID = commit.ID.String()
- } else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
+ } else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") && !notEnoughLines.MatchString(err.Error()) {
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
}
}
@@ -387,7 +388,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
}
if review.Type != issues_model.ReviewTypeApprove && review.Type != issues_model.ReviewTypeReject {
- return nil, fmt.Errorf("not need to dismiss this review because it's type is not Approve or change request")
+ return nil, errors.New("not need to dismiss this review because it's type is not Approve or change request")
}
// load data for notify
@@ -397,7 +398,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
// Check if the review's repoID is the one we're currently expecting.
if review.Issue.RepoID != repoID {
- return nil, fmt.Errorf("reviews's repository is not the same as the one we expect")
+ return nil, errors.New("reviews's repository is not the same as the one we expect")
}
issue := review.Issue
diff --git a/services/pull/review_test.go b/services/pull/review_test.go
index 4cb3ad007c..0d9fe7f902 100644
--- a/services/pull/review_test.go
+++ b/services/pull/review_test.go
@@ -6,11 +6,11 @@ package pull_test
import (
"testing"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- pull_service "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ pull_service "forgejo.org/services/pull"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index 36bdbde55c..76ae0df018 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -11,12 +11,12 @@ import (
"path/filepath"
"strings"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
)
// Temporary repos created here use standard branch names to help simplify
@@ -103,11 +103,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
remoteRepoName := "head_repo"
baseBranch := "base"
- fetchArgs := git.TrustedCmdArgs{"--no-tags"}
- if git.CheckGitVersionAtLeast("2.25.0") == nil {
- // Writing the commit graph can be slow and is not needed here
- fetchArgs = append(fetchArgs, "--no-write-commit-graph")
- }
+ fetchArgs := git.TrustedCmdArgs{"--no-tags", "--no-write-commit-graph"}
// addCacheRepo adds git alternatives for the cacheRepoPath in the repoPath
addCacheRepo := func(repoPath, cacheRepoPath string) error {
diff --git a/services/pull/update.go b/services/pull/update.go
index dbc1b711e2..563c11fff3 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -5,24 +5,25 @@ package pull
import (
"context"
+ "errors"
"fmt"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
)
// Update updates pull request with base branch.
func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error {
if pr.Flow == issues_model.PullRequestFlowAGit {
// TODO: update of agit flow pull request's head branch is unsupported
- return fmt.Errorf("update of agit flow pull request's head branch is unsupported")
+ return errors.New("update of agit flow pull request's head branch is unsupported")
}
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
@@ -175,6 +176,6 @@ func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*git.Diver
}
defer cancel()
- diff, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
+ diff, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch, nil)
return &diff, err
}
diff --git a/services/pull/update_rebase.go b/services/pull/update_rebase.go
index 3e2a7be132..b12613985a 100644
--- a/services/pull/update_rebase.go
+++ b/services/pull/update_rebase.go
@@ -8,13 +8,13 @@ import (
"fmt"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
)
// updateHeadByRebaseOnToBase handles updating a PR's head branch by rebasing it on the PR current base branch
diff --git a/services/release/release.go b/services/release/release.go
index 876514beec..90eb1320ed 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -9,22 +9,22 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/attachment"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/attachment"
+ notify_service "forgejo.org/services/notify"
)
type AttachmentChange struct {
@@ -161,17 +161,17 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, msg string,
for _, attachmentChange := range attachmentChanges {
if attachmentChange.Action != "add" {
- return fmt.Errorf("can only create new attachments when creating release")
+ return errors.New("can only create new attachments when creating release")
}
switch attachmentChange.Type {
case "attachment":
if attachmentChange.UUID == "" {
- return fmt.Errorf("new attachment should have a uuid")
+ return errors.New("new attachment should have a uuid")
}
addAttachmentUUIDs.Add(attachmentChange.UUID)
case "external":
if attachmentChange.Name == "" || attachmentChange.ExternalURL == "" {
- return fmt.Errorf("new external attachment should have a name and external url")
+ return errors.New("new external attachment should have a name and external url")
}
_, err = attachment.NewExternalAttachment(gitRepo.Ctx, &repo_model.Attachment{
@@ -186,7 +186,7 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, msg string,
}
default:
if attachmentChange.Type == "" {
- return fmt.Errorf("missing attachment type")
+ return errors.New("missing attachment type")
}
return fmt.Errorf("unknown attachment type: '%q'", attachmentChange.Type)
}
@@ -280,7 +280,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
addAttachmentUUIDs.Add(attachmentChange.UUID)
case "external":
if attachmentChange.Name == "" || attachmentChange.ExternalURL == "" {
- return fmt.Errorf("new external attachment should have a name and external url")
+ return errors.New("new external attachment should have a name and external url")
}
_, err := attachment.NewExternalAttachment(ctx, &repo_model.Attachment{
Name: attachmentChange.Name,
@@ -294,13 +294,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
}
default:
if attachmentChange.Type == "" {
- return fmt.Errorf("missing attachment type")
+ return errors.New("missing attachment type")
}
return fmt.Errorf("unknown attachment type: %q", attachmentChange.Type)
}
case "delete":
if attachmentChange.UUID == "" {
- return fmt.Errorf("attachment deletion should have a uuid")
+ return errors.New("attachment deletion should have a uuid")
}
delAttachmentUUIDs.Add(attachmentChange.UUID)
case "update":
@@ -308,7 +308,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
updateAttachments.Add(attachmentChange)
default:
if attachmentChange.Action == "" {
- return fmt.Errorf("missing attachment action")
+ return errors.New("missing attachment action")
}
return fmt.Errorf("unknown attachment action: %q", attachmentChange.Action)
}
@@ -372,7 +372,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
return err
}
- for _, uuid := range delAttachmentUUIDs.Values() {
+ for uuid := range delAttachmentUUIDs.Seq() {
if err := storage.Attachments.Delete(repo_model.AttachmentRelativePath(uuid)); err != nil {
// Even delete files failed, but the attachments has been removed from database, so we
// should not return error but only record the error on logs.
diff --git a/services/release/release_test.go b/services/release/release_test.go
index 5a22c473cf..f03b4d42b8 100644
--- a/services/release/release_test.go
+++ b/services/release/release_test.go
@@ -6,18 +6,18 @@ package release
import (
"strings"
"testing"
- "time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/services/attachment"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/test"
+ "forgejo.org/services/attachment"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -138,9 +138,9 @@ func TestRelease_Create(t *testing.T) {
}))
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, &release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
- assert.EqualValues(t, attach.Name, release.Attachments[0].Name)
- assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
+ assert.Equal(t, attach.UUID, release.Attachments[0].UUID)
+ assert.Equal(t, attach.Name, release.Attachments[0].Name)
+ assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
release = repo_model.Release{
RepoID: repo.ID,
@@ -165,8 +165,8 @@ func TestRelease_Create(t *testing.T) {
}))
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, &release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, "test", release.Attachments[0].Name)
- assert.EqualValues(t, "https://forgejo.org/", release.Attachments[0].ExternalURL)
+ assert.Equal(t, "test", release.Attachments[0].Name)
+ assert.Equal(t, "https://forgejo.org/", release.Attachments[0].ExternalURL)
release = repo_model.Release{
RepoID: repo.ID,
@@ -219,7 +219,7 @@ func TestRelease_Update(t *testing.T) {
release, err := repo_model.GetRelease(db.DefaultContext, repo.ID, "v1.1.1")
require.NoError(t, err)
releaseCreatedUnix := release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Note = "Changed note"
require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{}))
release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
@@ -243,7 +243,7 @@ func TestRelease_Update(t *testing.T) {
release, err = repo_model.GetRelease(db.DefaultContext, repo.ID, "v1.2.1")
require.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Title = "Changed title"
require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{}))
release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
@@ -267,7 +267,7 @@ func TestRelease_Update(t *testing.T) {
release, err = repo_model.GetRelease(db.DefaultContext, repo.ID, "v1.3.1")
require.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Title = "Changed title"
release.Note = "Changed note"
require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{}))
@@ -318,10 +318,10 @@ func TestRelease_Update(t *testing.T) {
}))
require.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, attach.Name, release.Attachments[0].Name)
- assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
+ assert.Equal(t, attach.UUID, release.Attachments[0].UUID)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, attach.Name, release.Attachments[0].Name)
+ assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
// update the attachment name
require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{
@@ -334,10 +334,10 @@ func TestRelease_Update(t *testing.T) {
release.Attachments = nil
require.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, "test2.txt", release.Attachments[0].Name)
- assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
+ assert.Equal(t, attach.UUID, release.Attachments[0].UUID)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, "test2.txt", release.Attachments[0].Name)
+ assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL)
// delete the attachment
require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{
@@ -361,9 +361,9 @@ func TestRelease_Update(t *testing.T) {
}))
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, "test", release.Attachments[0].Name)
- assert.EqualValues(t, "https://forgejo.org/", release.Attachments[0].ExternalURL)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, "test", release.Attachments[0].Name)
+ assert.Equal(t, "https://forgejo.org/", release.Attachments[0].ExternalURL)
externalAttachmentUUID := release.Attachments[0].UUID
// update the attachment name
@@ -378,10 +378,10 @@ func TestRelease_Update(t *testing.T) {
release.Attachments = nil
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, externalAttachmentUUID, release.Attachments[0].UUID)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, "test2", release.Attachments[0].Name)
- assert.EqualValues(t, "https://about.gitea.com/", release.Attachments[0].ExternalURL)
+ assert.Equal(t, externalAttachmentUUID, release.Attachments[0].UUID)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, "test2", release.Attachments[0].Name)
+ assert.Equal(t, "https://about.gitea.com/", release.Attachments[0].ExternalURL)
}
func TestRelease_createTag(t *testing.T) {
@@ -412,7 +412,7 @@ func TestRelease_createTag(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, release.CreatedUnix)
releaseCreatedUnix := release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Note = "Changed note"
_, err = createTag(db.DefaultContext, gitRepo, release, "")
require.NoError(t, err)
@@ -435,7 +435,7 @@ func TestRelease_createTag(t *testing.T) {
_, err = createTag(db.DefaultContext, gitRepo, release, "")
require.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Title = "Changed title"
_, err = createTag(db.DefaultContext, gitRepo, release, "")
require.NoError(t, err)
@@ -458,7 +458,7 @@ func TestRelease_createTag(t *testing.T) {
_, err = createTag(db.DefaultContext, gitRepo, release, "")
require.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
- time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
+ test.SleepTillNextSecond()
release.Title = "Changed title"
release.Note = "Changed note"
_, err = createTag(db.DefaultContext, gitRepo, release, "")
diff --git a/services/release/tag.go b/services/release/tag.go
index dae2b70f76..e1608d1897 100644
--- a/services/release/tag.go
+++ b/services/release/tag.go
@@ -8,12 +8,12 @@ import (
"errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
- repo_module "code.gitea.io/gitea/modules/repository"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
+ repo_module "forgejo.org/modules/repository"
"xorm.io/builder"
)
diff --git a/services/remote/promote.go b/services/remote/promote.go
index eb41ace462..f37d00168c 100644
--- a/services/remote/promote.go
+++ b/services/remote/promote.go
@@ -6,12 +6,12 @@ package remote
import (
"context"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/services/auth/source/oauth2"
- remote_source "code.gitea.io/gitea/services/auth/source/remote"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/auth/source/oauth2"
+ remote_source "forgejo.org/services/auth/source/remote"
)
type Reason int
@@ -98,7 +98,7 @@ func getRemoteUserToPromote(ctx context.Context, source *auth_model.Source, logi
return nil, NewReason(log.ERROR, ReasonErrorLoginName, "getUserByLoginName('%s') %v", loginName, err), err
}
if len(users) == 0 {
- return nil, NewReason(log.ERROR, ReasonLoginNameNotExists, "no user with LoginType UserTypeRemoteUser and LoginName '%s'", loginName), nil
+ return nil, NewReason(log.DEBUG, ReasonLoginNameNotExists, "no user with LoginType UserTypeRemoteUser and LoginName '%s'", loginName), nil
}
reason := ReasonNoSource
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 3d6fe71a09..3651b018e6 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -11,19 +11,19 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
"github.com/gobwas/glob"
)
diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go
index 71fb1fc885..a66b4c5ac0 100644
--- a/services/repository/adopt_test.go
+++ b/services/repository/adopt_test.go
@@ -8,11 +8,11 @@ import (
"path"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go
index 279067c002..cffb07210f 100644
--- a/services/repository/archiver/archiver.go
+++ b/services/repository/archiver/archiver.go
@@ -12,16 +12,16 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
)
// ArchiveRequest defines the parameters of an archive request, which notably
diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go
index e7a2422e55..00d82267c9 100644
--- a/services/repository/archiver/archiver_test.go
+++ b/services/repository/archiver/archiver_test.go
@@ -7,13 +7,13 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
+ "forgejo.org/services/contexttest"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -36,7 +36,7 @@ func TestArchive_Basic(t *testing.T) {
bogusReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
require.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName())
+ assert.Equal(t, firstCommit+".zip", bogusReq.GetArchiveName())
// Check a series of bogus requests.
// Step 1, valid commit with a bad extension.
@@ -57,12 +57,12 @@ func TestArchive_Basic(t *testing.T) {
bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP)
require.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName())
+ assert.Equal(t, "master.zip", bogusReq.GetArchiveName())
bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP)
require.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName())
+ assert.Equal(t, "test-archive.zip", bogusReq.GetArchiveName())
// Now two valid requests, firstCommit with valid extensions.
zipReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
diff --git a/services/repository/avatar.go b/services/repository/avatar.go
index 32940a7aa3..a1cd3228df 100644
--- a/services/repository/avatar.go
+++ b/services/repository/avatar.go
@@ -9,11 +9,11 @@ import (
"io"
"strconv"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/avatar"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/avatar"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/storage"
)
// UploadAvatar saves custom avatar for repository.
diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go
index b3c498dfc8..6f28113286 100644
--- a/services/repository/avatar_test.go
+++ b/services/repository/avatar_test.go
@@ -9,10 +9,10 @@ import (
"image/png"
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/avatar"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/avatar"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -60,7 +60,7 @@ func TestDeleteAvatar(t *testing.T) {
err = DeleteAvatar(db.DefaultContext, repo)
require.NoError(t, err)
- assert.Equal(t, "", repo.Avatar)
+ assert.Empty(t, repo.Avatar)
}
func TestTemplateGenerateAvatar(t *testing.T) {
diff --git a/services/repository/branch.go b/services/repository/branch.go
index 8e1a6cd27f..bc739825a5 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -9,28 +9,29 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/queue"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
- files_service "code.gitea.io/gitea/services/repository/files"
+ "forgejo.org/models"
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/queue"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ actions_service "forgejo.org/services/actions"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
+ files_service "forgejo.org/services/repository/files"
"xorm.io/builder"
)
@@ -250,7 +251,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
// For other batches, it will hit optimization 4.
if len(branchNames) != len(commitIDs) {
- return fmt.Errorf("branchNames and commitIDs length not match")
+ return errors.New("branchNames and commitIDs length not match")
}
return db.WithTx(ctx, func(ctx context.Context) error {
@@ -377,7 +378,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
from,
@@ -578,7 +579,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
oldDefaultBranchName,
diff --git a/services/repository/cache.go b/services/repository/cache.go
index b0811a99fc..cd5b95afa3 100644
--- a/services/repository/cache.go
+++ b/services/repository/cache.go
@@ -6,9 +6,9 @@ package repository
import (
"context"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
)
// CacheRef cachhe last commit information of the branch or the tag
diff --git a/services/repository/check.go b/services/repository/check.go
index 5cdcc14679..7e680f3c58 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -9,14 +9,14 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/util"
"xorm.io/builder"
)
diff --git a/services/repository/collaboration.go b/services/repository/collaboration.go
index dccc124748..7a0d7edb7f 100644
--- a/services/repository/collaboration.go
+++ b/services/repository/collaboration.go
@@ -7,10 +7,10 @@ package repository
import (
"context"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
)
// DeleteCollaboration removes collaboration relation between the user and repository.
diff --git a/services/repository/collaboration_test.go b/services/repository/collaboration_test.go
index c087018be4..b27b91be23 100644
--- a/services/repository/collaboration_test.go
+++ b/services/repository/collaboration_test.go
@@ -6,9 +6,9 @@ package repository
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/require"
)
diff --git a/services/repository/commit.go b/services/repository/commit.go
index e8c0262ef4..0ff4ea701e 100644
--- a/services/repository/commit.go
+++ b/services/repository/commit.go
@@ -7,8 +7,8 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/modules/util"
- gitea_ctx "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/util"
+ gitea_ctx "forgejo.org/services/context"
)
type ContainedLinks struct { // TODO: better name?
diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 635b0b108e..03a62d0410 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -9,17 +9,17 @@ import (
"fmt"
"slices"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- shared_automerge "code.gitea.io/gitea/services/shared/automerge"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ shared_automerge "forgejo.org/services/shared/automerge"
)
func getCacheKey(repoID int64, brancheName string) string {
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
index 48871813bd..1805bd5960 100644
--- a/services/repository/contributors_graph.go
+++ b/services/repository/contributors_graph.go
@@ -14,16 +14,16 @@ import (
"sync"
"time"
- "code.gitea.io/gitea/models/avatars"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/avatars"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
"code.forgejo.org/go-chi/cache"
)
@@ -111,7 +111,7 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode
var cachedStats map[string]*ContributorData
return cachedStats, json.Unmarshal([]byte(v), &cachedStats)
default:
- return nil, fmt.Errorf("unexpected type in cache detected")
+ return nil, errors.New("unexpected type in cache detected")
}
}
diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go
index c62bef25a1..45af85272d 100644
--- a/services/repository/contributors_graph_test.go
+++ b/services/repository/contributors_graph_test.go
@@ -8,12 +8,12 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/test"
"code.forgejo.org/go-chi/cache"
"github.com/stretchr/testify/assert"
@@ -53,14 +53,14 @@ func TestRepository_ContributorsGraph(t *testing.T) {
keys = append(keys, k)
}
slices.Sort(keys)
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"ethantkoenig@gmail.com",
"jimmy.praet@telenet.be",
"jon@allspice.io",
"total", // generated summary
}, keys)
- assert.EqualValues(t, &ContributorData{
+ assert.Equal(t, &ContributorData{
Name: "Ethan Koenig",
AvatarLink: "/assets/img/avatar_default.png",
TotalCommits: 1,
@@ -73,7 +73,7 @@ func TestRepository_ContributorsGraph(t *testing.T) {
},
},
}, data["ethantkoenig@gmail.com"])
- assert.EqualValues(t, &ContributorData{
+ assert.Equal(t, &ContributorData{
Name: "Total",
AvatarLink: "",
TotalCommits: 3,
diff --git a/services/repository/create.go b/services/repository/create.go
index d092d02a1f..4491b12497 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -12,18 +12,18 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/options"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates/vars"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/options"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/templates/vars"
+ "forgejo.org/modules/util"
)
// CreateRepoOptions contains the create repository options
@@ -45,6 +45,7 @@ type CreateRepoOptions struct {
TrustModel repo_model.TrustModelType
MirrorInterval string
ObjectFormatName string
+ Website string
}
func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
@@ -238,6 +239,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
DefaultBranch: opts.DefaultBranch,
WikiBranch: setting.Repository.DefaultBranch,
ObjectFormatName: opts.ObjectFormatName,
+ Website: opts.Website,
}
var rollbackRepo *repo_model.Repository
diff --git a/services/repository/create_test.go b/services/repository/create_test.go
index 9cde285181..0a6c34b6fe 100644
--- a/services/repository/create_test.go
+++ b/services/repository/create_test.go
@@ -7,13 +7,13 @@ import (
"fmt"
"testing"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -147,3 +147,13 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
}
require.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
}
+
+func TestCreateRepository(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+ r, err := CreateRepositoryDirectly(db.DefaultContext, user, user, CreateRepoOptions{Name: "repo-last"})
+ require.NoError(t, err)
+ require.NotNil(t, r.Topics)
+ require.Empty(t, r.Topics)
+}
diff --git a/services/repository/delete.go b/services/repository/delete.go
index 6e84194750..f4124fb9e2 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -1,4 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
@@ -7,27 +8,29 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models"
- actions_model "code.gitea.io/gitea/models/actions"
- activities_model "code.gitea.io/gitea/models/activities"
- admin_model "code.gitea.io/gitea/models/admin"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
- repo_model "code.gitea.io/gitea/models/repo"
- secret_model "code.gitea.io/gitea/models/secret"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/models/webhook"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models"
+ actions_model "forgejo.org/models/actions"
+ activities_model "forgejo.org/models/activities"
+ admin_model "forgejo.org/models/admin"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ access_model "forgejo.org/models/perm/access"
+ project_model "forgejo.org/models/project"
+ repo_model "forgejo.org/models/repo"
+ secret_model "forgejo.org/models/secret"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/models/webhook"
+ actions_module "forgejo.org/modules/actions"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ federation_service "forgejo.org/services/federation"
"xorm.io/builder"
)
@@ -87,6 +90,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
}
}
+ // If the repository was reported as abusive, a shadow copy should be created before deletion.
+ if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, false); err != nil {
+ return err
+ }
+
if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil {
return err
} else if cnt != 1 {
@@ -289,6 +297,15 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
return err
}
+ if err := federation_service.DeleteFollowingRepos(ctx, repo.ID); err != nil {
+ return err
+ }
+
+ // unlink packages linked to this repository
+ if err = packages_model.UnlinkRepositoryFromAllPackages(ctx, repoID); err != nil {
+ return err
+ }
+
if err = committer.Commit(); err != nil {
return err
}
diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go
index 451a182155..0e88a29230 100644
--- a/services/repository/files/cherry_pick.go
+++ b/services/repository/files/cherry_pick.go
@@ -5,16 +5,17 @@ package files
import (
"context"
+ "errors"
"fmt"
"strings"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/pull"
)
// CherryPick cherrypicks or reverts a commit to the given repository
@@ -79,21 +80,33 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
right, base = base, right
}
- description := fmt.Sprintf("CherryPick %s onto %s", right, opts.OldBranch)
- conflict, _, err := pull.AttemptThreeWayMerge(ctx,
- t.basePath, t.gitRepo, base, opts.LastCommitID, right, description)
- if err != nil {
- return nil, fmt.Errorf("failed to three-way merge %s onto %s: %w", right, opts.OldBranch, err)
- }
+ var treeHash string
+ if git.SupportGitMergeTree {
+ var conflict bool
+ treeHash, conflict, _, err = pull.MergeTree(ctx, t.gitRepo, base, opts.LastCommitID, right, nil)
+ if err != nil {
+ return nil, fmt.Errorf("failed to three-way merge %s onto %s: %w", right, opts.OldBranch, err)
+ }
- if conflict {
- return nil, fmt.Errorf("failed to merge due to conflicts")
- }
+ if conflict {
+ return nil, errors.New("failed to merge due to conflicts")
+ }
+ } else {
+ description := fmt.Sprintf("CherryPick %s onto %s", right, opts.OldBranch)
+ conflict, _, err := pull.AttemptThreeWayMerge(ctx, t.gitRepo, base, opts.LastCommitID, right, description)
+ if err != nil {
+ return nil, fmt.Errorf("failed to three-way merge %s onto %s: %w", right, opts.OldBranch, err)
+ }
- treeHash, err := t.WriteTree()
- if err != nil {
- // likely non-sensical tree due to merge conflicts...
- return nil, err
+ if conflict {
+ return nil, errors.New("failed to merge due to conflicts")
+ }
+
+ treeHash, err = t.WriteTree()
+ if err != nil {
+ // likely non-sensical tree due to merge conflicts...
+ return nil, err
+ }
}
// Now commit the tree
diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go
index e0dad29273..43c9048aaf 100644
--- a/services/repository/files/commit.go
+++ b/services/repository/files/commit.go
@@ -1,4 +1,5 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package files
@@ -6,15 +7,15 @@ package files
import (
"context"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/structs"
+ asymkey_model "forgejo.org/models/asymkey"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/structs"
)
// CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch
func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, branch string) (*git.DivergeObject, error) {
- divergence, err := git.GetDivergingCommits(ctx, repo.RepoPath(), repo.DefaultBranch, branch)
+ divergence, err := git.GetDivergingCommits(ctx, repo.RepoPath(), repo.DefaultBranch, branch, nil)
if err != nil {
return nil, err
}
@@ -38,7 +39,7 @@ func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *stru
verification.Verified = commitVerification.Verified
verification.Reason = commitVerification.Reason
if verification.Reason == "" && !verification.Verified {
- verification.Reason = "gpg.error.not_signed_commit"
+ verification.Reason = asymkey_model.NotSigned
}
return verification
}
diff --git a/services/repository/files/content.go b/services/repository/files/content.go
index 32517e8d91..5a6006e9f2 100644
--- a/services/repository/files/content.go
+++ b/services/repository/files/content.go
@@ -5,18 +5,19 @@ package files
import (
"context"
+ "errors"
"fmt"
"net/url"
"path"
"strings"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
)
// ContentType repo content type
@@ -178,12 +179,13 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
// All content types have these fields in populated
contentsResponse := &api.ContentsResponse{
- Name: entry.Name(),
- Path: treePath,
- SHA: entry.ID.String(),
- LastCommitSHA: lastCommit.ID.String(),
- Size: entry.Size(),
- URL: &selfURLString,
+ Name: entry.Name(),
+ Path: treePath,
+ SHA: entry.ID.String(),
+ LastCommitSHA: lastCommit.ID.String(),
+ LastCommitWhen: lastCommit.Committer.When,
+ Size: entry.Size(),
+ URL: &selfURLString,
Links: &api.FileLinksResponse{
Self: &selfURLString,
},
@@ -204,7 +206,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
} else if entry.IsLink() {
contentsResponse.Type = string(ContentTypeLink)
// The target of a symlink file is the content of the file
- targetFromContent, err := entry.Blob().GetBlobContent(1024)
+ targetFromContent, err := entry.LinkTarget()
if err != nil {
return nil, err
}
@@ -249,20 +251,35 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
return contentsResponse, nil
}
-// GetBlobBySHA get the GitBlobResponse of a repository using a sha hash.
-func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) {
+// GetBlobsBySHA gets multiple GitBlobs of a repository by sha hash.
+func GetBlobsBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, shas []string) ([]*api.GitBlob, error) {
+ if len(shas) > setting.API.MaxResponseItems {
+ shas = shas[:setting.API.MaxResponseItems]
+ }
+
+ blobs := make([]*api.GitBlob, 0, len(shas))
+ for _, sha := range shas {
+ blob, err := GetBlobBySHA(ctx, repo, gitRepo, sha)
+ if err != nil {
+ return nil, err
+ }
+ blobs = append(blobs, blob)
+ }
+ return blobs, nil
+}
+
+// GetBlobBySHA get the GitBlob of a repository using a sha hash.
+func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlob, error) {
gitBlob, err := gitRepo.GetBlob(sha)
if err != nil {
return nil, err
}
- content := ""
- if gitBlob.Size() <= setting.API.DefaultMaxBlobSize {
- content, err = gitBlob.GetBlobContentBase64()
- if err != nil {
- return nil, err
- }
+ content, err := gitBlob.GetContentBase64(setting.API.DefaultMaxBlobSize)
+ if err != nil && !errors.As(err, &git.BlobTooLargeError{}) {
+ return nil, err
}
- return &api.GitBlobResponse{
+
+ return &api.GitBlob{
SHA: gitBlob.ID.String(),
URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()),
Size: gitBlob.Size(),
diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go
index f5e2b84690..8fc8f56b4f 100644
--- a/services/repository/files/content_test.go
+++ b/services/repository/files/content_test.go
@@ -5,15 +5,16 @@ package files
import (
"testing"
+ "time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/gitrepo"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/gitrepo"
+ api "forgejo.org/modules/structs"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -33,18 +34,19 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse {
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
return &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
- LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: treePath,
+ Path: treePath,
+ SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
+ LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+ LastCommitWhen: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
+ Type: "file",
+ Size: 30,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -64,13 +66,13 @@ func TestGetContents(t *testing.T) {
t.Run("Get README.md contents with GetContents(ctx, )", func(t *testing.T) {
fileContentResponse, err := GetContents(db.DefaultContext, repo, treePath, ref, false)
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
+ assert.Equal(t, expectedContentsResponse, fileContentResponse)
require.NoError(t, err)
})
t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents(ctx, )", func(t *testing.T) {
fileContentResponse, err := GetContents(db.DefaultContext, repo, treePath, "", false)
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
+ assert.Equal(t, expectedContentsResponse, fileContentResponse)
require.NoError(t, err)
})
}
@@ -190,7 +192,7 @@ func TestGetBlobBySHA(t *testing.T) {
defer gitRepo.Close()
gbr, err := GetBlobBySHA(db.DefaultContext, repo, gitRepo, "65f1bf27bc3bf70f64657658635e66094edbcb4d")
- expectedGBR := &api.GitBlobResponse{
+ expectedGBR := &api.GitBlob{
Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK",
Encoding: "base64",
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d",
@@ -200,3 +202,43 @@ func TestGetBlobBySHA(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, expectedGBR, gbr)
}
+
+func TestGetBlobsBySHA(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+
+ gitRepo, err := gitrepo.OpenRepository(db.DefaultContext, repo)
+ require.NoError(t, err)
+ defer gitRepo.Close()
+
+ gbr, err := GetBlobsBySHA(db.DefaultContext, repo, gitRepo, []string{
+ "ea82fc8777a24b07c26b3a4bf4e2742c03733eab", // Home.md
+ "6395b68e1feebb1e4c657b4f9f6ba2676a283c0b", // line.svg
+ "26f842bcad37fa40a1bb34cbb5ee219ee35d863d", // test.xml
+ })
+ expectedGBR := []*api.GitBlob{
+ {
+ Content: "IyBIb21lIHBhZ2UKClRoaXMgaXMgdGhlIGhvbWUgcGFnZSEK",
+ Encoding: "base64",
+ URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/ea82fc8777a24b07c26b3a4bf4e2742c03733eab",
+ SHA: "ea82fc8777a24b07c26b3a4bf4e2742c03733eab",
+ Size: 36,
+ },
+ {
+ Content: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZwogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHdpZHRoPSIxMjgiCiAgIGhlaWdodD0iMTI4IgogICB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+CgogIDxsaW5lIHgxPSIwIiB5MT0iNyIgeDI9IjEwIiB5Mj0iNyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPC9zdmc+",
+ Encoding: "base64",
+ URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b",
+ SHA: "6395b68e1feebb1e4c657b4f9f6ba2676a283c0b",
+ Size: 246,
+ },
+ {
+ Content: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHRlc3Q+VGhpcyBpcyBYTUw8L3Rlc3Q+Cg==",
+ Encoding: "base64",
+ URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/26f842bcad37fa40a1bb34cbb5ee219ee35d863d",
+ SHA: "26f842bcad37fa40a1bb34cbb5ee219ee35d863d",
+ Size: 64,
+ },
+ }
+ require.NoError(t, err)
+ assert.Equal(t, expectedGBR, gbr)
+}
diff --git a/services/repository/files/diff.go b/services/repository/files/diff.go
index bf8b938e21..354a343d12 100644
--- a/services/repository/files/diff.go
+++ b/services/repository/files/diff.go
@@ -7,8 +7,8 @@ import (
"context"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/services/gitdiff"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/services/gitdiff"
)
// GetDiffPreview produces and returns diff result of a file which is not yet committed.
diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go
index 95de10e07e..67c63803d3 100644
--- a/services/repository/files/diff_test.go
+++ b/services/repository/files/diff_test.go
@@ -6,12 +6,12 @@ package files
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/services/contexttest"
- "code.gitea.io/gitea/services/gitdiff"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/json"
+ "forgejo.org/services/contexttest"
+ "forgejo.org/services/gitdiff"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -124,7 +124,7 @@ func TestGetDiffPreview(t *testing.T) {
require.NoError(t, err)
bs, err := json.Marshal(diff)
require.NoError(t, err)
- assert.EqualValues(t, string(expectedBs), string(bs))
+ assert.Equal(t, string(expectedBs), string(bs))
})
t.Run("empty branch, same results", func(t *testing.T) {
@@ -134,7 +134,7 @@ func TestGetDiffPreview(t *testing.T) {
require.NoError(t, err)
bs, err := json.Marshal(diff)
require.NoError(t, err)
- assert.EqualValues(t, expectedBs, bs)
+ assert.Equal(t, expectedBs, bs)
})
}
diff --git a/services/repository/files/file.go b/services/repository/files/file.go
index 852cca0371..5b93258840 100644
--- a/services/repository/files/file.go
+++ b/services/repository/files/file.go
@@ -5,16 +5,16 @@ package files
import (
"context"
- "fmt"
+ "errors"
"net/url"
"strings"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
)
func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch string, treeNames []string) (*api.FilesResponse, error) {
@@ -33,19 +33,6 @@ func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository
return filesResponse, nil
}
-// GetFileResponseFromCommit Constructs a FileResponse from a Commit object
-func GetFileResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) {
- fileContents, _ := GetContents(ctx, repo, treeName, branch, false) // ok if fails, then will be nil
- fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil
- verification := GetPayloadCommitVerification(ctx, commit)
- fileResponse := &api.FileResponse{
- Content: fileContents,
- Commit: fileCommitResponse,
- Verification: verification,
- }
- return fileResponse, nil
-}
-
// constructs a FileResponse with the file at the index from FilesResponse
func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index int) *api.FileResponse {
content := &api.ContentsResponse{}
@@ -63,10 +50,10 @@ func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index in
// GetFileCommitResponse Constructs a FileCommitResponse from a Commit object
func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*api.FileCommitResponse, error) {
if repo == nil {
- return nil, fmt.Errorf("repo cannot be nil")
+ return nil, errors.New("repo cannot be nil")
}
if commit == nil {
- return nil, fmt.Errorf("commit cannot be nil")
+ return nil, errors.New("commit cannot be nil")
}
commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()))
commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String()))
@@ -117,36 +104,35 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_m
// then we use bogus User objects for them to store their FullName and Email.
// If only one of the two are provided, we set both of them to it.
// If neither are provided, both are the doer.
- if committer != nil && committer.Email != "" {
- if doer != nil && strings.EqualFold(doer.Email, committer.Email) {
- committerUser = doer // the committer is the doer, so will use their user object
- if committer.Name != "" {
- committerUser.FullName = committer.Name
+ getUser := func(identity *IdentityOptions) *user_model.User {
+ if identity == nil || identity.Email == "" {
+ return nil
+ }
+
+ if doer != nil && strings.EqualFold(doer.Email, identity.Email) {
+ user := doer // the committer is the doer, so will use their user object
+ if identity.Name != "" {
+ user.FullName = identity.Name
}
// Use the provided email and not revert to placeholder mail.
- committerUser.KeepEmailPrivate = false
- } else {
- committerUser = &user_model.User{
- FullName: committer.Name,
- Email: committer.Email,
- }
- }
- }
- if author != nil && author.Email != "" {
- if doer != nil && strings.EqualFold(doer.Email, author.Email) {
- authorUser = doer // the author is the doer, so will use their user object
- if authorUser.Name != "" {
- authorUser.FullName = author.Name
- }
- // Use the provided email and not revert to placeholder mail.
- authorUser.KeepEmailPrivate = false
- } else {
- authorUser = &user_model.User{
- FullName: author.Name,
- Email: author.Email,
- }
+ user.KeepEmailPrivate = false
+ return user
+ }
+
+ var id int64
+ if doer != nil {
+ id = doer.ID
+ }
+ return &user_model.User{
+ ID: id, // Needed to ensure the doer is checked to pass rules for instance signing of CRUD actions.
+ FullName: identity.Name,
+ Email: identity.Email,
}
}
+
+ committerUser = getUser(committer)
+ authorUser = getUser(author)
+
if authorUser == nil {
if committerUser != nil {
authorUser = committerUser // No valid author was given so use the committer
diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go
index 7c387e2dd5..169cafba0d 100644
--- a/services/repository/files/file_test.go
+++ b/services/repository/files/file_test.go
@@ -6,15 +6,7 @@ package files
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
-
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestCleanUploadFileName(t *testing.T) {
@@ -22,94 +14,13 @@ func TestCleanUploadFileName(t *testing.T) {
name := "this/is/test"
cleanName := CleanUploadFileName(name)
expectedCleanName := name
- assert.EqualValues(t, expectedCleanName, cleanName)
+ assert.Equal(t, expectedCleanName, cleanName)
})
t.Run("Clean a .git path", func(t *testing.T) {
name := "this/is/test/.git"
cleanName := CleanUploadFileName(name)
expectedCleanName := ""
- assert.EqualValues(t, expectedCleanName, cleanName)
+ assert.Equal(t, expectedCleanName, cleanName)
})
}
-
-func getExpectedFileResponse() *api.FileResponse {
- treePath := "README.md"
- sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
- encoding := "base64"
- content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
- selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
- htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
- gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
- downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
- return &api.FileResponse{
- Content: &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: sha,
- LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
- Links: &api.FileLinksResponse{
- Self: &selfURL,
- GitURL: &gitURL,
- HTMLURL: &htmlURL,
- },
- },
- Commit: &api.FileCommitResponse{
- CommitMeta: api.CommitMeta{
- URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
- SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- },
- HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Author: &api.CommitUser{
- Identity: api.Identity{
- Name: "user1",
- Email: "address1@example.com",
- },
- Date: "2017-03-19T20:47:59Z",
- },
- Committer: &api.CommitUser{
- Identity: api.Identity{
- Name: "Ethan Koenig",
- Email: "ethantkoenig@gmail.com",
- },
- Date: "2017-03-19T20:47:59Z",
- },
- Parents: []*api.CommitMeta{},
- Message: "Initial commit\n",
- Tree: &api.CommitMeta{
- URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6",
- SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6",
- },
- },
- Verification: &api.PayloadCommitVerification{
- Verified: false,
- Reason: "gpg.error.not_signed_commit",
- Signature: "",
- Payload: "",
- },
- }
-}
-
-func TestGetFileResponseFromCommit(t *testing.T) {
- unittest.PrepareTestEnv(t)
-
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- branch := repo.DefaultBranch
- treePath := "README.md"
- gitRepo, _ := gitrepo.OpenRepository(db.DefaultContext, repo)
- defer gitRepo.Close()
- commit, _ := gitRepo.GetBranchCommit(branch)
- expectedFileResponse := getExpectedFileResponse()
-
- fileResponse, err := GetFileResponseFromCommit(db.DefaultContext, repo, commit, branch, treePath)
- require.NoError(t, err)
- assert.EqualValues(t, expectedFileResponse, fileResponse)
-}
diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go
index e5f7e2af96..18b5226c02 100644
--- a/services/repository/files/patch.go
+++ b/services/repository/files/patch.go
@@ -8,15 +8,15 @@ import (
"fmt"
"strings"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
+ asymkey_service "forgejo.org/services/asymkey"
)
// ApplyDiffPatchOptions holds the repository diff patch update options
@@ -147,11 +147,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
stdout := &strings.Builder{}
stderr := &strings.Builder{}
- cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
- if git.CheckGitVersionAtLeast("2.32") == nil {
- cmdApply.AddArguments("-3")
- }
-
+ cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary", "-3")
if err := cmdApply.Run(&git.RunOpts{
Dir: t.basePath,
Stdout: stdout,
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 6e7570b82c..64d3e5887d 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -6,6 +6,7 @@ package files
import (
"bytes"
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -13,15 +14,15 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- "code.gitea.io/gitea/services/gitdiff"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ asymkey_service "forgejo.org/services/asymkey"
+ "forgejo.org/services/gitdiff"
)
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
@@ -212,24 +213,6 @@ func (t *TemporaryUploadRepository) WriteTree() (string, error) {
return strings.TrimSpace(stdout), nil
}
-// GetLastCommit gets the last commit ID SHA of the repo
-func (t *TemporaryUploadRepository) GetLastCommit() (string, error) {
- return t.GetLastCommitByRef("HEAD")
-}
-
-// GetLastCommitByRef gets the last commit ID SHA of the repo by ref
-func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, error) {
- if ref == "" {
- ref = "HEAD"
- }
- stdout, _, err := git.NewCommand(t.ctx, "rev-parse").AddDynamicArguments(ref).RunStdString(&git.RunOpts{Dir: t.basePath})
- if err != nil {
- log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err)
- return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err)
- }
- return strings.TrimSpace(stdout), nil
-}
-
// CommitTree creates a commit from a given tree for the user with provided message
func (t *TemporaryUploadRepository) CommitTree(parent string, author, committer *user_model.User, treeHash, message string, signoff bool) (string, error) {
return t.CommitTreeWithDate(parent, author, committer, treeHash, message, signoff, time.Now(), time.Now())
@@ -386,7 +369,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
// GetBranchCommit Gets the commit object of the given branch
func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit, error) {
if t.gitRepo == nil {
- return nil, fmt.Errorf("repository has not been cloned")
+ return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetBranchCommit(branch)
}
@@ -394,7 +377,7 @@ func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit,
// GetCommit Gets the commit object of the given commit ID
func (t *TemporaryUploadRepository) GetCommit(commitID string) (*git.Commit, error) {
if t.gitRepo == nil {
- return nil, fmt.Errorf("repository has not been cloned")
+ return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetCommit(commitID)
}
diff --git a/services/repository/files/temp_repo_test.go b/services/repository/files/temp_repo_test.go
index e7d85ea3cc..852e762267 100644
--- a/services/repository/files/temp_repo_test.go
+++ b/services/repository/files/temp_repo_test.go
@@ -6,10 +6,10 @@ package files
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/git"
"github.com/stretchr/testify/require"
)
diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go
index e3a7f3b8b0..1e575f95e8 100644
--- a/services/repository/files/tree.go
+++ b/services/repository/files/tree.go
@@ -8,11 +8,11 @@ import (
"fmt"
"net/url"
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
)
// GetTreeBySHA get the GitTreeResponse of a repository using a sha hash.
diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go
index 9e5c5c1701..5cd628722b 100644
--- a/services/repository/files/tree_test.go
+++ b/services/repository/files/tree_test.go
@@ -6,9 +6,9 @@ package files
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/contexttest"
+ "forgejo.org/models/unittest"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/services/contexttest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -48,5 +48,5 @@ func TestGetTreeBySHA(t *testing.T) {
TotalCount: 1,
}
- assert.EqualValues(t, expectedTree, tree)
+ assert.Equal(t, expectedTree, tree)
}
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index d6025b6ced..8fb9644fa4 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -11,17 +11,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "forgejo.org/models"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ asymkey_service "forgejo.org/services/asymkey"
)
// IdentityOptions for a person's identity like an author or committer
@@ -193,28 +193,34 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
if hasOldBranch {
- // Get the commit of the original branch
- commit, err := t.GetBranchCommit(opts.OldBranch)
+ // Get the current commit of the original branch
+ actualBaseCommit, err := t.GetBranchCommit(opts.OldBranch)
if err != nil {
return nil, err // Couldn't get a commit for the branch
}
- // Assigned LastCommitID in opts if it hasn't been set
- if opts.LastCommitID == "" {
- opts.LastCommitID = commit.ID.String()
- } else {
- lastCommitID, err := t.gitRepo.ConvertToGitID(opts.LastCommitID)
+ var lastKnownCommit git.ObjectID // when nil, the sha provided in the opts.Files must match the current blob-sha
+ if opts.OldBranch != opts.NewBranch {
+ // when creating a new branch, ignore if a file has been changed in the meantime
+ // (such changes will visible when doing the merge)
+ lastKnownCommit = actualBaseCommit.ID
+ } else if opts.LastCommitID != "" {
+ lastKnownCommit, err = t.gitRepo.ConvertToGitID(opts.LastCommitID)
if err != nil {
return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %w", err)
}
- opts.LastCommitID = lastCommitID.String()
}
for _, file := range opts.Files {
- if err := handleCheckErrors(file, commit, opts); err != nil {
+ if err := handleCheckErrors(file, actualBaseCommit, lastKnownCommit); err != nil {
return nil, err
}
}
+
+ if opts.LastCommitID == "" {
+ // needed for t.CommitTree
+ opts.LastCommitID = actualBaseCommit.ID.String()
+ }
}
contentStore := lfs.NewContentStore()
@@ -277,9 +283,9 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
// handles the check for various issues for ChangeRepoFiles
-func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error {
+func handleCheckErrors(file *ChangeRepoFile, actualBaseCommit *git.Commit, lastKnownCommit git.ObjectID) error {
if file.Operation == "update" || file.Operation == "delete" {
- fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath)
+ fromEntry, err := actualBaseCommit.GetTreeEntryByPath(file.Options.fromTreePath)
if err != nil {
return err
}
@@ -292,22 +298,22 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
CurrentSHA: fromEntry.ID.String(),
}
}
- } else if opts.LastCommitID != "" {
- // If a lastCommitID was given and it doesn't match the commitID of the head of the branch throw
- // an error, but only if we aren't creating a new branch.
- if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch {
- if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil {
+ } else if lastKnownCommit != nil {
+ if actualBaseCommit.ID.String() != lastKnownCommit.String() {
+ // If a lastKnownCommit was given and it doesn't match the actualBaseCommit,
+ // check if the file has been changed in between
+ if changed, err := actualBaseCommit.FileChangedSinceCommit(file.Options.treePath, lastKnownCommit.String()); err != nil {
return err
} else if changed {
return models.ErrCommitIDDoesNotMatch{
- GivenCommitID: opts.LastCommitID,
- CurrentCommitID: opts.LastCommitID,
+ GivenCommitID: lastKnownCommit.String(),
+ CurrentCommitID: actualBaseCommit.ID.String(),
}
}
- // The file wasn't modified, so we are good to delete it
+ // The file wasn't modified, so we are good to update it
}
} else {
- // When updating a file, a lastCommitID or SHA needs to be given to make sure other commits
+ // When updating a file, a lastKnownCommit or SHA needs to be given to make sure other commits
// haven't been made. We throw an error if one wasn't provided.
return models.ErrSHAOrCommitIDNotProvided{}
}
@@ -322,7 +328,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
subTreePath := ""
for index, part := range treePathParts {
subTreePath = path.Join(subTreePath, part)
- entry, err := commit.GetTreeEntryByPath(subTreePath)
+ entry, err := actualBaseCommit.GetTreeEntryByPath(subTreePath)
if err != nil {
if git.IsErrNotExist(err) {
// Means there is no item with that name, so we're good
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index 1330116889..6359087e88 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -10,12 +10,12 @@ import (
"path"
"strings"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/setting"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/setting"
)
// UploadRepoFileOptions contains the uploaded repository file options
diff --git a/services/repository/fork.go b/services/repository/fork.go
index 0378f7bae6..9d15b6207d 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -9,17 +9,17 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
)
// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 2e1e72aaad..6de241e4d4 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -6,11 +6,12 @@ package repository
import (
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -36,7 +37,7 @@ func TestForkRepository(t *testing.T) {
assert.False(t, repo_model.IsErrReachLimitOfRepo(err))
// change AllowForkWithoutMaximumLimit to false for the test
- setting.Repository.AllowForkWithoutMaximumLimit = false
+ defer test.MockVariableValue(&setting.Repository.AllowForkWithoutMaximumLimit, false)()
// user has reached maximum limit of repositories
user.MaxRepoCreation = 0
fork2, err := ForkRepositoryAndUpdates(git.DefaultContext, user, user, ForkRepoOptions{
diff --git a/services/repository/generate.go b/services/repository/generate.go
index 4a312a33c3..e23e294de1 100644
--- a/services/repository/generate.go
+++ b/services/repository/generate.go
@@ -16,14 +16,14 @@ import (
"strings"
"time"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/util"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/util"
"github.com/gobwas/glob"
"github.com/huandu/xstrings"
@@ -43,12 +43,8 @@ type expansion struct {
var defaultTransformers = []transformer{
{Name: "SNAKE", Transform: xstrings.ToSnakeCase},
{Name: "KEBAB", Transform: xstrings.ToKebabCase},
- // as of xstrings v1.5.0 the CAMEL & PASCAL workarounds are no longer necessary
- // and can be removed https://codeberg.org/forgejo/forgejo/pulls/4050
- {Name: "CAMEL", Transform: func(str string) string {
- return xstrings.FirstRuneToLower(xstrings.ToCamelCase(str))
- }},
- {Name: "PASCAL", Transform: xstrings.ToCamelCase},
+ {Name: "CAMEL", Transform: xstrings.ToCamelCase},
+ {Name: "PASCAL", Transform: xstrings.ToPascalCase},
{Name: "LOWER", Transform: strings.ToLower},
{Name: "UPPER", Transform: strings.ToUpper},
{Name: "TITLE", Transform: util.ToTitleCase},
diff --git a/services/repository/generate_test.go b/services/repository/generate_test.go
index b0f97d0ffb..2eb3a55e96 100644
--- a/services/repository/generate_test.go
+++ b/services/repository/generate_test.go
@@ -65,3 +65,30 @@ func TestFileNameSanitize(t *testing.T) {
assert.Equal(t, "_", fileNameSanitize("\u0000"))
assert.Equal(t, "目标", fileNameSanitize("目标"))
}
+
+func TestTransformers(t *testing.T) {
+ input := "Foo_Forgejo-BAR"
+
+ tests := []struct {
+ name string
+ expected string
+ }{
+ {"SNAKE", "foo_forgejo_bar"},
+ {"KEBAB", "foo-forgejo-bar"},
+ {"CAMEL", "fooForgejoBar"},
+ {"PASCAL", "FooForgejoBar"},
+ {"LOWER", "foo_forgejo-bar"},
+ {"UPPER", "FOO_FORGEJO-BAR"},
+ {"TITLE", "Foo_forgejo-Bar"},
+ }
+
+ for i, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tranform := defaultTransformers[i]
+ assert.Equal(t, tt.name, tranform.Name)
+
+ got := tranform.Transform(input)
+ assert.Equal(t, tt.expected, got)
+ })
+ }
+}
diff --git a/modules/gitgraph/graph.go b/services/repository/gitgraph/graph.go
similarity index 97%
rename from modules/gitgraph/graph.go
rename to services/repository/gitgraph/graph.go
index 4db5598015..bf15baed2a 100644
--- a/modules/gitgraph/graph.go
+++ b/services/repository/gitgraph/graph.go
@@ -10,8 +10,8 @@ import (
"os"
"strings"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/setting"
)
// GetCommitGraph return a list of commit (GraphItems) from all branches
diff --git a/modules/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go
similarity index 94%
rename from modules/gitgraph/graph_models.go
rename to services/repository/gitgraph/graph_models.go
index 8ff1a6e916..20107cc646 100644
--- a/modules/gitgraph/graph_models.go
+++ b/services/repository/gitgraph/graph_models.go
@@ -10,13 +10,13 @@ import (
"strings"
"time"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
)
// NewGraph creates a basic graph
@@ -199,7 +199,7 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
if len(data) < 5 {
return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line))
}
- // Format is a slight modifcation from RFC1123Z
+ // Format is a slight modification from RFC1123Z
t, err := time.Parse("Mon, _2 Jan 2006 15:04:05 -0700", string(data[2]))
if err != nil {
return nil, fmt.Errorf("could not parse date of commit: %w", err)
diff --git a/modules/gitgraph/graph_test.go b/services/repository/gitgraph/graph_test.go
similarity index 99%
rename from modules/gitgraph/graph_test.go
rename to services/repository/gitgraph/graph_test.go
index e7e437e42d..374341b276 100644
--- a/modules/gitgraph/graph_test.go
+++ b/services/repository/gitgraph/graph_test.go
@@ -9,7 +9,7 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/modules/git"
+ "forgejo.org/modules/git"
)
func BenchmarkGetCommitGraph(b *testing.B) {
diff --git a/modules/gitgraph/parser.go b/services/repository/gitgraph/parser.go
similarity index 100%
rename from modules/gitgraph/parser.go
rename to services/repository/gitgraph/parser.go
diff --git a/services/repository/hooks.go b/services/repository/hooks.go
index 97e9e290a3..d3021414cf 100644
--- a/services/repository/hooks.go
+++ b/services/repository/hooks.go
@@ -7,12 +7,12 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/webhook"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
"xorm.io/builder"
)
diff --git a/services/repository/init.go b/services/repository/init.go
index 817fa4abd7..525b322752 100644
--- a/services/repository/init.go
+++ b/services/repository/init.go
@@ -9,13 +9,13 @@ import (
"os"
"time"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ asymkey_service "forgejo.org/services/asymkey"
)
// initRepoCommit temporarily changes with work directory.
diff --git a/services/repository/lfs.go b/services/repository/lfs.go
index 4cd1110e55..2e090290a7 100644
--- a/services/repository/lfs.go
+++ b/services/repository/lfs.go
@@ -8,14 +8,14 @@ import (
"fmt"
"time"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
)
// GarbageCollectLFSMetaObjectsOptions provides options for GarbageCollectLFSMetaObjects function
@@ -76,7 +76,7 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R
err = git_model.IterateLFSMetaObjectsForRepo(ctx, repo.ID, func(ctx context.Context, metaObject *git_model.LFSMetaObject) error {
total++
- pointerSha := git.ComputeBlobHash(objectFormat, []byte(metaObject.Pointer.StringContent()))
+ pointerSha := git.ComputeBlobHash(objectFormat, []byte(metaObject.StringContent()))
if gitRepo.IsObjectExist(pointerSha.String()) {
return git_model.MarkLFSMetaObject(ctx, metaObject.ID)
diff --git a/services/repository/lfs_test.go b/services/repository/lfs_test.go
index a0c01dff8f..e38c38e29c 100644
--- a/services/repository/lfs_test.go
+++ b/services/repository/lfs_test.go
@@ -5,18 +5,17 @@ package repository_test
import (
"bytes"
- "context"
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ repo_service "forgejo.org/services/repository"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -41,7 +40,7 @@ func TestGarbageCollectLFSMetaObjects(t *testing.T) {
lfsOid := storeObjectInRepo(t, repo.ID, &lfsContent)
// gc
- err = repo_service.GarbageCollectLFSMetaObjects(context.Background(), repo_service.GarbageCollectLFSMetaObjectsOptions{
+ err = repo_service.GarbageCollectLFSMetaObjects(t.Context(), repo_service.GarbageCollectLFSMetaObjectsOptions{
AutoFix: true,
OlderThan: time.Now().Add(7 * 24 * time.Hour).Add(5 * 24 * time.Hour),
UpdatedLessRecentlyThan: time.Time{}, // ensure that the models/fixtures/lfs_meta_object.yml objects are considered as well
diff --git a/services/repository/main_test.go b/services/repository/main_test.go
index 7ad1540aee..942a805638 100644
--- a/services/repository/main_test.go
+++ b/services/repository/main_test.go
@@ -6,7 +6,7 @@ package repository
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
)
func TestMain(m *testing.M) {
diff --git a/services/repository/migrate.go b/services/repository/migrate.go
index 39ced04ae3..80f5d68231 100644
--- a/services/repository/migrate.go
+++ b/services/repository/migrate.go
@@ -8,22 +8,20 @@ import (
"errors"
"fmt"
"net/http"
- "strings"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/migration"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/lfs"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/migration"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
)
// MigrateRepositoryGitData starts migrating git related data after created migrating repository
@@ -98,7 +96,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
defer gitRepo.Close()
- branch, err := gitrepo.GetDefaultBranch(ctx, repo)
+ branch, err := gitRepo.GetHEADBranch()
if err != nil {
log.Warn("Failed to get the default branch of a migrated wiki repo: %v", err)
if err := util.RemoveAll(wikiPath); err != nil {
@@ -107,7 +105,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
return repo, err
}
- repo.WikiBranch = branch
+ repo.WikiBranch = branch.Name
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
return repo, err
@@ -253,10 +251,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error {
cmd := git.NewCommand(ctx, "remote", "rm", "origin")
// if the origin does not exist
- _, stderr, err := cmd.RunStdString(&git.RunOpts{
+ _, _, err := cmd.RunStdString(&git.RunOpts{
Dir: repoPath,
})
- if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
return nil
@@ -275,7 +273,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo
}
_, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err)
}
diff --git a/services/repository/push.go b/services/repository/push.go
index a8e7d0f3b6..eaedd80e1f 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -10,22 +10,23 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/queue"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- issue_service "code.gitea.io/gitea/services/issue"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/cache"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ issue_service "forgejo.org/services/issue"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
)
// pushQueue represents a queue to handle update pull request tests
@@ -65,7 +66,7 @@ func PushUpdates(opts []*repo_module.PushUpdateOptions) error {
for _, opt := range opts {
if opt.IsNewRef() && opt.IsDelRef() {
- return fmt.Errorf("Old and new revisions are both NULL")
+ return errors.New("Old and new revisions are both NULL")
}
}
@@ -133,23 +134,26 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
} else { // is new tag
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
- return fmt.Errorf("gitRepo.GetCommit(%s) in %s/%s[%d]: %w", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err)
+ // in case there is dirty data, for example, the "github.com/git/git" repository has tags pointing to non-existing commits
+ if !errors.Is(err, util.ErrNotExist) {
+ log.Error("Unable to get tag commit: gitRepo.GetCommit(%s) in %s/%s[%d]: %v", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err)
+ }
+ } else {
+ commits := repo_module.NewPushCommits()
+ commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
+ commits.CompareURL = repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), opts.NewCommitID)
+
+ notify_service.PushCommits(
+ ctx, pusher, repo,
+ &repo_module.PushUpdateOptions{
+ RefFullName: opts.RefFullName,
+ OldCommitID: objectFormat.EmptyObjectID().String(),
+ NewCommitID: opts.NewCommitID,
+ }, commits)
+
+ addTags = append(addTags, tagName)
+ notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
}
-
- commits := repo_module.NewPushCommits()
- commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
- commits.CompareURL = repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), opts.NewCommitID)
-
- notify_service.PushCommits(
- ctx, pusher, repo,
- &repo_module.PushUpdateOptions{
- RefFullName: opts.RefFullName,
- OldCommitID: objectFormat.EmptyObjectID().String(),
- NewCommitID: opts.NewCommitID,
- }, commits)
-
- addTags = append(addTags, tagName)
- notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
}
} else if opts.RefFullName.IsBranch() {
if pusher == nil || pusher.ID != opts.PusherID {
@@ -252,10 +256,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits.CompareURL = ""
}
- if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
- commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
- }
-
notify_service.PushCommits(ctx, pusher, repo, opts, commits)
// Cache for big repository
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 116e24151b..41f3a96dd1 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -6,25 +6,24 @@ package repository
import (
"context"
+ "errors"
"fmt"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- federation_service "code.gitea.io/gitea/services/federation"
- notify_service "code.gitea.io/gitea/services/notify"
- pull_service "code.gitea.io/gitea/services/pull"
+ "forgejo.org/models/db"
+ "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ notify_service "forgejo.org/services/notify"
+ pull_service "forgejo.org/services/pull"
)
// WebSearchRepository represents a repository returned by web search
@@ -64,15 +63,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod
notify_service.DeleteRepository(ctx, doer, repo)
}
- if err := DeleteRepositoryDirectly(ctx, doer, repo.ID); err != nil {
- return err
- }
-
- if err := federation_service.DeleteFollowingRepos(ctx, repo.ID); err != nil {
- return err
- }
-
- return packages_model.UnlinkRepositoryFromAllPackages(ctx, repo.ID)
+ return DeleteRepositoryDirectly(ctx, doer, repo.ID)
}
// PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace
@@ -82,10 +73,10 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN
if ok, err := organization.CanCreateOrgRepo(ctx, owner.ID, authUser.ID); err != nil {
return nil, err
} else if !ok {
- return nil, fmt.Errorf("cannot push-create repository for org")
+ return nil, errors.New("cannot push-create repository for org")
}
} else if authUser.ID != owner.ID {
- return nil, fmt.Errorf("cannot push-create repository for another user")
+ return nil, errors.New("cannot push-create repository for another user")
}
}
diff --git a/services/repository/repository_test.go b/services/repository/repository_test.go
index a5c0b3efcd..c08f7151ca 100644
--- a/services/repository/repository_test.go
+++ b/services/repository/repository_test.go
@@ -6,10 +6,10 @@ package repository
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/repository/review.go b/services/repository/review.go
index 40513e6bc6..c4000a2846 100644
--- a/services/repository/review.go
+++ b/services/repository/review.go
@@ -6,9 +6,9 @@ package repository
import (
"context"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ repo_model "forgejo.org/models/repo"
)
// GetReviewerTeams get all teams can be requested to review
diff --git a/services/repository/review_test.go b/services/repository/review_test.go
index eb1712c2ce..5ece99a2e3 100644
--- a/services/repository/review_test.go
+++ b/services/repository/review_test.go
@@ -6,9 +6,9 @@ package repository
import (
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/repository/setting.go b/services/repository/setting.go
index 33b00cca8c..68cdfc370b 100644
--- a/services/repository/setting.go
+++ b/services/repository/setting.go
@@ -7,12 +7,11 @@ import (
"context"
"slices"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
- actions_service "code.gitea.io/gitea/services/actions"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/modules/log"
+ actions_service "forgejo.org/services/actions"
)
// UpdateRepositoryUnits updates a repository's units
@@ -29,7 +28,7 @@ func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, uni
}
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo, true); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
}
diff --git a/services/repository/star.go b/services/repository/star.go
index 505da0f099..8cc2e0a243 100644
--- a/services/repository/star.go
+++ b/services/repository/star.go
@@ -6,10 +6,10 @@ package repository
import (
"context"
- "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/federation"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/services/federation"
)
func StarRepoAndSendLikeActivities(ctx context.Context, doer user.User, repoID int64, star bool) error {
diff --git a/services/repository/sync_fork.go b/services/repository/sync_fork.go
new file mode 100644
index 0000000000..ebcac76136
--- /dev/null
+++ b/services/repository/sync_fork.go
@@ -0,0 +1,113 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+ "context"
+ "fmt"
+ "slices"
+
+ git_model "forgejo.org/models/git"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ repo_module "forgejo.org/modules/repository"
+ api "forgejo.org/modules/structs"
+)
+
+// SyncFork syncs a branch of a fork with the base repo
+func SyncFork(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) error {
+ err := repo.MustNotBeArchived()
+ if err != nil {
+ return err
+ }
+
+ err = repo.GetBaseRepo(ctx)
+ if err != nil {
+ return err
+ }
+
+ err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{
+ Remote: repo.RepoPath(),
+ Branch: fmt.Sprintf("%s:%s", branch, branch),
+ Env: repo_module.PushingEnvironment(doer, repo),
+ })
+
+ return err
+}
+
+// CanSyncFork returns information about syncing a fork
+func GetSyncForkInfo(ctx context.Context, repo *repo_model.Repository, branch string) (*api.SyncForkInfo, error) {
+ info := new(api.SyncForkInfo)
+
+ if !repo.IsFork {
+ return info, nil
+ }
+
+ if repo.IsArchived {
+ return info, nil
+ }
+
+ err := repo.GetBaseRepo(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ forkBranch, err := git_model.GetBranch(ctx, repo.ID, branch)
+ if err != nil {
+ return nil, err
+ }
+
+ info.ForkCommit = forkBranch.CommitID
+
+ baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, branch)
+ if err != nil {
+ if git_model.IsErrBranchNotExist(err) {
+ // If the base repo don't have the branch, we don't need to continue
+ return info, nil
+ }
+ return nil, err
+ }
+
+ info.BaseCommit = baseBranch.CommitID
+
+ // If both branches has the same latest commit, we don't need to sync
+ if forkBranch.CommitID == baseBranch.CommitID {
+ return info, nil
+ }
+
+ // Check if the latest commit of the fork is also in the base
+ gitRepo, err := git.OpenRepository(ctx, repo.BaseRepo.RepoPath())
+ if err != nil {
+ return nil, err
+ }
+ defer gitRepo.Close()
+
+ commit, err := gitRepo.GetCommit(forkBranch.CommitID)
+ if err != nil {
+ if git.IsErrNotExist(err) {
+ return info, nil
+ }
+ return nil, err
+ }
+
+ branchList, err := commit.GetAllBranches()
+ if err != nil {
+ return nil, err
+ }
+
+ if !slices.Contains(branchList, branch) {
+ return info, nil
+ }
+
+ diff, err := git.GetDivergingCommits(ctx, repo.BaseRepo.RepoPath(), baseBranch.CommitID, forkBranch.CommitID, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ info.Allowed = true
+ info.CommitsBehind = diff.Behind
+
+ return info, nil
+}
diff --git a/services/repository/template.go b/services/repository/template.go
index 36a680c8e2..3566aa2b7e 100644
--- a/services/repository/template.go
+++ b/services/repository/template.go
@@ -6,12 +6,12 @@ package repository
import (
"context"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ notify_service "forgejo.org/services/notify"
)
// GenerateIssueLabels generates issue labels from a template repository
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 467c85ef6f..6026d85ae1 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -9,19 +9,19 @@ import (
"os"
"strings"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/sync"
- "code.gitea.io/gitea/modules/util"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/sync"
+ "forgejo.org/modules/util"
+ notify_service "forgejo.org/services/notify"
)
// repoWorkingPool represents a working pool to order the parallel changes to the same repository
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index cc51a05781..4bb0fc140c 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -7,17 +7,17 @@ import (
"sync"
"testing"
- "code.gitea.io/gitea/models"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/feed"
- notify_service "code.gitea.io/gitea/services/notify"
+ "forgejo.org/models"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/feed"
+ notify_service "forgejo.org/services/notify"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go
index 031c474dd7..2d5aebdbc1 100644
--- a/services/secrets/secrets.go
+++ b/services/secrets/secrets.go
@@ -6,8 +6,8 @@ package secrets
import (
"context"
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
+ "forgejo.org/models/db"
+ secret_model "forgejo.org/models/secret"
)
func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*secret_model.Secret, bool, error) {
diff --git a/services/secrets/validation.go b/services/secrets/validation.go
index 3db5b96452..44250ba87b 100644
--- a/services/secrets/validation.go
+++ b/services/secrets/validation.go
@@ -6,7 +6,7 @@ package secrets
import (
"regexp"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/modules/util"
)
// https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets
diff --git a/services/shared/automerge/automerge.go b/services/shared/automerge/automerge.go
index 8f38cf260a..be7b2f6eb4 100644
--- a/services/shared/automerge/automerge.go
+++ b/services/shared/automerge/automerge.go
@@ -9,21 +9,21 @@ import (
"strconv"
"strings"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
)
// PRAutoMergeQueue represents a queue to handle update pull request tests
var PRAutoMergeQueue *queue.WorkerPoolQueue[string]
func addToQueue(pr *issues_model.PullRequest, sha string) {
- log.Trace("Adding pullID: %d to the pull requests patch checking queue with sha %s", pr.ID, sha)
+ log.Trace("Adding pullID: %d to the automerge queue with sha %s", pr.ID, sha)
if err := PRAutoMergeQueue.Push(fmt.Sprintf("%d_%s", pr.ID, sha)); err != nil {
- log.Error("Error adding pullID: %d to the pull requests patch checking queue %v", pr.ID, err)
+ log.Error("Error adding pullID: %d to the automerge queue %v", pr.ID, err)
}
}
@@ -43,32 +43,29 @@ func StartPRCheckAndAutoMergeBySHA(ctx context.Context, sha string, repo *repo_m
return nil
}
-// StartPRCheckAndAutoMerge start an automerge check and auto merge task for a pull request
func StartPRCheckAndAutoMerge(ctx context.Context, pull *issues_model.PullRequest) {
if pull == nil || pull.HasMerged || !pull.CanAutoMerge() {
return
}
- if err := pull.LoadBaseRepo(ctx); err != nil {
- log.Error("LoadBaseRepo: %v", err)
- return
+ commitID := pull.HeadCommitID
+ if commitID == "" {
+ commitID = getCommitIDFromRefName(ctx, pull)
}
- gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo)
- if err != nil {
- log.Error("OpenRepository: %v", err)
- return
- }
- defer gitRepo.Close()
- commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
- if err != nil {
- log.Error("GetRefCommitID: %v", err)
+ if commitID == "" {
return
}
addToQueue(pull, commitID)
}
+var AddToQueueIfMergeable = func(ctx context.Context, pull *issues_model.PullRequest) {
+ if pull.Status == issues_model.PullRequestStatusMergeable {
+ StartPRCheckAndAutoMerge(ctx, pull)
+ }
+}
+
func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.Repository, filter func(*issues_model.PullRequest) bool) (map[int64]*issues_model.PullRequest, error) {
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
if err != nil {
@@ -118,3 +115,24 @@ func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.
return pulls, nil
}
+
+func getCommitIDFromRefName(ctx context.Context, pull *issues_model.PullRequest) string {
+ if err := pull.LoadBaseRepo(ctx); err != nil {
+ log.Error("LoadBaseRepo: %v", err)
+ return ""
+ }
+
+ gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo)
+ if err != nil {
+ log.Error("OpenRepository: %v", err)
+ return ""
+ }
+ defer gitRepo.Close()
+ commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
+ if err != nil {
+ log.Error("GetRefCommitID: %v", err)
+ return ""
+ }
+
+ return commitID
+}
diff --git a/services/task/migrate.go b/services/task/migrate.go
index 9cef77a6c8..a9f76299fd 100644
--- a/services/task/migrate.go
+++ b/services/task/migrate.go
@@ -10,20 +10,20 @@ import (
"strings"
"time"
- admin_model "code.gitea.io/gitea/models/admin"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/migrations"
- notify_service "code.gitea.io/gitea/services/notify"
+ admin_model "forgejo.org/models/admin"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/migration"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/migrations"
+ notify_service "forgejo.org/services/notify"
)
func handleCreateError(owner *user_model.User, err error) error {
diff --git a/services/task/task.go b/services/task/task.go
index ac659ac3e5..f030bdb38c 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -5,23 +5,24 @@ package task
import (
"context"
+ "errors"
"fmt"
- admin_model "code.gitea.io/gitea/models/admin"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/secret"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
- repo_service "code.gitea.io/gitea/services/repository"
+ admin_model "forgejo.org/models/admin"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/secret"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
+ repo_service "forgejo.org/services/repository"
)
// taskQueue is a global queue of tasks
@@ -41,7 +42,7 @@ func Run(ctx context.Context, t *admin_model.Task) error {
func Init() error {
taskQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "task", handler)
if taskQueue == nil {
- return fmt.Errorf("unable to create task queue")
+ return errors.New("unable to create task queue")
}
go graceful.GetManager().RunWithCancel(taskQueue)
return nil
diff --git a/services/uinotification/notify.go b/services/uinotification/notify.go
index be5f7019a2..25048e7b53 100644
--- a/services/uinotification/notify.go
+++ b/services/uinotification/notify.go
@@ -6,16 +6,16 @@ package uinotification
import (
"context"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/queue"
- notify_service "code.gitea.io/gitea/services/notify"
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/container"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/queue"
+ notify_service "forgejo.org/services/notify"
)
type (
diff --git a/services/user/avatar.go b/services/user/avatar.go
index 3f87466eaa..79dfc76503 100644
--- a/services/user/avatar.go
+++ b/services/user/avatar.go
@@ -10,11 +10,11 @@ import (
"io"
"os"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/avatar"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/avatar"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/storage"
)
// UploadAvatar saves custom avatar for user.
diff --git a/services/user/avatar_test.go b/services/user/avatar_test.go
index 21fca8dd09..17132a74ab 100644
--- a/services/user/avatar_test.go
+++ b/services/user/avatar_test.go
@@ -10,11 +10,11 @@ import (
"os"
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/test"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -42,7 +42,7 @@ func TestUserDeleteAvatar(t *testing.T) {
err := UploadAvatar(db.DefaultContext, user, buff.Bytes())
require.NoError(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- assert.NotEqual(t, "", verification.Avatar)
+ assert.NotEmpty(t, verification.Avatar)
// fail to delete ...
storage.Avatars = storage.UninitializedStorage
@@ -60,7 +60,7 @@ func TestUserDeleteAvatar(t *testing.T) {
// ... the avatar is removed from the database
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- assert.Equal(t, "", verification.Avatar)
+ assert.Empty(t, verification.Avatar)
})
t.Run("Success", func(t *testing.T) {
@@ -70,12 +70,12 @@ func TestUserDeleteAvatar(t *testing.T) {
err := UploadAvatar(db.DefaultContext, user, buff.Bytes())
require.NoError(t, err)
verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- assert.NotEqual(t, "", verification.Avatar)
+ assert.NotEmpty(t, verification.Avatar)
err = DeleteAvatar(db.DefaultContext, user)
require.NoError(t, err)
verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- assert.Equal(t, "", verification.Avatar)
+ assert.Empty(t, verification.Avatar)
})
}
diff --git a/services/user/block.go b/services/user/block.go
index 0b31119dfb..6be8dc5f70 100644
--- a/services/user/block.go
+++ b/services/user/block.go
@@ -5,10 +5,10 @@ package user
import (
"context"
- model "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
+ model "forgejo.org/models"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
"xorm.io/builder"
)
diff --git a/services/user/block_test.go b/services/user/block_test.go
index f9e95ed7f7..a2ba5d71a7 100644
--- a/services/user/block_test.go
+++ b/services/user/block_test.go
@@ -6,11 +6,12 @@ package user
import (
"testing"
- model "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
+ model "forgejo.org/models"
+ "forgejo.org/models/db"
+ issues_model "forgejo.org/models/issues"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -89,4 +90,24 @@ func TestBlockUser(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3, OwnerID: blockedUser.ID})
assert.Equal(t, repo_model.RepositoryReady, repo.Status)
})
+
+ t.Run("Issues", func(t *testing.T) {
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ defer user_model.UnblockUser(db.DefaultContext, doer.ID, blockedUser.ID)
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: doer.ID})
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4, RepoID: repo.ID}, "is_closed = true")
+
+ _, err := issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false)
+ require.NoError(t, err)
+
+ _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, doer, true)
+ require.NoError(t, err)
+
+ require.NoError(t, BlockUser(db.DefaultContext, doer.ID, blockedUser.ID))
+
+ _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false)
+ require.Error(t, err)
+ })
}
diff --git a/services/user/delete.go b/services/user/delete.go
index 587e3c2a8f..9caa24c373 100644
--- a/services/user/delete.go
+++ b/services/user/delete.go
@@ -1,4 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@@ -10,20 +11,20 @@ import (
_ "image/jpeg" // Needed for jpeg support
- actions_model "code.gitea.io/gitea/models/actions"
- activities_model "code.gitea.io/gitea/models/activities"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
- pull_model "code.gitea.io/gitea/models/pull"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- issue_service "code.gitea.io/gitea/services/issue"
+ actions_model "forgejo.org/models/actions"
+ activities_model "forgejo.org/models/activities"
+ asymkey_model "forgejo.org/models/asymkey"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ git_model "forgejo.org/models/git"
+ issues_model "forgejo.org/models/issues"
+ "forgejo.org/models/organization"
+ access_model "forgejo.org/models/perm/access"
+ pull_model "forgejo.org/models/pull"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ issue_service "forgejo.org/services/issue"
"xorm.io/builder"
)
@@ -216,6 +217,11 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
}
// ***** END: ExternalLoginUser *****
+ // If the user was reported as abusive, a shadow copy should be created before deletion.
+ if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil {
+ return err
+ }
+
if _, err = db.DeleteByID[user_model.User](ctx, u.ID); err != nil {
return fmt.Errorf("delete: %w", err)
}
diff --git a/services/user/email.go b/services/user/email.go
index 31404aadaa..7a01fa77b3 100644
--- a/services/user/email.go
+++ b/services/user/email.go
@@ -1,4 +1,5 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@@ -8,12 +9,12 @@ import (
"errors"
"strings"
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/validation"
- "code.gitea.io/gitea/services/mailer"
+ "forgejo.org/models/db"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/validation"
+ "forgejo.org/services/mailer"
)
// AdminAddOrSetPrimaryEmailAddress is used by admins to add or set a user's primary email address
@@ -203,6 +204,11 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary
oldPrimaryEmail := u.Email
+ // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns).
+ if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil {
+ return err
+ }
+
// 1. Update user table
u.Email = newPrimaryEmail.Email
if _, err = sess.ID(u.ID).Cols("email").Update(u); err != nil {
diff --git a/services/user/email_test.go b/services/user/email_test.go
index 86f31a8984..b48936a27e 100644
--- a/services/user/email_test.go
+++ b/services/user/email_test.go
@@ -6,11 +6,11 @@ package user
import (
"testing"
- "code.gitea.io/gitea/models/db"
- organization_model "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/db"
+ organization_model "forgejo.org/models/organization"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
"github.com/gobwas/glob"
"github.com/stretchr/testify/assert"
diff --git a/services/user/update.go b/services/user/update.go
index 26c90505c8..65d3992751 100644
--- a/services/user/update.go
+++ b/services/user/update.go
@@ -7,14 +7,14 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models"
- auth_model "code.gitea.io/gitea/models/auth"
- user_model "code.gitea.io/gitea/models/user"
- password_module "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/mailer"
+ "forgejo.org/models"
+ auth_model "forgejo.org/models/auth"
+ user_model "forgejo.org/models/user"
+ password_module "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/mailer"
)
type UpdateOptions struct {
@@ -40,6 +40,7 @@ type UpdateOptions struct {
SetLastLogin bool
RepoAdminChangeTeamAccess optional.Option[bool]
EnableRepoUnitHints optional.Option[bool]
+ KeepPronounsPrivate optional.Option[bool]
}
func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error {
@@ -97,6 +98,12 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
cols = append(cols, "enable_repo_unit_hints")
}
+ if opts.KeepPronounsPrivate.Has() {
+ u.KeepPronounsPrivate = opts.KeepPronounsPrivate.Value()
+
+ cols = append(cols, "keep_pronouns_private")
+ }
+
if opts.AllowGitHook.Has() {
u.AllowGitHook = opts.AllowGitHook.Value()
diff --git a/services/user/update_test.go b/services/user/update_test.go
index 11379d4508..f1754b2db9 100644
--- a/services/user/update_test.go
+++ b/services/user/update_test.go
@@ -6,12 +6,12 @@ package user
import (
"testing"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- password_module "code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/structs"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ password_module "forgejo.org/modules/auth/password"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/user/user.go b/services/user/user.go
index 7610a3fbdd..d90fbac978 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -11,28 +11,37 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/eventsource"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/agit"
- org_service "code.gitea.io/gitea/services/org"
- "code.gitea.io/gitea/services/packages"
- container_service "code.gitea.io/gitea/services/packages/container"
- repo_service "code.gitea.io/gitea/services/repository"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ packages_model "forgejo.org/models/packages"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/eventsource"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/agit"
+ org_service "forgejo.org/services/org"
+ "forgejo.org/services/packages"
+ container_service "forgejo.org/services/packages/container"
+ repo_service "forgejo.org/services/repository"
)
// RenameUser renames a user
func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error {
+ return renameUser(ctx, u, newUserName, false)
+}
+
+// RenameUser renames a user as an admin.
+func AdminRenameUser(ctx context.Context, u *user_model.User, newUserName string) error {
+ return renameUser(ctx, u, newUserName, true)
+}
+
+func renameUser(ctx context.Context, u *user_model.User, newUserName string, doerIsAdmin bool) error {
if newUserName == u.Name {
return nil
}
@@ -49,6 +58,17 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err
return err
}
+ // Check if the new username can be claimed.
+ if !doerIsAdmin {
+ if ok, expireTime, err := user_model.CanClaimUsername(ctx, newUserName, u.ID); err != nil {
+ return err
+ } else if !ok {
+ return user_model.ErrCooldownPeriod{
+ ExpireTime: expireTime,
+ }
+ }
+ }
+
onlyCapitalization := strings.EqualFold(newUserName, u.Name)
oldUserName := u.Name
@@ -85,6 +105,12 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err
return err
}
+ if setting.Service.MaxUserRedirects > 0 {
+ if err := user_model.LimitUserRedirects(ctx, u.ID, setting.Service.MaxUserRedirects); err != nil {
+ return err
+ }
+ }
+
if err := agit.UserNameChanged(ctx, u, newUserName); err != nil {
return err
}
diff --git a/services/user/user_test.go b/services/user/user_test.go
index 719c9d733c..4678d3bc9a 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -11,17 +11,17 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/timeutil"
+ "forgejo.org/models"
+ asymkey_model "forgejo.org/models/asymkey"
+ "forgejo.org/models/auth"
+ "forgejo.org/models/db"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -67,13 +67,7 @@ func TestDeleteUser(t *testing.T) {
}
func TestPurgeUser(t *testing.T) {
- defer unittest.OverrideFixtures(
- unittest.FixturesOptions{
- Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
- Base: setting.AppWorkPath,
- Dirs: []string{"services/user/TestPurgeUser/"},
- },
- )()
+ defer unittest.OverrideFixtures("services/user/TestPurgeUser")()
require.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.SSH.RootPath, t.TempDir())()
defer test.MockVariableValue(&setting.SSH.CreateAuthorizedKeysFile, true)()
@@ -195,10 +189,33 @@ func TestRenameUser(t *testing.T) {
redirectUID, err := user_model.LookupUserRedirect(db.DefaultContext, oldUsername)
require.NoError(t, err)
- assert.EqualValues(t, user.ID, redirectUID)
+ assert.Equal(t, user.ID, redirectUID)
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name})
})
+
+ t.Run("Keep N redirects", func(t *testing.T) {
+ defer test.MockProtect(&setting.Service.MaxUserRedirects)()
+ // Start clean
+ unittest.AssertSuccessfulDelete(t, &user_model.Redirect{RedirectUserID: user.ID})
+
+ setting.Service.MaxUserRedirects = 1
+
+ require.NoError(t, RenameUser(db.DefaultContext, user, "redirect-1"))
+ unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "user_rename"})
+
+ // The granularity of created_unix is a second.
+ test.SleepTillNextSecond()
+ require.NoError(t, RenameUser(db.DefaultContext, user, "redirect-2"))
+ unittest.AssertExistsIf(t, false, &user_model.Redirect{LowerName: "user_rename"})
+ unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-1"})
+
+ setting.Service.MaxUserRedirects = 2
+ test.SleepTillNextSecond()
+ require.NoError(t, RenameUser(db.DefaultContext, user, "redirect-3"))
+ unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-1"})
+ unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-2"})
+ })
}
func TestCreateUser_Issue5882(t *testing.T) {
diff --git a/services/webhook/TestPushCommits/webhook.yml b/services/webhook/TestPushCommits/webhook.yml
new file mode 100644
index 0000000000..c1ee7ceb9e
--- /dev/null
+++ b/services/webhook/TestPushCommits/webhook.yml
@@ -0,0 +1,9 @@
+-
+ id: 1001
+ repo_id: 2
+ type: forgejo
+ url: http://www.example.com/blåhaj
+ http_method: POST
+ content_type: 1 # json
+ events: '{"send_everything":true,"branch_filter":"{master*,main*}"}'
+ is_active: true
diff --git a/services/webhook/default.go b/services/webhook/default.go
index 089ff8bae3..797b98f99a 100644
--- a/services/webhook/default.go
+++ b/services/webhook/default.go
@@ -11,14 +11,14 @@ import (
"net/url"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/svg"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/svg"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
var _ Handler = defaultHandler{}
@@ -36,8 +36,7 @@ func (dh defaultHandler) Type() webhook_module.HookType {
func (dh defaultHandler) Icon(size int) template.HTML {
if dh.forgejo {
- // forgejo.svg is not in web_src/svg/, so svg.RenderHTML does not work
- return shared.ImgIcon("forgejo.svg", size)
+ return svg.RenderHTML("gitea-forgejo", size, "img")
}
return svg.RenderHTML("gitea-gitea", size, "img")
}
diff --git a/services/webhook/default_test.go b/services/webhook/default_test.go
index f3e2848659..fcef4612e1 100644
--- a/services/webhook/default_test.go
+++ b/services/webhook/default_test.go
@@ -4,12 +4,11 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ webhook_module "forgejo.org/modules/webhook"
jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/assert"
@@ -45,7 +44,7 @@ func TestGiteaPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -74,7 +73,7 @@ func TestGiteaPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -103,7 +102,7 @@ func TestGiteaPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -148,7 +147,7 @@ func TestForgejoPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -177,7 +176,7 @@ func TestForgejoPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -206,7 +205,7 @@ func TestForgejoPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dh.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -238,7 +237,7 @@ func TestOpenProjectPayload(t *testing.T) {
assert.Equal(t, 12, j.Get("number").MustBeValid().ToInt())
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", j.Get("html_url").MustBeValid().ToString())
assert.Equal(t, jsoniter.NilValue, j.Get("updated_at").ValueType())
- assert.Equal(t, "", j.Get("state").MustBeValid().ToString())
+ assert.Empty(t, j.Get("state").MustBeValid().ToString())
assert.Equal(t, "Fix bug", j.Get("title").MustBeValid().ToString())
assert.Equal(t, "fixes bug #2", j.Get("body").MustBeValid().ToString())
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index 25668143e6..23aca80345 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -6,6 +6,7 @@ package webhook
import (
"context"
"crypto/tls"
+ "errors"
"fmt"
"io"
"net/http"
@@ -14,16 +15,16 @@ import (
"sync"
"time"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/hostmatcher"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/proxy"
- "code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/hostmatcher"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/timeutil"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/gobwas/glob"
)
@@ -218,7 +219,7 @@ func Init() error {
hookQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "webhook_sender", handler)
if hookQueue == nil {
- return fmt.Errorf("unable to create webhook_sender queue")
+ return errors.New("unable to create webhook_sender queue")
}
go graceful.GetManager().RunWithCancel(hookQueue)
diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go
index 21af3c7116..1a9ce05de4 100644
--- a/services/webhook/deliver_test.go
+++ b/services/webhook/deliver_test.go
@@ -4,38 +4,30 @@
package webhook
import (
- "context"
"io"
"net/http"
"net/http/httptest"
"net/url"
- "os"
"strings"
"testing"
"time"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/hostmatcher"
- "code.gitea.io/gitea/modules/setting"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/hostmatcher"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWebhookProxy(t *testing.T) {
- oldWebhook := setting.Webhook
- oldHTTPProxy := os.Getenv("http_proxy")
- oldHTTPSProxy := os.Getenv("https_proxy")
- t.Cleanup(func() {
- setting.Webhook = oldWebhook
- os.Setenv("http_proxy", oldHTTPProxy)
- os.Setenv("https_proxy", oldHTTPSProxy)
- })
- os.Unsetenv("http_proxy")
- os.Unsetenv("https_proxy")
+ defer test.MockProtect(&setting.Webhook)()
+ t.Setenv("http_proxy", "")
+ t.Setenv("https_proxy", "")
setting.Webhook.ProxyURL = "http://localhost:8080"
setting.Webhook.ProxyURLFixed, _ = url.Parse(setting.Webhook.ProxyURL)
@@ -124,7 +116,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, hookTask)
- require.NoError(t, Deliver(context.Background(), hookTask))
+ require.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -145,17 +137,17 @@ func TestWebhookDeliverHookTask(t *testing.T) {
case "/webhook/66d222a5d6349e1311f551e50722d837e30fce98":
// Version 1
assert.Equal(t, "push", r.Header.Get("X-GitHub-Event"))
- assert.Equal(t, "", r.Header.Get("Content-Type"))
+ assert.Empty(t, r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
assert.Equal(t, `{"data": 42}`, string(body))
- case "/webhook/6db5dc1e282529a8c162c7fe93dd2667494eeb51":
+ case "/webhook/86aaa4d69df5aa487cb0148af4ae7e546933057b":
// Version 2
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
- assert.Len(t, body, 2147)
+ assert.Len(t, body, 1909)
default:
w.WriteHeader(404)
@@ -190,7 +182,7 @@ func TestWebhookDeliverHookTask(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, hookTask)
- require.NoError(t, Deliver(context.Background(), hookTask))
+ require.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -216,7 +208,7 @@ func TestWebhookDeliverHookTask(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, hookTask)
- require.NoError(t, Deliver(context.Background(), hookTask))
+ require.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -317,7 +309,7 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, hookTask)
- require.NoError(t, Deliver(context.Background(), hookTask))
+ require.NoError(t, Deliver(t.Context(), hookTask))
select {
case gotBody := <-hc.gotBody:
assert.NotEqual(t, string(data), string(gotBody), "request body must be different from the event payload")
diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go
index 899c5b2d9f..ec53c79c2c 100644
--- a/services/webhook/dingtalk.go
+++ b/services/webhook/dingtalk.go
@@ -11,13 +11,13 @@ import (
"net/url"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type dingtalkHandler struct{}
@@ -207,6 +207,12 @@ func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, err
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
}
+func (dc dingtalkConvertor) Action(p *api.ActionPayload) (DingtalkPayload, error) {
+ text, _ := getActionPayloadInfo(p, noneLinkFormatter)
+
+ return createDingtalkPayload(text, text, "view action", p.Run.HTMLURL), nil
+}
+
func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload {
return DingtalkPayload{
MsgType: "actionCard",
diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go
index d0a2d48908..5d2a240660 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -4,14 +4,13 @@
package webhook
import (
- "context"
"net/url"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -236,7 +235,7 @@ func TestDingTalkJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := dingtalkHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := dingtalkHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index cd25175ea1..7259c4a995 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -15,17 +15,18 @@ import (
"strings"
"unicode/utf8"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "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"
- gitea_context "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/base"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ gitea_context "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
"code.forgejo.org/go-chi/binding"
)
@@ -151,6 +152,18 @@ var (
redColor = color("ff3232")
)
+// https://discord.com/developers/docs/resources/message#embed-object-embed-limits
+// Discord has some limits in place for the embeds.
+// According to some tests, there is no consistent limit for different character sets.
+// For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed.
+// To keep it simple, we currently truncate at 2000.
+const discordDescriptionCharactersLimit = 2000
+
+type discordConvertor struct {
+ Username string
+ AvatarURL string
+}
+
// Create implements PayloadConvertor Create method
func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) {
// created tag/branch
@@ -312,9 +325,10 @@ func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error)
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
}
-type discordConvertor struct {
- Username string
- AvatarURL string
+func (d discordConvertor) Action(p *api.ActionPayload) (DiscordPayload, error) {
+ text, color := getActionPayloadInfo(p, noneLinkFormatter)
+
+ return d.createPayload(p.Run.TriggerUser, text, "", p.Run.HTMLURL, color), nil
}
var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{}
@@ -336,7 +350,7 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string,
case webhook_module.HookEventPullRequestReviewApproved:
return "approved", nil
case webhook_module.HookEventPullRequestReviewRejected:
- return "rejected", nil
+ return "requested changes", nil
case webhook_module.HookEventPullRequestReviewComment:
return "comment", nil
default:
@@ -357,7 +371,7 @@ func (d discordConvertor) createPayload(s *api.User, title, text, url string, co
Embeds: []DiscordEmbed{
{
Title: title,
- Description: text,
+ Description: base.TruncateString(text, discordDescriptionCharactersLimit),
URL: url,
Color: color,
Author: DiscordEmbedAuthor{
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index 4edd06bd76..b04be30bc6 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -4,14 +4,13 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -176,7 +175,7 @@ func TestDiscordPayload(t *testing.T) {
require.NoError(t, err)
assert.Len(t, pl.Embeds, 1)
- assert.Len(t, pl.Embeds[0].Description, 4096)
+ assert.Len(t, pl.Embeds[0].Description, 2000)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -346,7 +345,7 @@ func TestDiscordJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := discordHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := discordHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go
index f77c3bbd65..57f2362783 100644
--- a/services/webhook/feishu.go
+++ b/services/webhook/feishu.go
@@ -10,12 +10,12 @@ import (
"net/http"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type feishuHandler struct{}
@@ -191,6 +191,12 @@ func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error)
return newFeishuTextPayload(text), nil
}
+func (fc feishuConvertor) Action(p *api.ActionPayload) (FeishuPayload, error) {
+ text, _ := getActionPayloadInfo(p, noneLinkFormatter)
+
+ return newFeishuTextPayload(text), nil
+}
+
type feishuConvertor struct{}
var _ shared.PayloadConvertor[FeishuPayload] = feishuConvertor{}
diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go
index 9744571b39..7cf24b84ed 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -4,13 +4,12 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -177,7 +176,7 @@ func TestFeishuJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := feishuHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := feishuHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/general.go b/services/webhook/general.go
index c41f58fe8d..c728b6ba1a 100644
--- a/services/webhook/general.go
+++ b/services/webhook/general.go
@@ -9,11 +9,11 @@ import (
"net/url"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "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"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
)
type linkFormatter = func(string, string) string
@@ -37,11 +37,12 @@ func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, o
for i, user := range assignList {
assignStringList[i] = user.UserName
}
- if p.Action == api.HookIssueAssigned {
+ switch p.Action {
+ case api.HookIssueAssigned:
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
- } else if p.Action == api.HookIssueUnassigned {
+ case api.HookIssueUnassigned:
operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
- } else if p.Action == api.HookIssueMilestoned {
+ case api.HookIssueMilestoned:
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
}
link = p.PullRequest.HTMLURL
@@ -62,11 +63,12 @@ func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operate
for i, user := range assignList {
assignStringList[i] = user.UserName
}
- if p.Action == api.HookIssueAssigned {
+ switch p.Action {
+ case api.HookIssueAssigned:
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
- } else if p.Action == api.HookIssueUnassigned {
+ case api.HookIssueUnassigned:
operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
- } else if p.Action == api.HookIssueMilestoned {
+ case api.HookIssueMilestoned:
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
}
link = p.Issue.HTMLURL
@@ -92,7 +94,6 @@ func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator
}
func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
- repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
titleLink := linkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), issueTitle)
var text string
@@ -100,39 +101,37 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with
switch p.Action {
case api.HookIssueOpened:
- text = fmt.Sprintf("[%s] Issue opened: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue opened: %s", p.Repository.FullName, titleLink)
color = orangeColor
case api.HookIssueClosed:
- text = fmt.Sprintf("[%s] Issue closed: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue closed: %s", p.Repository.FullName, titleLink)
color = redColor
case api.HookIssueReOpened:
- text = fmt.Sprintf("[%s] Issue re-opened: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue re-opened: %s", p.Repository.FullName, titleLink)
case api.HookIssueEdited:
- text = fmt.Sprintf("[%s] Issue edited: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue edited: %s", p.Repository.FullName, titleLink)
case api.HookIssueAssigned:
list := make([]string, len(p.Issue.Assignees))
for i, user := range p.Issue.Assignees {
list[i] = linkFormatter(setting.AppURL+url.PathEscape(user.UserName), user.UserName)
}
- text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink)
+ text = fmt.Sprintf("[%s] Issue assigned to %s: %s", p.Repository.FullName, strings.Join(list, ", "), titleLink)
color = greenColor
case api.HookIssueUnassigned:
- text = fmt.Sprintf("[%s] Issue unassigned: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue unassigned: %s", p.Repository.FullName, titleLink)
case api.HookIssueLabelUpdated:
- text = fmt.Sprintf("[%s] Issue labels updated: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue labels updated: %s", p.Repository.FullName, titleLink)
case api.HookIssueLabelCleared:
- text = fmt.Sprintf("[%s] Issue labels cleared: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue labels cleared: %s", p.Repository.FullName, titleLink)
case api.HookIssueSynchronized:
- text = fmt.Sprintf("[%s] Issue synchronized: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue synchronized: %s", p.Repository.FullName, titleLink)
case api.HookIssueMilestoned:
- mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
- text = fmt.Sprintf("[%s] Issue milestoned to %s: %s", repoLink,
- linkFormatter(mileStoneLink, p.Issue.Milestone.Title), titleLink)
+ text = fmt.Sprintf("[%s] Issue milestoned to %s: %s", p.Repository.FullName, p.Issue.Milestone.Title, titleLink)
case api.HookIssueDemilestoned:
- text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Issue milestone cleared: %s", p.Repository.FullName, titleLink)
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
}
var attachmentText string
@@ -144,7 +143,6 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with
}
func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
- repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := linkFormatter(p.PullRequest.URL, issueTitle)
var text string
@@ -153,83 +151,79 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
switch p.Action {
case api.HookIssueOpened:
- text = fmt.Sprintf("[%s] Pull request opened: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request opened: %s", p.Repository.FullName, titleLink)
attachmentText = p.PullRequest.Body
color = greenColor
case api.HookIssueClosed:
if p.PullRequest.HasMerged {
- text = fmt.Sprintf("[%s] Pull request merged: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request merged: %s", p.Repository.FullName, titleLink)
color = purpleColor
} else {
- text = fmt.Sprintf("[%s] Pull request closed: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request closed: %s", p.Repository.FullName, titleLink)
color = redColor
}
case api.HookIssueReOpened:
- text = fmt.Sprintf("[%s] Pull request re-opened: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request re-opened: %s", p.Repository.FullName, titleLink)
case api.HookIssueEdited:
- text = fmt.Sprintf("[%s] Pull request edited: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request edited: %s", p.Repository.FullName, titleLink)
attachmentText = p.PullRequest.Body
case api.HookIssueAssigned:
list := make([]string, len(p.PullRequest.Assignees))
for i, user := range p.PullRequest.Assignees {
list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName)
}
- text = fmt.Sprintf("[%s] Pull request assigned to %s: %s", repoLink,
+ text = fmt.Sprintf("[%s] Pull request assigned to %s: %s", p.Repository.FullName,
strings.Join(list, ", "), titleLink)
color = greenColor
case api.HookIssueUnassigned:
- text = fmt.Sprintf("[%s] Pull request unassigned: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request unassigned: %s", p.Repository.FullName, titleLink)
case api.HookIssueLabelUpdated:
- text = fmt.Sprintf("[%s] Pull request labels updated: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request labels updated: %s", p.Repository.FullName, titleLink)
case api.HookIssueLabelCleared:
- text = fmt.Sprintf("[%s] Pull request labels cleared: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request labels cleared: %s", p.Repository.FullName, titleLink)
case api.HookIssueSynchronized:
- text = fmt.Sprintf("[%s] Pull request synchronized: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request synchronized: %s", p.Repository.FullName, titleLink)
case api.HookIssueMilestoned:
- mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
- text = fmt.Sprintf("[%s] Pull request milestoned to %s: %s", repoLink,
- linkFormatter(mileStoneLink, p.PullRequest.Milestone.Title), titleLink)
+ text = fmt.Sprintf("[%s] Pull request milestoned to %s: %s", p.Repository.FullName, p.PullRequest.Milestone.Title, titleLink)
case api.HookIssueDemilestoned:
- text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", p.Repository.FullName, titleLink)
case api.HookIssueReviewed:
- text = fmt.Sprintf("[%s] Pull request reviewed: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request reviewed: %s", p.Repository.FullName, titleLink)
attachmentText = p.Review.Content
case api.HookIssueReviewRequested:
- text = fmt.Sprintf("[%s] Pull request review requested: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request review requested: %s", p.Repository.FullName, titleLink)
case api.HookIssueReviewRequestRemoved:
- text = fmt.Sprintf("[%s] Pull request review request removed: %s", repoLink, titleLink)
+ text = fmt.Sprintf("[%s] Pull request review request removed: %s", p.Repository.FullName, titleLink)
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
}
return text, issueTitle, attachmentText, color
}
func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
- repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
refLink := linkFormatter(p.Repository.HTMLURL+"/releases/tag/"+util.PathEscapeSegments(p.Release.TagName), p.Release.TagName)
switch p.Action {
case api.HookReleasePublished:
- text = fmt.Sprintf("[%s] Release created: %s", repoLink, refLink)
+ text = fmt.Sprintf("[%s] Release created: %s", p.Repository.FullName, refLink)
color = greenColor
case api.HookReleaseUpdated:
- text = fmt.Sprintf("[%s] Release updated: %s", repoLink, refLink)
+ text = fmt.Sprintf("[%s] Release updated: %s", p.Repository.FullName, refLink)
color = yellowColor
case api.HookReleaseDeleted:
- text = fmt.Sprintf("[%s] Release deleted: %s", repoLink, refLink)
+ text = fmt.Sprintf("[%s] Release deleted: %s", p.Repository.FullName, refLink)
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
}
return text, color
}
func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSender bool) (string, int, string) {
- repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
pageLink := linkFormatter(p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page), p.Page)
var text string
@@ -237,12 +231,12 @@ func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSen
switch p.Action {
case api.HookWikiCreated:
- text = fmt.Sprintf("[%s] New wiki page '%s'", repoLink, pageLink)
+ text = fmt.Sprintf("[%s] New wiki page '%s'", p.Repository.FullName, pageLink)
case api.HookWikiEdited:
- text = fmt.Sprintf("[%s] Wiki page '%s' edited", repoLink, pageLink)
+ text = fmt.Sprintf("[%s] Wiki page '%s' edited", p.Repository.FullName, pageLink)
color = yellowColor
case api.HookWikiDeleted:
- text = fmt.Sprintf("[%s] Wiki page '%s' deleted", repoLink, pageLink)
+ text = fmt.Sprintf("[%s] Wiki page '%s' deleted", p.Repository.FullName, pageLink)
color = redColor
}
@@ -251,14 +245,13 @@ func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSen
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
}
return text, color, pageLink
}
func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter, withSender bool) (string, string, int) {
- repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
issueTitle := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
var text, typ, titleLink string
@@ -274,20 +267,20 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
switch p.Action {
case api.HookIssueCommentCreated:
- text = fmt.Sprintf("[%s] New comment on %s %s", repoLink, typ, titleLink)
+ text = fmt.Sprintf("[%s] New comment on %s %s", p.Repository.FullName, typ, titleLink)
if p.IsPull {
color = greenColorLight
} else {
color = orangeColorLight
}
case api.HookIssueCommentEdited:
- text = fmt.Sprintf("[%s] Comment edited on %s %s", repoLink, typ, titleLink)
+ text = fmt.Sprintf("[%s] Comment edited on %s %s", p.Repository.FullName, typ, titleLink)
case api.HookIssueCommentDeleted:
- text = fmt.Sprintf("[%s] Comment deleted on %s %s", repoLink, typ, titleLink)
+ text = fmt.Sprintf("[%s] Comment deleted on %s %s", p.Repository.FullName, typ, titleLink)
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
}
return text, issueTitle, color
@@ -305,7 +298,26 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, w
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += fmt.Sprintf(" by %s", p.Sender.UserName)
+ }
+
+ return text, color
+}
+
+func getActionPayloadInfo(p *api.ActionPayload, linkFormatter linkFormatter) (text string, color int) {
+ runLink := linkFormatter(p.Run.HTMLURL, p.Run.Title)
+ repoLink := linkFormatter(p.Run.Repo.HTMLURL, p.Run.Repo.FullName)
+
+ switch p.Action {
+ case api.HookActionFailure:
+ text = fmt.Sprintf("%s Action Failed in %s %s", runLink, repoLink, p.Run.PrettyRef)
+ color = redColor
+ case api.HookActionRecover:
+ text = fmt.Sprintf("%s Action Recovered in %s %s", runLink, repoLink, p.Run.PrettyRef)
+ color = greenColor
+ case api.HookActionSuccess:
+ text = fmt.Sprintf("%s Action Succeeded in %s %s", runLink, repoLink, p.Run.PrettyRef)
+ color = greenColor
}
return text, color
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index 8412293708..10c779742d 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- api "code.gitea.io/gitea/modules/structs"
+ api "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -270,6 +270,22 @@ func pullReleaseTestPayload() *api.ReleasePayload {
}
}
+func ActionTestPayload() *api.ActionPayload {
+ // this is not a complete action payload but enough for testing purposes
+ return &api.ActionPayload{
+ Run: &api.ActionRun{
+ Repo: &api.Repository{
+ HTMLURL: "http://localhost:3000/test/repo",
+ Name: "repo",
+ FullName: "test/repo",
+ },
+ PrettyRef: "main",
+ HTMLURL: "http://localhost:3000/test/repo/actions/runs/69",
+ Title: "Build release",
+ },
+ }
+}
+
func pullRequestTestPayload() *api.PullRequestPayload {
return &api.PullRequestPayload{
Action: api.HookIssueOpened,
@@ -675,3 +691,36 @@ func TestGetIssueCommentPayloadInfo(t *testing.T) {
assert.Equal(t, c.color, color, "case %d", i)
}
}
+
+func TestGetActionPayloadInfo(t *testing.T) {
+ p := ActionTestPayload()
+
+ cases := []struct {
+ action api.HookActionAction
+ text string
+ color int
+ }{
+ {
+ api.HookActionFailure,
+ "Build release Action Failed in test/repo main",
+ redColor,
+ },
+ {
+ api.HookActionSuccess,
+ "Build release Action Succeeded in test/repo main",
+ greenColor,
+ },
+ {
+ api.HookActionRecover,
+ "Build release Action Recovered in test/repo main",
+ greenColor,
+ },
+ }
+
+ for i, c := range cases {
+ p.Action = c.action
+ text, color := getActionPayloadInfo(p, noneLinkFormatter)
+ assert.Equal(t, c.text, text, "case %d", i)
+ assert.Equal(t, c.color, color, "case %d", i)
+ }
+}
diff --git a/services/webhook/gogs.go b/services/webhook/gogs.go
index 7dbf64343f..bbab1ad41d 100644
--- a/services/webhook/gogs.go
+++ b/services/webhook/gogs.go
@@ -7,10 +7,10 @@ import (
"html/template"
"net/http"
- webhook_model "code.gitea.io/gitea/models/webhook"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type gogsHandler struct{ defaultHandler }
diff --git a/services/webhook/main_test.go b/services/webhook/main_test.go
index 6147aac499..97957291ca 100644
--- a/services/webhook/main_test.go
+++ b/services/webhook/main_test.go
@@ -6,13 +6,13 @@ package webhook
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/hostmatcher"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/hostmatcher"
+ "forgejo.org/modules/setting"
- _ "code.gitea.io/gitea/models"
- _ "code.gitea.io/gitea/models/actions"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models"
+ _ "forgejo.org/models/actions"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index e70e7a2f8e..bdb0c292ab 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -9,23 +9,23 @@ import (
"crypto/sha1"
"encoding/hex"
"fmt"
+ "html"
"html/template"
"net/http"
"net/url"
"regexp"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/svg"
- "code.gitea.io/gitea/modules/util"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/svg"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type matrixHandler struct{}
@@ -143,9 +143,8 @@ func (m matrixConvertor) newPayload(text string, commits ...*api.PayloadCommit)
// Create implements payloadConvertor Create method
func (m matrixConvertor) Create(p *api.CreatePayload) (MatrixPayload, error) {
- repoLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
refLink := MatrixLinkToRef(p.Repo.HTMLURL, p.Ref)
- text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
+ text := fmt.Sprintf("[%s:%s] %s created by %s", p.Repo.FullName, refLink, p.RefType, p.Sender.UserName)
return m.newPayload(text)
}
@@ -206,9 +205,8 @@ func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) {
commitDesc = fmt.Sprintf("%d commits", p.TotalCommits)
}
- repoLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
- branchLink := MatrixLinkToRef(p.Repo.HTMLURL, p.Ref)
- text := fmt.Sprintf("[%s] %s pushed %s to %s:
", repoLink, p.Pusher.UserName, commitDesc, branchLink)
+ refName := html.EscapeString(git.RefName(p.Ref).ShortName())
+ text := fmt.Sprintf("[%s] %s pushed %s to %s:
", p.Repo.FullName, p.Pusher.UserName, commitDesc, refName)
// for each commit, generate a new line text
for i, commit := range p.Commits {
@@ -231,10 +229,8 @@ func (m matrixConvertor) PullRequest(p *api.PullRequestPayload) (MatrixPayload,
// Review implements payloadConvertor Review method
func (m matrixConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (MatrixPayload, error) {
- senderLink := htmlLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := htmlLinkFormatter(p.PullRequest.HTMLURL, title)
- repoLink := htmlLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
if p.Action == api.HookIssueReviewed {
@@ -243,7 +239,7 @@ func (m matrixConvertor) Review(p *api.PullRequestPayload, event webhook_module.
return MatrixPayload{}, err
}
- text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", repoLink, action, titleLink, senderLink)
+ text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", p.Repository.FullName, action, titleLink, p.Sender.UserName)
}
return m.newPayload(text)
@@ -251,34 +247,38 @@ func (m matrixConvertor) Review(p *api.PullRequestPayload, event webhook_module.
// Repository implements payloadConvertor Repository method
func (m matrixConvertor) Repository(p *api.RepositoryPayload) (MatrixPayload, error) {
- senderLink := htmlLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
repoLink := htmlLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
switch p.Action {
case api.HookRepoCreated:
- text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink)
+ text = fmt.Sprintf("[%s] Repository created by %s", repoLink, p.Sender.UserName)
case api.HookRepoDeleted:
- text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink)
+ text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, p.Sender.UserName)
}
return m.newPayload(text)
}
func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) {
- senderLink := htmlLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
packageLink := htmlLinkFormatter(p.Package.HTMLURL, p.Package.Name)
var text string
switch p.Action {
case api.HookPackageCreated:
- text = fmt.Sprintf("[%s] Package published by %s", packageLink, senderLink)
+ text = fmt.Sprintf("[%s] Package published by %s", packageLink, p.Sender.UserName)
case api.HookPackageDeleted:
- text = fmt.Sprintf("[%s] Package deleted by %s", packageLink, senderLink)
+ text = fmt.Sprintf("[%s] Package deleted by %s", packageLink, p.Sender.UserName)
}
return m.newPayload(text)
}
+func (m matrixConvertor) Action(p *api.ActionPayload) (MatrixPayload, error) {
+ text, _ := getActionPayloadInfo(p, htmlLinkFormatter)
+
+ return m.newPayload(text)
+}
+
var urlRegex = regexp.MustCompile(`]*?href="([^">]*?)">(.*?)`)
func getMessageBody(htmlText string) string {
diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go
index 6cedb15ef3..1644def0e1 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -4,13 +4,12 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -28,8 +27,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.Body)
- assert.Equal(t, `[test/repo:test] branch created by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo:[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.Body)
+ assert.Equal(t, `[test/repo:test] branch created by user1`, pl.FormattedBody)
})
t.Run("Delete", func(t *testing.T) {
@@ -61,8 +60,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.Body)
- assert.Equal(t, `[test/repo] user1 pushed 2 commits to test:
2020558: commit message - user1
2020558: commit message - user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] user1 pushed 2 commits to test:\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.Body)
+ assert.Equal(t, `[test/repo] user1 pushed 2 commits to test:
2020558: commit message - user1
2020558: commit message - user1`, pl.FormattedBody)
})
t.Run("Issue", func(t *testing.T) {
@@ -73,16 +72,16 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1`, pl.FormattedBody)
p.Action = api.HookIssueClosed
pl, err = mc.Issue(p)
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.FormattedBody)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -92,8 +91,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1`, pl.FormattedBody)
})
t.Run("PullRequest", func(t *testing.T) {
@@ -103,8 +102,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1`, pl.FormattedBody)
})
t.Run("PullRequestComment", func(t *testing.T) {
@@ -114,8 +113,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1`, pl.FormattedBody)
})
t.Run("Review", func(t *testing.T) {
@@ -126,8 +125,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.FormattedBody)
})
t.Run("Repository", func(t *testing.T) {
@@ -137,8 +136,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.Body)
- assert.Equal(t, `[test/repo] Repository created by user1`, pl.FormattedBody)
+ assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by user1`, pl.Body)
+ assert.Equal(t, `[test/repo] Repository created by user1`, pl.FormattedBody)
})
t.Run("Package", func(t *testing.T) {
@@ -148,8 +147,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by [user1](https://try.gitea.io/user1)`, pl.Body)
- assert.Equal(t, `[GiteaContainer] Package published by user1`, pl.FormattedBody)
+ assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by user1`, pl.Body)
+ assert.Equal(t, `[GiteaContainer] Package published by user1`, pl.FormattedBody)
})
t.Run("Wiki", func(t *testing.T) {
@@ -160,24 +159,24 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.FormattedBody)
p.Action = api.HookWikiEdited
pl, err = mc.Wiki(p)
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.FormattedBody)
p.Action = api.HookWikiDeleted
pl, err = mc.Wiki(p)
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.FormattedBody)
})
t.Run("Release", func(t *testing.T) {
@@ -187,8 +186,8 @@ func TestMatrixPayload(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pl)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by [user1](https://try.gitea.io/user1)", pl.Body)
- assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.FormattedBody)
+ assert.Equal(t, "[test/repo] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by user1", pl.Body)
+ assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.FormattedBody)
})
}
@@ -211,18 +210,18 @@ func TestMatrixJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := matrixHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := matrixHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
assert.Equal(t, "PUT", req.Method)
- assert.Equal(t, "/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path)
+ assert.Equal(t, "/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message/86aaa4d69df5aa487cb0148af4ae7e546933057b", req.URL.Path)
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body MatrixPayload
err = json.NewDecoder(req.Body).Decode(&body)
require.NoError(t, err)
- assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", body.Body)
+ assert.Equal(t, "[test/repo] user1 pushed 2 commits to test:\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", body.Body)
}
func Test_getTxnID(t *testing.T) {
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index 736d084a8c..3b35c407e1 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -11,13 +11,13 @@ import (
"net/url"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type msteamsHandler struct{}
@@ -326,6 +326,23 @@ func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error)
), nil
}
+func (m msteamsConvertor) Action(p *api.ActionPayload) (MSTeamsPayload, error) {
+ title, color := getActionPayloadInfo(p, noneLinkFormatter)
+
+ // TODO: is TriggerUser correct here?
+ // if you'd like to test these proprietary services, see the discussion on: https://codeberg.org/forgejo/forgejo/pulls/7508
+ return createMSTeamsPayload(
+ p.Run.Repo,
+ p.Run.TriggerUser,
+ title,
+ "",
+ p.Run.HTMLURL,
+ color,
+ // TODO: does this make any sense?
+ &MSTeamsFact{"Action:", p.Run.Title},
+ ), nil
+}
+
func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) MSTeamsPayload {
facts := make([]MSTeamsFact, 0, 2)
if r != nil {
diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go
index a97e9f3de3..da6439f198 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -4,13 +4,12 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -336,7 +335,7 @@ func TestMSTeamsPayload(t *testing.T) {
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
- assert.Equal(t, "", pl.Sections[0].Text)
+ assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
@@ -357,7 +356,7 @@ func TestMSTeamsPayload(t *testing.T) {
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
- assert.Equal(t, "", pl.Sections[0].Text)
+ assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
@@ -439,7 +438,7 @@ func TestMSTeamsJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := msteamsHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := msteamsHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index fed33d8008..009efc994f 100644
--- a/services/webhook/notifier.go
+++ b/services/webhook/notifier.go
@@ -6,20 +6,21 @@ package webhook
import (
"context"
- issues_model "code.gitea.io/gitea/models/issues"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/convert"
- notify_service "code.gitea.io/gitea/services/notify"
+ actions_model "forgejo.org/models/actions"
+ issues_model "forgejo.org/models/issues"
+ packages_model "forgejo.org/models/packages"
+ "forgejo.org/models/perm"
+ access_model "forgejo.org/models/perm/access"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/convert"
+ notify_service "forgejo.org/services/notify"
)
func init() {
@@ -599,6 +600,10 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
}
func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+ if len(commits.Commits) > setting.Webhook.PayloadCommitLimit {
+ commits.Commits = commits.Commits[:setting.Webhook.PayloadCommitLimit]
+ }
+
apiPusher := convert.ToUser(ctx, pusher, nil)
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
if err != nil {
@@ -840,6 +845,10 @@ func (m *webhookNotifier) DeleteRelease(ctx context.Context, doer *user_model.Us
}
func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+ if len(commits.Commits) > setting.Webhook.PayloadCommitLimit {
+ commits.Commits = commits.Commits[:setting.Webhook.PayloadCommitLimit]
+ }
+
apiPusher := convert.ToUser(ctx, pusher, nil)
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
if err != nil {
@@ -879,6 +888,45 @@ func (m *webhookNotifier) PackageDelete(ctx context.Context, doer *user_model.Us
notifyPackage(ctx, doer, pd, api.HookPackageDeleted)
}
+func (m *webhookNotifier) ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) {
+ source := EventSource{
+ Repository: run.Repo,
+ Owner: run.TriggerUser,
+ }
+
+ // The doer is the one whose perspective is used to view this ActionRun.
+ // In the best case we use the user that created the webhook.
+ // Unfortunately we don't know who that was.
+ // So instead we use the repo owner, who is able to create webhooks and allow others to do so by making them repo admins.
+ // This is pretty close to perfect.
+ doer := run.Repo.Owner
+
+ payload := &api.ActionPayload{
+ Run: convert.ToActionRun(ctx, run, doer),
+ LastRun: convert.ToActionRun(ctx, lastRun, doer),
+ PriorStatus: priorStatus.String(),
+ }
+
+ if run.Status.IsSuccess() {
+ payload.Action = api.HookActionSuccess
+ if err := PrepareWebhooks(ctx, source, webhook_module.HookEventActionRunSuccess, payload); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+ // send another event when this is a recover
+ if lastRun != nil && !lastRun.Status.IsSuccess() {
+ payload.Action = api.HookActionRecover
+ if err := PrepareWebhooks(ctx, source, webhook_module.HookEventActionRunRecover, payload); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+ }
+ } else {
+ payload.Action = api.HookActionFailure
+ if err := PrepareWebhooks(ctx, source, webhook_module.HookEventActionRunFailure, payload); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+ }
+}
+
func notifyPackage(ctx context.Context, sender *user_model.User, pd *packages_model.PackageDescriptor, action api.HookPackageAction) {
source := EventSource{
Repository: pd.Repository,
diff --git a/services/webhook/notifier_test.go b/services/webhook/notifier_test.go
new file mode 100644
index 0000000000..a810de91c1
--- /dev/null
+++ b/services/webhook/notifier_test.go
@@ -0,0 +1,311 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package webhook
+
+import (
+ "testing"
+
+ actions_model "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/repository"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/test"
+ webhook_module "forgejo.org/modules/webhook"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func pushCommits() *repository.PushCommits {
+ pushCommits := repository.NewPushCommits()
+ pushCommits.Commits = []*repository.PushCommit{
+ {
+ Sha1: "2c54faec6c45d31c1abfaecdab471eac6633738a",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "not signed commit",
+ },
+ {
+ Sha1: "205ac761f3326a7ebe416e8673760016450b5cec",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "good signed commit (with not yet validated email)",
+ },
+ {
+ Sha1: "1032bbf17fbc0d9c95bb5418dabe8f8c99278700",
+ CommitterEmail: "user2@example.com",
+ CommitterName: "User2",
+ AuthorEmail: "user2@example.com",
+ AuthorName: "User2",
+ Message: "good signed commit",
+ },
+ }
+ pushCommits.HeadCommit = &repository.PushCommit{Sha1: "2c54faec6c45d31c1abfaecdab471eac6633738a"}
+ return pushCommits
+}
+
+func TestSyncPushCommits(t *testing.T) {
+ defer unittest.OverrideFixtures("services/webhook/TestPushCommits")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: user.ID})
+
+ t.Run("All commits", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master-1")}, pushCommits())
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("payload_content LIKE '%master-1%'"))
+
+ var payloadContent structs.PushPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Len(t, payloadContent.Commits, 3)
+ })
+
+ t.Run("Only one commit", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 1)()
+
+ NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main-1")}, pushCommits())
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("payload_content LIKE '%main-1%'"))
+
+ var payloadContent structs.PushPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Len(t, payloadContent.Commits, 1)
+ assert.Equal(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID)
+ })
+}
+
+func TestPushCommits(t *testing.T) {
+ defer unittest.OverrideFixtures("services/webhook/TestPushCommits")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: user.ID})
+
+ t.Run("All commits", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master-2")}, pushCommits())
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("payload_content LIKE '%master-2%'"))
+
+ var payloadContent structs.PushPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Len(t, payloadContent.Commits, 3)
+ })
+
+ t.Run("Only one commit", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 1)()
+
+ NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main-2")}, pushCommits())
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("payload_content LIKE '%main-2%'"))
+
+ var payloadContent structs.PushPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Len(t, payloadContent.Commits, 1)
+ assert.Equal(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID)
+ })
+}
+
+func assertActionEqual(t *testing.T, expectedRun *actions_model.ActionRun, actualRun *structs.ActionRun) {
+ assert.NotNil(t, expectedRun)
+ assert.NotNil(t, actualRun)
+ // only test a few things
+ assert.Equal(t, expectedRun.ID, actualRun.ID)
+ assert.Equal(t, expectedRun.Status.String(), actualRun.Status)
+ assert.Equal(t, expectedRun.Index, actualRun.Index)
+ assert.Equal(t, expectedRun.RepoID, actualRun.Repo.ID)
+ // convert to unix because of time zones
+ assert.Equal(t, expectedRun.Stopped.AsTime().Unix(), actualRun.Stopped.Unix())
+ assert.Equal(t, expectedRun.Title, actualRun.Title)
+ assert.Equal(t, expectedRun.WorkflowID, actualRun.WorkflowID)
+}
+
+func TestAction(t *testing.T) {
+ defer unittest.OverrideFixtures("services/webhook/TestPushCommits")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ triggerUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: triggerUser.ID})
+
+ oldSuccessRun := &actions_model.ActionRun{
+ ID: 1,
+ Status: actions_model.StatusSuccess,
+ Index: 1,
+ RepoID: repo.ID,
+ Stopped: 1693648027,
+ WorkflowID: "some_workflow",
+ Title: "oldSuccessRun",
+ TriggerUser: triggerUser,
+ TriggerUserID: triggerUser.ID,
+ TriggerEvent: "push",
+ }
+ oldSuccessRun.LoadAttributes(db.DefaultContext)
+ oldFailureRun := &actions_model.ActionRun{
+ ID: 1,
+ Status: actions_model.StatusFailure,
+ Index: 1,
+ RepoID: repo.ID,
+ Stopped: 1693648027,
+ WorkflowID: "some_workflow",
+ Title: "oldFailureRun",
+ TriggerUser: triggerUser,
+ TriggerUserID: triggerUser.ID,
+ TriggerEvent: "push",
+ }
+ oldFailureRun.LoadAttributes(db.DefaultContext)
+ newSuccessRun := &actions_model.ActionRun{
+ ID: 1,
+ Status: actions_model.StatusSuccess,
+ Index: 1,
+ RepoID: repo.ID,
+ Stopped: 1693648327,
+ WorkflowID: "some_workflow",
+ Title: "newSuccessRun",
+ TriggerUser: triggerUser,
+ TriggerUserID: triggerUser.ID,
+ TriggerEvent: "push",
+ }
+ newSuccessRun.LoadAttributes(db.DefaultContext)
+ newFailureRun := &actions_model.ActionRun{
+ ID: 1,
+ Status: actions_model.StatusFailure,
+ Index: 1,
+ RepoID: repo.ID,
+ Stopped: 1693648327,
+ WorkflowID: "some_workflow",
+ Title: "newFailureRun",
+ TriggerUser: triggerUser,
+ TriggerUserID: triggerUser.ID,
+ TriggerEvent: "push",
+ }
+ newFailureRun.LoadAttributes(db.DefaultContext)
+
+ t.Run("Successful Run after Nothing", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newSuccessRun, actions_model.StatusWaiting, nil)
+
+ // there's only one of these at the time
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_success' AND payload_content LIKE '%success%newSuccessRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunSuccess, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionSuccess, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newSuccessRun, payloadContent.Run)
+ assert.Nil(t, payloadContent.LastRun)
+ })
+
+ t.Run("Successful Run after Failure", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newSuccessRun, actions_model.StatusWaiting, oldFailureRun)
+
+ {
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_success' AND payload_content LIKE '%success%newSuccessRun%oldFailureRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunSuccess, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionSuccess, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newSuccessRun, payloadContent.Run)
+ assertActionEqual(t, oldFailureRun, payloadContent.LastRun)
+ }
+ {
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_recover' AND payload_content LIKE '%recover%newSuccessRun%oldFailureRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunRecover, hookTask.EventType)
+
+ log.Error("something: %s", hookTask.PayloadContent)
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionRecover, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newSuccessRun, payloadContent.Run)
+ assertActionEqual(t, oldFailureRun, payloadContent.LastRun)
+ }
+ })
+
+ t.Run("Successful Run after Success", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newSuccessRun, actions_model.StatusWaiting, oldSuccessRun)
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_success' AND payload_content LIKE '%success%newSuccessRun%oldSuccessRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunSuccess, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionSuccess, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newSuccessRun, payloadContent.Run)
+ assertActionEqual(t, oldSuccessRun, payloadContent.LastRun)
+ })
+
+ t.Run("Failed Run after Nothing", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newFailureRun, actions_model.StatusWaiting, nil)
+
+ // there should only be this one at the time
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_failure' AND payload_content LIKE '%failure%newFailureRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunFailure, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionFailure, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newFailureRun, payloadContent.Run)
+ assert.Nil(t, payloadContent.LastRun)
+ })
+
+ t.Run("Failed Run after Failure", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newFailureRun, actions_model.StatusWaiting, oldFailureRun)
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_failure' AND payload_content LIKE '%failure%newFailureRun%oldFailureRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunFailure, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionFailure, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newFailureRun, payloadContent.Run)
+ assertActionEqual(t, oldFailureRun, payloadContent.LastRun)
+ })
+
+ t.Run("Failed Run after Success", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Webhook.PayloadCommitLimit, 10)()
+
+ NewNotifier().ActionRunNowDone(db.DefaultContext, newFailureRun, actions_model.StatusWaiting, oldSuccessRun)
+
+ hookTask := unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{}, unittest.Cond("event_type == 'action_run_failure' AND payload_content LIKE '%failure%newFailureRun%oldSuccessRun%'"))
+ assert.Equal(t, webhook_module.HookEventActionRunFailure, hookTask.EventType)
+
+ var payloadContent structs.ActionPayload
+ require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
+ assert.Equal(t, structs.HookActionFailure, payloadContent.Action)
+ assert.Equal(t, actions_model.StatusWaiting.String(), payloadContent.PriorStatus)
+ assertActionEqual(t, newFailureRun, payloadContent.Run)
+ assertActionEqual(t, oldSuccessRun, payloadContent.LastRun)
+ })
+}
diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go
index 9831a4e008..7ae3e0c48f 100644
--- a/services/webhook/packagist.go
+++ b/services/webhook/packagist.go
@@ -10,12 +10,12 @@ import (
"net/http"
"net/url"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type packagistHandler struct{}
diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go
index 320c1c85a1..e5bf4ec8d1 100644
--- a/services/webhook/packagist_test.go
+++ b/services/webhook/packagist_test.go
@@ -4,14 +4,13 @@
package webhook
import (
- "context"
"fmt"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -53,7 +52,7 @@ func TestPackagistPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := packagistHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := packagistHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/shared/img.go b/services/webhook/shared/img.go
index 2d65ba4e0f..95286c563e 100644
--- a/services/webhook/shared/img.go
+++ b/services/webhook/shared/img.go
@@ -5,7 +5,7 @@ import (
"html/template"
"strconv"
- "code.gitea.io/gitea/modules/setting"
+ "forgejo.org/modules/setting"
)
func ImgIcon(name string, size int) template.HTML {
diff --git a/services/webhook/shared/payloader.go b/services/webhook/shared/payloader.go
index cf0bfa82cb..e3be4c4b4c 100644
--- a/services/webhook/shared/payloader.go
+++ b/services/webhook/shared/payloader.go
@@ -14,10 +14,10 @@ import (
"io"
"net/http"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
)
var ErrPayloadTypeNotSupported = errors.New("unsupported webhook event")
@@ -36,6 +36,7 @@ type PayloadConvertor[T any] interface {
Release(*api.ReleasePayload) (T, error)
Wiki(*api.WikiPayload) (T, error)
Package(*api.PackagePayload) (T, error)
+ Action(*api.ActionPayload) (T, error)
}
func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (T, error) {
@@ -86,6 +87,8 @@ func NewPayload[T any](rc PayloadConvertor[T], data []byte, event webhook_module
return convertUnmarshalledJSON(rc.Wiki, data)
case webhook_module.HookEventPackage:
return convertUnmarshalledJSON(rc.Package, data)
+ case webhook_module.HookEventActionRunFailure, webhook_module.HookEventActionRunRecover, webhook_module.HookEventActionRunSuccess:
+ return convertUnmarshalledJSON(rc.Action, data)
}
var t T
return t, fmt.Errorf("newPayload unsupported event: %s", event)
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index 1fa33444f3..8c61e7ba25 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -11,16 +11,15 @@ import (
"regexp"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- gitea_context "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ gitea_context "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
"code.forgejo.org/go-chi/binding"
)
@@ -143,11 +142,11 @@ func SlackLinkToRef(repoURL, ref string) string {
return SlackLinkFormatter(url, refName)
}
+// TODO: fix spelling to Converter
// Create implements payloadConvertor Create method
func (s slackConvertor) Create(p *api.CreatePayload) (SlackPayload, error) {
- repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
- text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
+ text := fmt.Sprintf("[%s:%s] %s created by %s", p.Repo.FullName, refLink, p.RefType, p.Sender.UserName)
return s.createPayload(text, nil), nil
}
@@ -240,9 +239,8 @@ func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) {
commitString = commitDesc
}
- repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
branchLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
- text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
+ text := fmt.Sprintf("[%s:%s] %s pushed by %s", p.Repo.FullName, branchLink, commitString, p.Pusher.UserName)
var attachmentText string
// for each commit, generate attachment text
@@ -283,10 +281,8 @@ func (s slackConvertor) PullRequest(p *api.PullRequestPayload) (SlackPayload, er
// Review implements payloadConvertor Review method
func (s slackConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (SlackPayload, error) {
- senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
- repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
if p.Action == api.HookIssueReviewed {
@@ -295,7 +291,7 @@ func (s slackConvertor) Review(p *api.PullRequestPayload, event webhook_module.H
return SlackPayload{}, err
}
- text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
+ text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", p.Repository.FullName, action, title, titleLink, p.Sender.UserName)
}
return s.createPayload(text, nil), nil
@@ -303,20 +299,25 @@ func (s slackConvertor) Review(p *api.PullRequestPayload, event webhook_module.H
// Repository implements payloadConvertor Repository method
func (s slackConvertor) Repository(p *api.RepositoryPayload) (SlackPayload, error) {
- senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
switch p.Action {
case api.HookRepoCreated:
- text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink)
+ text = fmt.Sprintf("[%s] Repository created by %s", repoLink, p.Sender.UserName)
case api.HookRepoDeleted:
- text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink)
+ text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, p.Sender.UserName)
}
return s.createPayload(text, nil), nil
}
+func (s slackConvertor) Action(p *api.ActionPayload) (SlackPayload, error) {
+ text, _ := getActionPayloadInfo(p, SlackLinkFormatter)
+
+ return s.createPayload(text, nil), nil
+}
+
func (s slackConvertor) createPayload(text string, attachments []SlackAttachment) SlackPayload {
return SlackPayload{
Channel: s.Channel,
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index 3d801843ae..62090fd310 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -4,13 +4,12 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,7 +24,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Create(p)
require.NoError(t, err)
- assert.Equal(t, "[:] branch created by user1", pl.Text)
+ assert.Equal(t, "[test/repo:] branch created by user1", pl.Text)
})
t.Run("Delete", func(t *testing.T) {
@@ -52,7 +51,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Push(p)
require.NoError(t, err)
- assert.Equal(t, "[:] 2 new commits pushed by user1", pl.Text)
+ assert.Equal(t, "[test/repo:] 2 new commits pushed by user1", pl.Text)
})
t.Run("Issue", func(t *testing.T) {
@@ -62,13 +61,13 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Issue(p)
require.NoError(t, err)
- assert.Equal(t, "[] Issue opened: by ", pl.Text)
+ assert.Equal(t, "[test/repo] Issue opened: by user1", pl.Text)
p.Action = api.HookIssueClosed
pl, err = sc.Issue(p)
require.NoError(t, err)
- assert.Equal(t, "[] Issue closed: by ", pl.Text)
+ assert.Equal(t, "[test/repo] Issue closed: by user1", pl.Text)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -77,7 +76,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.IssueComment(p)
require.NoError(t, err)
- assert.Equal(t, "[] New comment on issue by ", pl.Text)
+ assert.Equal(t, "[test/repo] New comment on issue by user1", pl.Text)
})
t.Run("PullRequest", func(t *testing.T) {
@@ -86,7 +85,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.PullRequest(p)
require.NoError(t, err)
- assert.Equal(t, "[] Pull request opened: by ", pl.Text)
+ assert.Equal(t, "[test/repo] Pull request opened: by user1", pl.Text)
})
t.Run("PullRequestComment", func(t *testing.T) {
@@ -95,7 +94,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.IssueComment(p)
require.NoError(t, err)
- assert.Equal(t, "[] New comment on pull request by ", pl.Text)
+ assert.Equal(t, "[test/repo] New comment on pull request by user1", pl.Text)
})
t.Run("Review", func(t *testing.T) {
@@ -105,7 +104,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Review(p, webhook_module.HookEventPullRequestReviewApproved)
require.NoError(t, err)
- assert.Equal(t, "[] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by ", pl.Text)
+ assert.Equal(t, "[test/repo] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1", pl.Text)
})
t.Run("Repository", func(t *testing.T) {
@@ -114,7 +113,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Repository(p)
require.NoError(t, err)
- assert.Equal(t, "[] Repository created by ", pl.Text)
+ assert.Equal(t, "[] Repository created by user1", pl.Text)
})
t.Run("Package", func(t *testing.T) {
@@ -123,7 +122,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Package(p)
require.NoError(t, err)
- assert.Equal(t, "Package created: by ", pl.Text)
+ assert.Equal(t, "Package created: by user1", pl.Text)
})
t.Run("Wiki", func(t *testing.T) {
@@ -133,19 +132,19 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, "[] New wiki page '' (Wiki change comment) by ", pl.Text)
+ assert.Equal(t, "[test/repo] New wiki page '' (Wiki change comment) by user1", pl.Text)
p.Action = api.HookWikiEdited
pl, err = sc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, "[] Wiki page '' edited (Wiki change comment) by ", pl.Text)
+ assert.Equal(t, "[test/repo] Wiki page '' edited (Wiki change comment) by user1", pl.Text)
p.Action = api.HookWikiDeleted
pl, err = sc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, "[] Wiki page '' deleted by ", pl.Text)
+ assert.Equal(t, "[test/repo] Wiki page '' deleted by user1", pl.Text)
})
t.Run("Release", func(t *testing.T) {
@@ -154,7 +153,7 @@ func TestSlackPayload(t *testing.T) {
pl, err := sc.Release(p)
require.NoError(t, err)
- assert.Equal(t, "[] Release created: by ", pl.Text)
+ assert.Equal(t, "[test/repo] Release created: by user1", pl.Text)
})
}
@@ -178,7 +177,7 @@ func TestSlackJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := slackHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := slackHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
@@ -190,7 +189,7 @@ func TestSlackJSONPayload(t *testing.T) {
var body SlackPayload
err = json.NewDecoder(req.Body).Decode(&body)
require.NoError(t, err)
- assert.Equal(t, "[:] 2 new commits pushed by user1", body.Text)
+ assert.Equal(t, "[test/repo:] 2 new commits pushed by user1", body.Text)
}
func TestIsValidSlackChannel(t *testing.T) {
diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go
index 5a34503a7d..2593afb0b2 100644
--- a/services/webhook/sourcehut/builds.go
+++ b/services/webhook/sourcehut/builds.go
@@ -8,21 +8,22 @@ import (
"context"
"fmt"
"html/template"
+ "io"
"io/fs"
"net/http"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- gitea_context "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ gitea_context "forgejo.org/services/context"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
"code.forgejo.org/go-chi/binding"
"gopkg.in/yaml.v3"
@@ -189,11 +190,15 @@ func (pc sourcehutConvertor) Package(_ *api.PackagePayload) (graphqlPayload[buil
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
}
-// mustBuildManifest adjusts the manifest to submit to the builds service
+func (pc sourcehutConvertor) Action(_ *api.ActionPayload) (graphqlPayload[buildsVariables], error) {
+ return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
+}
+
+// newPayload opens and adjusts the manifest to submit to the builds service
//
// in case of an error the Error field will be set, to be visible by the end-user under recent deliveries
func (pc sourcehutConvertor) newPayload(repo *api.Repository, commitID, ref, note string, trusted bool) (graphqlPayload[buildsVariables], error) {
- manifest, err := pc.buildManifest(repo, commitID, ref)
+ manifest, err := pc.constructManifest(repo, commitID, ref)
if err != nil {
if len(manifest) == 0 {
return graphqlPayload[buildsVariables]{}, err
@@ -238,9 +243,9 @@ func (pc sourcehutConvertor) newPayload(repo *api.Repository, commitID, ref, not
}, nil
}
-// buildManifest adjusts the manifest to submit to the builds service
+// constructManifest opens and adjusts the manifest to submit to the builds service
// in case of an error the []byte might contain an error that can be displayed to the user
-func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRef string) ([]byte, error) {
+func (pc sourcehutConvertor) constructManifest(repo *api.Repository, commitID, gitRef string) ([]byte, error) {
gitRepo, err := gitrepo.OpenRepository(pc.ctx, repo)
if err != nil {
msg := "could not open repository"
@@ -265,6 +270,10 @@ func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRe
}
defer r.Close()
+ return adjustManifest(repo, commitID, gitRef, r, pc.meta.ManifestPath)
+}
+
+func adjustManifest(repo *api.Repository, commitID, gitRef string, r io.Reader, path string) ([]byte, error) {
// reference: https://man.sr.ht/builds.sr.ht/manifest.md
var manifest struct {
Sources []string `yaml:"sources"`
@@ -273,7 +282,7 @@ func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRe
Rest map[string]yaml.Node `yaml:",inline"`
}
if err := yaml.NewDecoder(r).Decode(&manifest); err != nil {
- msg := fmt.Sprintf("could not decode manifest %q", pc.meta.ManifestPath)
+ msg := fmt.Sprintf("could not decode manifest %q", path)
return []byte(msg), fmt.Errorf(msg+": %w", err)
}
@@ -284,16 +293,21 @@ func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRe
manifest.Environment["BUILD_SUBMITTER_URL"] = setting.AppURL
manifest.Environment["GIT_REF"] = gitRef
- source := repo.CloneURL + "#" + commitID
found := false
for i, s := range manifest.Sources {
- if s == repo.CloneURL {
- manifest.Sources[i] = source
+ if s == repo.CloneURL || s == repo.SSHURL {
+ manifest.Sources[i] = s + "#" + commitID
found = true
break
}
}
if !found {
+ source := repo.CloneURL
+ if repo.Private || setting.Repository.DisableHTTPGit {
+ // default to ssh for private repos or when git clone is disabled over http
+ source = repo.SSHURL
+ }
+ source += "#" + commitID
manifest.Sources = append(manifest.Sources, source)
}
diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go
index 1a37279c99..ac4172f5ff 100644
--- a/services/webhook/sourcehut/builds_test.go
+++ b/services/webhook/sourcehut/builds_test.go
@@ -4,17 +4,17 @@
package sourcehut
import (
- "context"
+ "strings"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/test"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/test"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/webhook/shared"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,7 +25,7 @@ func gitInit(t testing.TB) {
return
}
t.Cleanup(test.MockVariableValue(&setting.Git.HomePath, t.TempDir()))
- require.NoError(t, git.InitSimple(context.Background()))
+ require.NoError(t, git.InitSimple(t.Context()))
}
func TestSourcehutBuildsPayload(t *testing.T) {
@@ -371,7 +371,7 @@ func TestSourcehutJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := BuildsHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := BuildsHandler{}.NewRequest(t.Context(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
@@ -384,3 +384,134 @@ func TestSourcehutJSONPayload(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "json test", body.Variables.Note)
}
+
+func TestSourcehutAdjustManifest(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://example.forgejo.org/")()
+ t.Run("without sources", func(t *testing.T) {
+ repo := &api.Repository{
+ CloneURL: "http://localhost:3000/testdata/repo.git",
+ }
+
+ manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge
+tasks:
+ - say-hello: |
+ echo hello
+ - say-world: echo world`), ".build.yml")
+
+ require.NoError(t, err)
+ assert.Equal(t, `sources:
+ - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
+environment:
+ BUILD_SUBMITTER: forgejo
+ BUILD_SUBMITTER_URL: https://example.forgejo.org/
+ GIT_REF: refs/heads/main
+image: alpine/edge
+tasks:
+ - say-hello: |
+ echo hello
+ - say-world: echo world
+`, string(manifest))
+ })
+
+ t.Run("with other sources", func(t *testing.T) {
+ repo := &api.Repository{
+ CloneURL: "http://localhost:3000/testdata/repo.git",
+ }
+
+ manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge
+sources:
+- http://other.example.conm/repo.git
+tasks:
+ - hello: echo world`), ".build.yml")
+
+ require.NoError(t, err)
+ assert.Equal(t, `sources:
+ - http://other.example.conm/repo.git
+ - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
+environment:
+ BUILD_SUBMITTER: forgejo
+ BUILD_SUBMITTER_URL: https://example.forgejo.org/
+ GIT_REF: refs/heads/main
+image: alpine/edge
+tasks:
+ - hello: echo world
+`, string(manifest))
+ })
+
+ t.Run("with same source", func(t *testing.T) {
+ repo := &api.Repository{
+ CloneURL: "http://localhost:3000/testdata/repo.git",
+ }
+
+ manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge
+sources:
+- http://localhost:3000/testdata/repo.git
+- http://other.example.conm/repo.git
+tasks:
+ - hello: echo world`), ".build.yml")
+
+ require.NoError(t, err)
+ assert.Equal(t, `sources:
+ - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
+ - http://other.example.conm/repo.git
+environment:
+ BUILD_SUBMITTER: forgejo
+ BUILD_SUBMITTER_URL: https://example.forgejo.org/
+ GIT_REF: refs/heads/main
+image: alpine/edge
+tasks:
+ - hello: echo world
+`, string(manifest))
+ })
+
+ t.Run("with ssh source", func(t *testing.T) {
+ repo := &api.Repository{
+ CloneURL: "http://localhost:3000/testdata/repo.git",
+ SSHURL: "git@localhost:testdata/repo.git",
+ }
+
+ manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge
+sources:
+- git@localhost:testdata/repo.git
+- http://other.example.conm/repo.git
+tasks:
+ - hello: echo world`), ".build.yml")
+
+ require.NoError(t, err)
+ assert.Equal(t, `sources:
+ - git@localhost:testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
+ - http://other.example.conm/repo.git
+environment:
+ BUILD_SUBMITTER: forgejo
+ BUILD_SUBMITTER_URL: https://example.forgejo.org/
+ GIT_REF: refs/heads/main
+image: alpine/edge
+tasks:
+ - hello: echo world
+`, string(manifest))
+ })
+
+ t.Run("private without source", func(t *testing.T) {
+ repo := &api.Repository{
+ CloneURL: "http://localhost:3000/testdata/repo.git",
+ SSHURL: "git@localhost:testdata/repo.git",
+ Private: true,
+ }
+
+ manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge
+tasks:
+ - hello: echo world`), ".build.yml")
+
+ require.NoError(t, err)
+ assert.Equal(t, `sources:
+ - git@localhost:testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
+environment:
+ BUILD_SUBMITTER: forgejo
+ BUILD_SUBMITTER_URL: https://example.forgejo.org/
+ GIT_REF: refs/heads/main
+image: alpine/edge
+tasks:
+ - hello: echo world
+`, string(manifest))
+ })
+}
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index bacfa64db5..e6897f68bc 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -11,15 +11,15 @@ import (
"net/url"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/markup"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type telegramHandler struct{}
@@ -79,7 +79,7 @@ func (telegramHandler) Metadata(w *webhook_model.Webhook) any {
func (t telegramConvertor) Create(p *api.CreatePayload) (TelegramPayload, error) {
// created tag/branch
refName := git.RefName(p.Ref).ShortName()
- title := fmt.Sprintf(`[%s] %s %s created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
+ title := fmt.Sprintf(`[%s] %s %s created`, p.Repo.FullName, p.RefType,
p.Repo.HTMLURL+"/src/"+refName, refName)
return createTelegramPayload(title), nil
@@ -89,7 +89,7 @@ func (t telegramConvertor) Create(p *api.CreatePayload) (TelegramPayload, error)
func (t telegramConvertor) Delete(p *api.DeletePayload) (TelegramPayload, error) {
// created tag/branch
refName := git.RefName(p.Ref).ShortName()
- title := fmt.Sprintf(`[%s] %s %s deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
+ title := fmt.Sprintf(`[%s] %s %s deleted`, p.Repo.FullName, p.RefType,
p.Repo.HTMLURL+"/src/"+refName, refName)
return createTelegramPayload(title), nil
@@ -108,19 +108,13 @@ func (t telegramConvertor) Push(p *api.PushPayload) (TelegramPayload, error) {
branchName = git.RefName(p.Ref).ShortName()
commitDesc string
)
-
- var titleLink string
if p.TotalCommits == 1 {
commitDesc = "1 new commit"
- titleLink = p.Commits[0].URL
} else {
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
- titleLink = p.CompareURL
}
- if titleLink == "" {
- titleLink = p.Repo.HTMLURL + "/src/" + branchName
- }
- title := fmt.Sprintf(`[%s:%s] %s`, p.Repo.HTMLURL, p.Repo.FullName, titleLink, branchName, commitDesc)
+
+ title := fmt.Sprintf(`[%s:%s] %s`, p.Repo.FullName, branchName, commitDesc)
var text string
// for each commit, generate attachment text
@@ -211,6 +205,12 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro
return createTelegramPayload(text), nil
}
+func (telegramConvertor) Action(p *api.ActionPayload) (TelegramPayload, error) {
+ text, _ := getActionPayloadInfo(p, htmlLinkFormatter)
+
+ return createTelegramPayload(text), nil
+}
+
func createTelegramPayload(message string) TelegramPayload {
return TelegramPayload{
Message: markup.Sanitize(strings.TrimSpace(message)),
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index 0e27535a03..5066e55b8c 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -4,13 +4,12 @@
package webhook
import (
- "context"
"testing"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/json"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -33,7 +32,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Create(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] branch test created`, pl.Message)
+ assert.Equal(t, `[test/repo] branch test created`, pl.Message)
})
t.Run("Delete", func(t *testing.T) {
@@ -42,7 +41,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Delete(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] branch test deleted`, pl.Message)
+ assert.Equal(t, `[test/repo] branch test deleted`, pl.Message)
})
t.Run("Fork", func(t *testing.T) {
@@ -60,7 +59,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Push(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo:test] 2 new commits
+ assert.Equal(t, `[test/repo:test] 2 new commits
[2020558] commit message - user1
[2020558] commit message - user1`, pl.Message)
})
@@ -72,7 +71,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Issue(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1
+ assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1
issue body`, pl.Message)
@@ -80,7 +79,7 @@ issue body`, pl.Message)
pl, err = tc.Issue(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.Message)
+ assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.Message)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -89,7 +88,7 @@ issue body`, pl.Message)
pl, err := tc.IssueComment(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1
+ assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1
more info needed`, pl.Message)
})
@@ -99,7 +98,7 @@ more info needed`, pl.Message)
pl, err := tc.PullRequest(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1
+ assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1
fixes bug #2`, pl.Message)
})
@@ -109,7 +108,7 @@ fixes bug #2`, pl.Message)
pl, err := tc.IssueComment(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1
+ assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1
changes requested`, pl.Message)
})
@@ -139,7 +138,7 @@ good job`, pl.Message)
pl, err := tc.Package(p)
require.NoError(t, err)
- assert.Equal(t, `Package created: GiteaContainer:latest by user1`, pl.Message)
+ assert.Equal(t, `Package created: GiteaContainer:latest by user1`, pl.Message)
})
t.Run("Wiki", func(t *testing.T) {
@@ -149,19 +148,19 @@ good job`, pl.Message)
pl, err := tc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.Message)
+ assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.Message)
p.Action = api.HookWikiEdited
pl, err = tc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.Message)
+ assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.Message)
p.Action = api.HookWikiDeleted
pl, err = tc.Wiki(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.Message)
+ assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.Message)
})
t.Run("Release", func(t *testing.T) {
@@ -170,7 +169,7 @@ good job`, pl.Message)
pl, err := tc.Release(p)
require.NoError(t, err)
- assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.Message)
+ assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.Message)
})
}
@@ -194,7 +193,7 @@ func TestTelegramJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := telegramHandler{}.NewRequest(context.Background(), hook, task)
+ req, reqBody, err := telegramHandler{}.NewRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
@@ -206,7 +205,7 @@ func TestTelegramJSONPayload(t *testing.T) {
var body TelegramPayload
err = json.NewDecoder(req.Body).Decode(&body)
require.NoError(t, err)
- assert.Equal(t, `[test/repo:test] 2 new commits
+ assert.Equal(t, `[test/repo:test] 2 new commits
[2020558] commit message - user1
[2020558] commit message - user1`, body.Message)
}
diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go
index 1366ea8e8f..ecbbfcfbd6 100644
--- a/services/webhook/webhook.go
+++ b/services/webhook/webhook.go
@@ -11,21 +11,21 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/queue"
- "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"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/sourcehut"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/queue"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/sourcehut"
"github.com/gobwas/glob"
)
@@ -103,7 +103,7 @@ type EventSource struct {
Owner *user_model.User
}
-// handle delivers hook tasks
+// handler delivers hook tasks
func handler(items ...int64) []int64 {
ctx := graceful.GetManager().HammerContext()
diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go
index 816940a2b5..15cb8f620c 100644
--- a/services/webhook/webhook_test.go
+++ b/services/webhook/webhook_test.go
@@ -7,12 +7,16 @@ import (
"fmt"
"testing"
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- webhook_model "code.gitea.io/gitea/models/webhook"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/setting"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/test"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/convert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -98,3 +102,12 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
})
}
}
+
+func TestWebhookUserMail(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ defer test.MockVariableValue(&setting.Service.NoReplyAddress, "no-reply.com")()
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(db.DefaultContext, user, nil).Email)
+ assert.Equal(t, user.Email, convert.ToUser(db.DefaultContext, user, user).Email)
+}
diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go
index 87f8bb8b18..5c765b0754 100644
--- a/services/webhook/wechatwork.go
+++ b/services/webhook/wechatwork.go
@@ -10,12 +10,12 @@ import (
"net/http"
"strings"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- webhook_module "code.gitea.io/gitea/modules/webhook"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/webhook/shared"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ webhook_module "forgejo.org/modules/webhook"
+ "forgejo.org/services/forms"
+ "forgejo.org/services/webhook/shared"
)
type wechatworkHandler struct{}
@@ -201,6 +201,12 @@ func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload,
return newWechatworkMarkdownPayload(text), nil
}
+func (wc wechatworkConvertor) Action(p *api.ActionPayload) (WechatworkPayload, error) {
+ text, _ := getActionPayloadInfo(p, noneLinkFormatter)
+
+ return newWechatworkMarkdownPayload(text), nil
+}
+
type wechatworkConvertor struct{}
var _ shared.PayloadConvertor[WechatworkPayload] = wechatworkConvertor{}
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 63196aa862..cf1477e72c 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -11,17 +11,17 @@ import (
"os"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/sync"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
- repo_service "code.gitea.io/gitea/services/repository"
+ repo_model "forgejo.org/models/repo"
+ system_model "forgejo.org/models/system"
+ "forgejo.org/models/unit"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
+ "forgejo.org/modules/log"
+ repo_module "forgejo.org/modules/repository"
+ "forgejo.org/modules/sync"
+ asymkey_service "forgejo.org/services/asymkey"
+ repo_service "forgejo.org/services/repository"
)
// TODO: use clustered lock (unique queue? or *abuse* cache)
diff --git a/services/wiki/wiki_path.go b/services/wiki/wiki_path.go
index 74c7064043..ca312388af 100644
--- a/services/wiki/wiki_path.go
+++ b/services/wiki/wiki_path.go
@@ -8,11 +8,11 @@ import (
"path"
"strings"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/services/convert"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/modules/git"
+ api "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+ "forgejo.org/services/convert"
)
// To define the wiki related concepts:
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index efcc13db99..cb984425af 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -8,13 +8,13 @@ import (
"strings"
"testing"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/git"
+ "forgejo.org/modules/gitrepo"
- _ "code.gitea.io/gitea/models/actions"
+ _ "forgejo.org/models/actions"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
func TestWebPathSegments(t *testing.T) {
a := WebPathSegments("a%2Fa/b+c/d-e/f-g.-")
- assert.EqualValues(t, []string{"a/a", "b c", "d e", "f-g"}, a)
+ assert.Equal(t, []string{"a/a", "b c", "d e", "f-g"}, a)
}
func TestUserTitleToWebPath(t *testing.T) {
@@ -63,7 +63,7 @@ func TestWebPathToDisplayName(t *testing.T) {
{"a b", "a%20b.md"},
} {
_, displayName := WebPathToUserTitle(test.WebPath)
- assert.EqualValues(t, test.Expected, displayName)
+ assert.Equal(t, test.Expected, displayName)
}
}
@@ -80,7 +80,7 @@ func TestWebPathToGitPath(t *testing.T) {
{"2000-01-02-meeting.md", "2000-01-02+meeting"},
{"2000-01-02 meeting.-.md", "2000-01-02%20meeting.-"},
} {
- assert.EqualValues(t, test.Expected, WebPathToGitPath(test.WikiName))
+ assert.Equal(t, test.Expected, WebPathToGitPath(test.WikiName))
}
}
@@ -134,9 +134,9 @@ func TestUserWebGitPathConsistency(t *testing.T) {
_, userTitle1 := WebPathToUserTitle(webPath1)
gitPath1 := WebPathToGitPath(webPath1)
- assert.EqualValues(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle)
- assert.EqualValues(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle)
- assert.EqualValues(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle)
+ assert.Equal(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle)
+ assert.Equal(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle)
+ assert.Equal(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle)
}
}
@@ -175,7 +175,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
gitPath := WebPathToGitPath(webPath)
entry, err := masterTree.GetTreeEntryByPath(gitPath)
require.NoError(t, err)
- assert.EqualValues(t, gitPath, entry.Name(), "%s not added correctly", userTitle)
+ assert.Equal(t, gitPath, entry.Name(), "%s not added correctly", userTitle)
})
}
@@ -220,7 +220,7 @@ func TestRepository_EditWikiPage(t *testing.T) {
gitPath := WebPathToGitPath(webPath)
entry, err := masterTree.GetTreeEntryByPath(gitPath)
require.NoError(t, err)
- assert.EqualValues(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName)
+ assert.Equal(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName)
if newWikiName != "Home" {
_, err := masterTree.GetTreeEntryByPath("Home.md")
@@ -284,12 +284,12 @@ func TestPrepareWikiFileName(t *testing.T) {
}
if existence != tt.existence {
if existence {
- t.Errorf("expect to find no escaped file but we detect one")
+ t.Error("expect to find no escaped file but we detect one")
} else {
- t.Errorf("expect to find an escaped file but we could not detect one")
+ t.Error("expect to find an escaped file but we could not detect one")
}
}
- assert.EqualValues(t, tt.wikiPath, newWikiPath)
+ assert.Equal(t, tt.wikiPath, newWikiPath)
})
}
}
@@ -311,13 +311,13 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
existence, newWikiPath, err := prepareGitPath(gitRepo, "master", "Home")
assert.False(t, existence)
require.NoError(t, err)
- assert.EqualValues(t, "Home.md", newWikiPath)
+ assert.Equal(t, "Home.md", newWikiPath)
}
func TestWebPathConversion(t *testing.T) {
assert.Equal(t, "path/wiki", WebPathToURLPath(WebPath("path/wiki")))
assert.Equal(t, "wiki", WebPathToURLPath(WebPath("wiki")))
- assert.Equal(t, "", WebPathToURLPath(WebPath("")))
+ assert.Empty(t, WebPathToURLPath(WebPath("")))
}
func TestWebPathFromRequest(t *testing.T) {
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000000..cfd555fa37
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,28 @@
+{
+ pkgs ? import { },
+}:
+
+pkgs.mkShell {
+ name = "forgejo";
+ nativeBuildInputs = with pkgs; [
+ # generic
+ git
+ git-lfs
+ gnumake
+ gnused
+ gnutar
+ gzip
+
+ # frontend
+ nodejs
+
+ # backend
+ gofumpt
+ sqlite
+ go
+ gopls
+
+ # tests
+ openssh
+ ];
+}
diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl
index 8a8bd61148..1ca5573cae 100644
--- a/templates/admin/auth/edit.tmpl
+++ b/templates/admin/auth/edit.tmpl
@@ -326,19 +326,28 @@
- {{range .OAuth2Providers}}{{if .CustomURLSettings}}
-
-
-
-
-
-
- {{end}}{{end}}
+ {{range .OAuth2Providers}}
+ {{if .CustomURLSettings}}
+
+
+
+
+
+
+ {{end}}
+ {{if .CanProvideSSHKeys}}
+
+ {{end}}
+ {{end}}
+
+
+
+
@@ -371,51 +380,6 @@
{{end}}
-
- {{if .Source.IsSSPI}}
- {{$cfg:=.Source.Cfg}}
-
-
-
-
-
{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users_helper"}}
-
-
-
-
-
-
-
{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users_helper"}}
-
-
-
-
-
-
-
{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names_helper"}}
-
-
-
-
-
-
{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement_helper"}}
-
-
-
-
-
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
-
{{range .AllLangs}}{{if eq $cfg.DefaultLanguage .Lang}}{{.Name}}{{end}}{{end}}
-
-
-
{{ctx.Locale.Tr "admin.auths.sspi_default_language_helper"}}
-
- {{end}}
{{if .Source.IsLDAP}}
diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl
index c162b7b74d..0c7138bd68 100644
--- a/templates/admin/auth/list.tmpl
+++ b/templates/admin/auth/list.tmpl
@@ -30,6 +30,8 @@
{{DateUtils.AbsoluteShort .CreatedUnix}} |
{{svg "octicon-pencil"}} |
+ {{else}}
+
{{ctx.Locale.Tr "repo.pulls.no_results"}} |
{{end}}