.+?))?\\s"
- ]
- }
]
}
diff --git a/routers/api/actions/actions.go b/routers/api/actions/actions.go
index a418b3a1c4..70158c4e18 100644
--- a/routers/api/actions/actions.go
+++ b/routers/api/actions/actions.go
@@ -6,9 +6,9 @@ package actions
import (
"net/http"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/api/actions/ping"
- "code.gitea.io/gitea/routers/api/actions/runner"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/actions/ping"
+ "forgejo.org/routers/api/actions/runner"
)
func Routes(prefix string) *web.Route {
diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 405686a058..85a1f5f5be 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -69,18 +69,18 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/modules/json"
- "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"
- web_types "code.gitea.io/gitea/modules/web/types"
- actions_service "code.gitea.io/gitea/services/actions"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ web_types "forgejo.org/modules/web/types"
+ actions_service "forgejo.org/services/actions"
+ "forgejo.org/services/context"
)
const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
diff --git a/routers/api/actions/artifacts_chunks.go b/routers/api/actions/artifacts_chunks.go
index cdb56584b8..c0af750d7b 100644
--- a/routers/api/actions/artifacts_chunks.go
+++ b/routers/api/actions/artifacts_chunks.go
@@ -17,10 +17,10 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
+ "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/storage"
)
func saveUploadChunkBase(st storage.ObjectStorage, ctx *ArtifactContext,
@@ -51,11 +51,11 @@ func saveUploadChunkBase(st storage.ObjectStorage, ctx *ArtifactContext,
log.Info("[artifact] check chunk md5, sum: %s, header: %s", chunkMd5String, reqMd5String)
// if md5 not match, delete the chunk
if reqMd5String != chunkMd5String {
- checkErr = fmt.Errorf("md5 not match")
+ checkErr = errors.New("md5 not match")
}
}
if writtenSize != contentSize {
- checkErr = errors.Join(checkErr, fmt.Errorf("contentSize not match body size"))
+ checkErr = errors.Join(checkErr, errors.New("contentSize not match body size"))
}
if checkErr != nil {
if err := st.Delete(storagePath); err != nil {
@@ -261,7 +261,7 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
return fmt.Errorf("save merged file error: %v", err)
}
if written != artifact.FileCompressedSize {
- return fmt.Errorf("merged file size is not equal to chunk length")
+ return errors.New("merged file size is not equal to chunk length")
}
defer func() {
diff --git a/routers/api/actions/artifacts_utils.go b/routers/api/actions/artifacts_utils.go
index db602f1e14..18c8e3fbed 100644
--- a/routers/api/actions/artifacts_utils.go
+++ b/routers/api/actions/artifacts_utils.go
@@ -10,9 +10,9 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "forgejo.org/models/actions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
)
const (
diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go
index 0417f98242..dee5f1b2f3 100644
--- a/routers/api/actions/artifactsv4.go
+++ b/routers/api/actions/artifactsv4.go
@@ -98,16 +98,16 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- 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/storage"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/models/actions"
+ "forgejo.org/models/db"
+ quota_model "forgejo.org/models/quota"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/storage"
+ "forgejo.org/modules/util"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/common"
+ "forgejo.org/services/context"
"google.golang.org/protobuf/encoding/protojson"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -166,8 +166,8 @@ func (r artifactV4Routes) buildSignature(endp, expires, artifactName string, tas
mac.Write([]byte(endp))
mac.Write([]byte(expires))
mac.Write([]byte(artifactName))
- mac.Write([]byte(fmt.Sprint(taskID)))
- mac.Write([]byte(fmt.Sprint(artifactID)))
+ fmt.Fprint(mac, taskID)
+ fmt.Fprint(mac, artifactID)
return mac.Sum(nil)
}
diff --git a/routers/api/actions/ping/ping.go b/routers/api/actions/ping/ping.go
index 13985c93a3..6b3378b72e 100644
--- a/routers/api/actions/ping/ping.go
+++ b/routers/api/actions/ping/ping.go
@@ -8,7 +8,7 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/log"
+ "forgejo.org/modules/log"
pingv1 "code.gitea.io/actions-proto-go/ping/v1"
"code.gitea.io/actions-proto-go/ping/v1/pingv1connect"
diff --git a/routers/api/actions/runner/interceptor.go b/routers/api/actions/runner/interceptor.go
index b56e6870ac..be83af6997 100644
--- a/routers/api/actions/runner/interceptor.go
+++ b/routers/api/actions/runner/interceptor.go
@@ -9,11 +9,11 @@ import (
"errors"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
+ actions_model "forgejo.org/models/actions"
+ auth_model "forgejo.org/models/auth"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/modules/util"
"connectrpc.com/connect"
)
diff --git a/routers/api/actions/runner/main_test.go b/routers/api/actions/runner/main_test.go
index bed63c166e..112ebe3cb6 100644
--- a/routers/api/actions/runner/main_test.go
+++ b/routers/api/actions/runner/main_test.go
@@ -6,9 +6,9 @@ package runner
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
+ "forgejo.org/models/unittest"
- _ "code.gitea.io/gitea/models/forgefed"
+ _ "forgejo.org/models/forgefed"
)
func TestMain(m *testing.M) {
diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go
index 6cd37fd224..a971cd3fbf 100644
--- a/routers/api/actions/runner/runner.go
+++ b/routers/api/actions/runner/runner.go
@@ -9,13 +9,13 @@ import (
"fmt"
"net/http"
- 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/actions"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
- actions_service "code.gitea.io/gitea/services/actions"
+ actions_model "forgejo.org/models/actions"
+ repo_model "forgejo.org/models/repo"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/actions"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/util"
+ actions_service "forgejo.org/services/actions"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
@@ -178,7 +178,7 @@ func (s *Service) UpdateTask(
) (*connect.Response[runnerv1.UpdateTaskResponse], error) {
runner := GetRunner(ctx)
- task, err := actions_model.UpdateTaskByState(ctx, runner.ID, req.Msg.State)
+ task, err := actions_service.UpdateTaskByState(ctx, runner.ID, req.Msg.State)
if err != nil {
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("update task: %w", err))
}
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
index 37a2b64563..a118459ce3 100644
--- a/routers/api/packages/alt/alt.go
+++ b/routers/api/packages/alt/alt.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"
- alt_service "code.gitea.io/gitea/services/packages/alt"
+ "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) {
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index ffc62254d0..721f96b768 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -8,37 +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/alt"
- "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) {
@@ -48,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())
@@ -116,7 +117,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) {
var err error
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if err != nil {
- log.Error("Failed to verify user: %v", err)
+ log.Info("Failed to verify user: %v", err)
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
return
}
@@ -630,7 +631,7 @@ func CommonRoutes() *web.Route {
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`)
+ 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) {
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 a3ea2c2f9a..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
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 24e52d0972..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
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 9c9da38424..873ce5c23a 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)
}
}
@@ -389,12 +399,7 @@ func EndUploadBlob(ctx *context.Context) {
}
return
}
- doClose := true
- defer func() {
- if doClose {
- uploader.Close()
- }
- }()
+ defer uploader.Close()
if ctx.Req.Body != nil {
if err := uploader.Append(ctx, ctx.Req.Body); err != nil {
@@ -427,12 +432,6 @@ func EndUploadBlob(ctx *context.Context) {
return
}
- if err := uploader.Close(); err != nil {
- apiError(ctx, http.StatusInternalServerError, err)
- return
- }
- doClose = false
-
if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -691,33 +690,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 +721,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..47d1f18623 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.
@@ -25,7 +25,8 @@ func LogAndProcessError(ctx *context.Context, status int, obj any, cb func(strin
message = fmt.Sprintf("%s", obj)
}
if status == http.StatusInternalServerError {
- log.ErrorWithSkip(1, message)
+ // LogAndProcessError is always wrapped in a `apiError` call, so we need to skip two frames
+ log.ErrorWithSkip(2, message)
if setting.IsProd && (ctx.Doer == nil || !ctx.Doer.IsAdmin) {
message = ""
@@ -39,16 +40,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 +53,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 073b2700ef..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 {
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 19d6dc6903..360632570e 100644
--- a/routers/api/packages/pypi/pypi.go
+++ b/routers/api/packages/pypi/pypi.go
@@ -12,14 +12,14 @@ import (
"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
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index d56678514c..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) {
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index 28f584b391..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 (
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 5e863cb9eb..59d9f28d60 100644
--- a/routers/api/shared/middleware.go
+++ b/routers/api/shared/middleware.go
@@ -4,13 +4,15 @@
package shared
import (
+ "fmt"
"net/http"
- "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"
+ auth_model "forgejo.org/models/auth"
+ "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"
)
@@ -92,6 +94,25 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC
})
return
}
+
+ if ctx.Doer.MustHaveTwoFactor() {
+ hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
+ log.Error("Error getting 2fa: %s", err)
+ ctx.JSON(http.StatusInternalServerError, map[string]string{
+ "message": fmt.Sprintf("Error getting 2fa: %s", err),
+ })
+ return
+ }
+ if !hasTwoFactor {
+ ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
+ ctx.JSON(http.StatusForbidden, map[string]string{
+ "message": ctx.Locale.TrString("error.must_enable_2fa", fmt.Sprintf("%suser/settings/security", setting.AppURL)),
+ })
+ return
+ }
+ }
}
// Redirect to dashboard if user tries to visit any non-login page.
@@ -130,7 +151,7 @@ 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.")
+ ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.")
}
}
diff --git a/routers/api/v1/activitypub/actor.go b/routers/api/v1/activitypub/actor.go
index 4f128e74c4..0ff822c7f4 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,11 +28,11 @@ 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()
- err := actor.PreferredUsername.Set("en", ap.Content(setting.Domain))
+ err := actor.PreferredUsername.Set("en", ap.Content("ghost"))
if err != nil {
ctx.ServerError("PreferredUsername.Set", err)
return
@@ -41,12 +41,10 @@ func Actor(ctx *context.APIContext) {
actor.URL = ap.IRI(setting.AppURL)
actor.Inbox = ap.IRI(link + "/inbox")
- actor.Outbox = ap.IRI(link + "/outbox")
-
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..72f9680b9b 100644
--- a/routers/api/v1/activitypub/person.go
+++ b/routers/api/v1/activitypub/person.go
@@ -4,14 +4,17 @@
package activitypub
import (
- "fmt"
"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/models/activities"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/web"
+ "forgejo.org/routers/api/v1/utils"
+ "forgejo.org/services/context"
+ "forgejo.org/services/convert"
+ "forgejo.org/services/federation"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
@@ -34,45 +37,12 @@ func Person(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/ActivityPub"
- // TODO: the setting.AppURL during the test doesn't follow the definition: "It always has a '/' suffix"
- link := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.ContextUser.ID)
- person := ap.PersonNew(ap.IRI(link))
-
- person.Name = ap.NaturalLanguageValuesNew()
- err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName))
+ person, err := convert.ToActivityPubPerson(ctx, ctx.ContextUser)
if err != nil {
- ctx.ServerError("Set Name", err)
+ ctx.ServerError("convert.ToActivityPubPerson", err)
return
}
- person.PreferredUsername = ap.NaturalLanguageValuesNew()
- err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name))
- if err != nil {
- ctx.ServerError("Set PreferredUsername", err)
- return
- }
-
- person.URL = ap.IRI(ctx.ContextUser.HTMLURL())
-
- person.Icon = ap.Image{
- Type: ap.ImageType,
- MediaType: "image/png",
- URL: ap.IRI(ctx.ContextUser.AvatarLink(ctx)),
- }
-
- person.Inbox = ap.IRI(link + "/inbox")
- person.Outbox = ap.IRI(link + "/outbox")
-
- person.PublicKey.ID = ap.IRI(link + "#main-key")
- person.PublicKey.Owner = ap.IRI(link)
-
- publicKeyPem, err := activitypub.GetPublicKey(ctx, ctx.ContextUser)
- if err != nil {
- ctx.ServerError("GetPublicKey", err)
- return
- }
- person.PublicKey.PublicKeyPem = publicKeyPem
-
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(person)
if err != nil {
ctx.ServerError("MarshalJSON", err)
@@ -99,8 +69,174 @@ func PersonInbox(ctx *context.APIContext) {
// type: integer
// required: true
// responses:
- // "204":
+ // "202":
// "$ref": "#/responses/empty"
- ctx.Status(http.StatusNoContent)
+ form := web.GetForm(ctx)
+ activity := form.(*ap.Activity)
+ result, err := federation.ProcessPersonInbox(ctx, ctx.ContextUser, activity)
+ if err != nil {
+ ctx.Error(federation.HTTPStatus(err), "PersonInbox", err)
+ return
+ }
+ responseServiceResult(ctx, result)
+}
+
+// PersonFeed returns the recorded activities in the user's feed
+func PersonFeed(ctx *context.APIContext) {
+ // swagger:operation GET /activitypub/user-id/{user-id}/outbox activitypub activitypubPersonFeed
+ // ---
+ // summary: List the user's recorded activity
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: user-id
+ // in: path
+ // description: user ID of the user
+ // type: integer
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/PersonFeed"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ listOptions := utils.GetListOptions(ctx)
+ opts := activities.GetFollowingFeedsOptions{
+ ListOptions: listOptions,
+ }
+ items, count, err := activities.GetFollowingFeeds(ctx, ctx.ContextUser.ID, opts)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetFollowingFeeds", err)
+ return
+ }
+ ctx.SetTotalCountHeader(count)
+
+ feed := ap.OrderedCollectionNew(ap.IRI(ctx.ContextUser.APActorID() + "/outbox"))
+ feed.AttributedTo = ap.IRI(ctx.ContextUser.APActorID())
+ for _, item := range items {
+ if err := feed.OrderedItems.Append(convert.ToActivityPubPersonFeedItem(item)); err != nil {
+ ctx.Error(http.StatusInternalServerError, "OrderedItems.Append", err)
+ return
+ }
+ }
+
+ binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(feed)
+ if err != nil {
+ ctx.ServerError("MarshalJSON", err)
+ return
+ }
+
+ ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
+ ctx.Resp.WriteHeader(http.StatusOK)
+ if _, err = ctx.Resp.Write(binary); err != nil {
+ log.Error("write to resp err: %v", err)
+ }
+}
+
+func getActivity(ctx *context.APIContext, id int64) (*forgefed.ForgeUserActivity, error) {
+ action, err := activities.GetActivityByID(ctx, id)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetActivityByID", err.Error())
+ return nil, err
+ }
+
+ if action.UserID != action.ActUserID || action.ActUserID != ctx.ContextUser.ID {
+ ctx.NotFound()
+ return nil, err
+ }
+
+ actions := activities.ActionList{action}
+ if err := actions.LoadAttributes(ctx); err != nil {
+ ctx.Error(http.StatusInternalServerError, "action.LoadAttributes", err.Error())
+ return nil, err
+ }
+
+ activity, err := convert.ActionToForgeUserActivity(ctx, actions[0])
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "ActionToForgeUserActivity", err.Error())
+ return nil, err
+ }
+
+ return &activity, nil
+}
+
+// PersonActivity returns a user's given activity
+func PersonActivity(ctx *context.APIContext) {
+ // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id}/activity activitypub activitypubPersonActivity
+ // ---
+ // summary: Get a specific activity of the user
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: user-id
+ // in: path
+ // description: user ID of the user
+ // type: integer
+ // required: true
+ // - name: activity-id
+ // in: path
+ // description: activity ID of the sought activity
+ // type: integer
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActivityPub"
+
+ id := ctx.ParamsInt64("activity-id")
+ activity, err := getActivity(ctx, id)
+ if err != nil {
+ return
+ }
+
+ binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity)
+ if err != nil {
+ ctx.ServerError("MarshalJSON", err)
+ return
+ }
+ ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
+ ctx.Resp.WriteHeader(http.StatusOK)
+ if _, err = ctx.Resp.Write(binary); err != nil {
+ log.Error("write to resp err: %v", err)
+ }
+}
+
+// PersonActivity returns the Object part of a user's given activity
+func PersonActivityNote(ctx *context.APIContext) {
+ // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id} activitypub activitypubPersonActivityNote
+ // ---
+ // summary: Get a specific activity object of the user
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: user-id
+ // in: path
+ // description: user ID of the user
+ // type: integer
+ // required: true
+ // - name: activity-id
+ // in: path
+ // description: activity ID of the sought activity
+ // type: integer
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActivityPub"
+
+ id := ctx.ParamsInt64("activity-id")
+ activity, err := getActivity(ctx, id)
+ if err != nil {
+ return
+ }
+
+ binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity.Object)
+ if err != nil {
+ ctx.ServerError("MarshalJSON", err)
+ return
+ }
+ ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
+ ctx.Resp.WriteHeader(http.StatusOK)
+ if _, err = ctx.Resp.Write(binary); err != nil {
+ log.Error("write to resp err: %v", err)
+ }
}
diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go
index 14381664d4..3eaa6b82c5 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"
)
@@ -71,10 +71,11 @@ 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)
+ activity := form.(*ap.Activity)
+ result, err := federation.ProcessRepositoryInbox(ctx, activity, repository.ID)
if err != nil {
- ctx.Error(httpStatus, title, err)
+ ctx.Error(federation.HTTPStatus(err), "Processing Repository Inbox failed", result)
+ return
}
- ctx.Status(http.StatusNoContent)
+ responseServiceResult(ctx, result)
}
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 19d167b50b..38cb067b89 100644
--- a/routers/api/v1/activitypub/reqsignature.go
+++ b/routers/api/v1/activitypub/reqsignature.go
@@ -4,61 +4,21 @@
package activitypub
import (
- "crypto"
- "crypto/x509"
- "encoding/pem"
- "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/modules/log"
+ "forgejo.org/modules/setting"
+ services_context "forgejo.org/services/context"
+ "forgejo.org/services/federation"
"github.com/42wim/httpsig"
- ap "github.com/go-ap/activitypub"
)
-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)
+func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authenticated bool, err error) {
+ if !setting.Federation.SignatureEnforced {
+ return true, nil
}
- 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
- 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")
- }
- p, err = x509.ParsePKIXPublicKey(block.Bytes)
- return p, err
-}
-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()
- 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)
- }
- b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
- return b, err
-}
-
-func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
r := ctx.Req
// 1. Figure out what key we need to verify
@@ -66,30 +26,65 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
if err != nil {
return false, err
}
- ID := v.KeyId()
- idIRI, err := url.Parse(ID)
+
+ signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0])
+ pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId())
+ if err != nil || pubKey == nil {
+ pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId())
+ if err != nil {
+ return false, err
+ }
+ }
+
+ err = v.Verify(pubKey, signatureAlgorithm)
if err != nil {
return false, err
}
- // 2. Fetch the public key of the other actor
- b, err := fetch(idIRI)
+ return true, nil
+}
+
+func verifyHTTPUserSignature(ctx services_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
+ v, err := httpsig.NewVerifier(r)
if err != nil {
return false, err
}
- pubKey, err := getPublicKeyFromResponse(b, idIRI)
+
+ signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0])
+ pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId())
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
- return authenticated, err
+
+ err = v.Verify(pubKey, signatureAlgorithm)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
}
// ReqHTTPSignature function
-func ReqHTTPSignature() func(ctx *gitea_context.APIContext) {
- return func(ctx *gitea_context.APIContext) {
- if authenticated, err := verifyHTTPSignatures(ctx); err != nil {
+func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) {
+ return func(ctx *services_context.APIContext) {
+ if authenticated, err := verifyHTTPUserOrInstanceSignature(*ctx); err != nil {
+ log.Warn("verifyHttpSignatures failed: %v", err)
+ ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed")
+ } else if !authenticated {
+ ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed")
+ }
+ }
+}
+
+// ReqHTTPUserSignature function
+func ReqHTTPUserSignature() func(ctx *services_context.APIContext) {
+ return func(ctx *services_context.APIContext) {
+ if authenticated, err := verifyHTTPUserSignature(*ctx); err != nil {
log.Warn("verifyHttpSignatures failed: %v", err)
ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed")
} else if !authenticated {
diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go
index 42ef375f12..64413cebb1 100644
--- a/routers/api/v1/activitypub/response.go
+++ b/routers/api/v1/activitypub/response.go
@@ -6,16 +6,50 @@ 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"
+ "forgejo.org/services/federation"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
)
// Respond with an ActivityStreams object
+func responseServiceResult(ctx *context.APIContext, result federation.ServiceResult) {
+ ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
+
+ switch {
+ case result.StatusOnly():
+ ctx.Resp.WriteHeader(result.HTTPStatus)
+ return
+ case result.WithBytes():
+ ctx.Resp.WriteHeader(result.HTTPStatus)
+ if _, err := ctx.Resp.Write(result.Bytes); err != nil {
+ log.Error("Error writing a response: %v", err)
+ ctx.Error(http.StatusInternalServerError, "Error writing a response", err)
+ return
+ }
+ case result.WithActivity():
+ binary, err := jsonld.WithContext(
+ jsonld.IRI(ap.ActivityBaseURI),
+ jsonld.IRI(ap.SecurityContextURI),
+ jsonld.IRI(forgefed.ForgeFedNamespaceURI),
+ ).Marshal(result.Activity)
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
+ ctx.Resp.WriteHeader(result.HTTPStatus)
+ if _, err = ctx.Resp.Write(binary); err != nil {
+ log.Error("write to resp err: %v", err)
+ }
+ }
+}
+
+// Respond with an ActivityStreams object
+// Deprecated
func response(ctx *context.APIContext, v any) {
binary, err := jsonld.WithContext(
jsonld.IRI(ap.ActivityBaseURI),
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..9f6ef0fedf 100644
--- a/routers/api/v1/admin/email.go
+++ b/routers/api/v1/admin/email.go
@@ -6,18 +6,18 @@ 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
func GetAllEmails(ctx *context.APIContext) {
// swagger:operation GET /admin/emails admin adminGetAllEmails
// ---
- // summary: List all emails
+ // summary: List all users' email addresses
// produces:
// - application/json
// parameters:
@@ -60,7 +60,7 @@ func GetAllEmails(ctx *context.APIContext) {
func SearchEmail(ctx *context.APIContext) {
// swagger:operation GET /admin/emails/search admin adminSearchEmails
// ---
- // summary: Search all emails
+ // summary: Search users' email addresses
// produces:
// - application/json
// parameters:
diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go
index b246cb61b1..36ca6831e6 100644
--- a/routers/api/v1/admin/hooks.go
+++ b/routers/api/v1/admin/hooks.go
@@ -7,21 +7,21 @@ 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
+// ListHooks list system webhooks
func ListHooks(ctx *context.APIContext) {
// swagger:operation GET /admin/hooks admin adminListHooks
// ---
- // summary: List system's webhooks
+ // summary: List global (system) webhooks
// produces:
// - application/json
// parameters:
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 d0cfef8e48..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
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index db8f6627e6..f3e321a047 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) {
@@ -51,11 +51,11 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
u.LoginSource = source.ID
}
-// CreateUser create a user
+// CreateUser create a user account
func CreateUser(ctx *context.APIContext) {
// swagger:operation POST /admin/users admin adminCreateUser
// ---
- // summary: Create a user
+ // summary: Create a user account
// consumes:
// - application/json
// produces:
@@ -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)
@@ -275,7 +274,7 @@ func EditUser(ctx *context.APIContext) {
func DeleteUser(ctx *context.APIContext) {
// swagger:operation DELETE /admin/users/{username} admin adminDeleteUser
// ---
- // summary: Delete a user
+ // summary: Delete user account
// produces:
// - application/json
// parameters:
@@ -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
}
@@ -325,11 +324,11 @@ func DeleteUser(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// CreatePublicKey api for creating a public key to a user
+// CreatePublicKey adds an SSH public key to user's account
func CreatePublicKey(ctx *context.APIContext) {
// swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey
// ---
- // summary: Add a public key on behalf of a user
+ // summary: Add an SSH public key to user's account
// consumes:
// - application/json
// produces:
@@ -357,11 +356,11 @@ func CreatePublicKey(ctx *context.APIContext) {
user.CreateUserPublicKey(ctx, *form, ctx.ContextUser.ID)
}
-// DeleteUserPublicKey api for deleting a user's public key
+// DeleteUserPublicKey removes an SSH public key from user's account
func DeleteUserPublicKey(ctx *context.APIContext) {
// swagger:operation DELETE /admin/users/{username}/keys/{id} admin adminDeleteUserPublicKey
// ---
- // summary: Delete a user's public key
+ // summary: Remove a public key from user's account
// produces:
// - application/json
// parameters:
@@ -437,26 +436,6 @@ func SearchUsers(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- sort := ctx.FormString("sort")
- var orderBy db.SearchOrderBy
-
- switch sort {
- case "oldest":
- orderBy = db.SearchOrderByOldest
- case "newest":
- orderBy = db.SearchOrderByNewest
- case "alphabetically":
- orderBy = db.SearchOrderByAlphabetically
- case "reversealphabetically":
- orderBy = db.SearchOrderByAlphabeticallyReverse
- case "recentupdate":
- orderBy = db.SearchOrderByRecentUpdated
- case "leastupdate":
- orderBy = db.SearchOrderByLeastUpdated
- default:
- orderBy = db.SearchOrderByAlphabetically
- }
-
intSource, err := strconv.ParseInt(ctx.FormString("source_id"), 10, 64)
var sourceID optional.Option[int64]
if ctx.FormString("source_id") == "" || err != nil {
@@ -470,7 +449,7 @@ func SearchUsers(ctx *context.APIContext) {
Type: user_model.UserTypeIndividual,
LoginName: ctx.FormTrim("login_name"),
SourceID: sourceID,
- OrderBy: orderBy,
+ OrderBy: utils.GetDbSearchOrder(ctx),
ListOptions: listOptions,
})
if err != nil {
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 3791a6fbba..26a2c0ffe3 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -36,12 +36,12 @@
// type: apiKey
// name: token
// in: query
-// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
+// description: This authentication option is deprecated for removal in Forgejo v13.0.0. 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.
+// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.
// AuthorizationHeaderToken:
// type: apiKey
// name: Authorization
@@ -71,39 +71,39 @@ 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/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"
+ ap "github.com/go-ap/activitypub"
)
func sudo() func(ctx *context.APIContext) {
@@ -203,19 +203,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 +692,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 +716,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 +777,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
@@ -838,24 +838,27 @@ func Routes() *web.Route {
if setting.Federation.Enabled {
m.Get("/nodeinfo", misc.NodeInfo)
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.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
- }, context.UserAssignmentAPI(), checkTokenPublicOnly())
m.Group("/user-id/{user-id}", func() {
- m.Get("", activitypub.Person)
- m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
+ m.Get("", activitypub.ReqHTTPUserOrInstanceSignature(), activitypub.Person)
+ m.Post("/inbox",
+ activitypub.ReqHTTPUserSignature(),
+ bind(ap.Activity{}),
+ activitypub.PersonInbox)
+ m.Group("/activities/{activity-id}", func() {
+ m.Get("", activitypub.PersonActivityNote)
+ m.Get("/activity", activitypub.PersonActivity)
+ })
+ m.Get("/outbox", activitypub.ReqHTTPUserSignature(), activitypub.PersonFeed)
}, context.UserIDAssignmentAPI(), checkTokenPublicOnly())
m.Group("/actor", func() {
m.Get("", activitypub.Actor)
- m.Post("/inbox", activitypub.ActorInbox)
+ m.Post("/inbox", activitypub.ReqHTTPUserOrInstanceSignature(), activitypub.ActorInbox)
})
m.Group("/repository-id/{repository-id}", func() {
- m.Get("", activitypub.Repository)
+ m.Get("", activitypub.ReqHTTPUserSignature(), activitypub.Repository)
m.Post("/inbox",
- bind(forgefed.ForgeLike{}),
- // TODO: activitypub.ReqHTTPSignature(),
+ bind(ap.Activity{}),
+ activitypub.ReqHTTPUserSignature(),
activitypub.RepositoryInbox)
}, context.RepositoryIDAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub))
@@ -865,6 +868,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)
@@ -1183,9 +1187,13 @@ 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() {
+ m.Group("/{workflowfilename}", func() {
m.Post("/dispatches", reqToken(), reqRepoWriter(unit.TypeActions), mustNotBeArchived, bind(api.DispatchWorkflowOption{}), repo.DispatchWorkflow)
})
})
@@ -1317,6 +1325,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() {
@@ -1355,6 +1364,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())
@@ -1488,16 +1503,16 @@ func Routes() *web.Route {
m.Group("/{type}/{name}", func() {
m.Group("/{version}", func() {
m.Get("", packages.GetPackage)
- m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
+ m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
m.Get("/files", packages.ListPackageFiles)
})
- m.Post("/-/link/{repo_name}", reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage)
- m.Post("/-/unlink", reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage)
+ m.Post("/-/link/{repo_name}", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage)
+ m.Post("/-/unlink", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage)
})
m.Get("/", packages.ListPackages)
- }, reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
+ }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
// Organizations
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
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 32568b805f..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"
)
diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go
index 70187e0ebf..9631de7edd 100644
--- a/routers/api/v1/misc/nodeinfo.go
+++ b/routers/api/v1/misc/nodeinfo.go
@@ -7,11 +7,11 @@ 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"
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 3417475d21..5802c12462 100644
--- a/routers/api/v1/misc/version.go
+++ b/routers/api/v1/misc/version.go
@@ -6,9 +6,9 @@ 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
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 99e70e0740..8b330aa752 100644
--- a/routers/api/v1/org/action.go
+++ b/routers/api/v1/org/action.go
@@ -7,24 +7,24 @@ 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
+// ListActionsSecrets lists actions secrets of an organization
func (Action) ListActionsSecrets(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets
// ---
- // summary: List an organization's actions secrets
+ // summary: List actions secrets of an organization
// produces:
// - application/json
// parameters:
@@ -218,7 +218,7 @@ func (Action) SearchActionRunJobs(ctx *context.APIContext) {
func (Action) ListVariables(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
// ---
- // summary: Get an org-level variables list
+ // summary: List variables of an organization
// produces:
// - application/json
// parameters:
@@ -266,11 +266,11 @@ func (Action) ListVariables(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, variables)
}
-// GetVariable get an org-level variable
+// GetVariable gives organization's variable
func (Action) GetVariable(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable
// ---
- // summary: Get an org-level variable
+ // summary: Get organization's variable by name
// produces:
// - application/json
// parameters:
@@ -315,11 +315,11 @@ func (Action) GetVariable(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, variable)
}
-// DeleteVariable delete an org-level variable
+// DeleteVariable deletes an organization's variable
func (Action) DeleteVariable(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable
// ---
- // summary: Delete an org-level variable
+ // summary: Delete organization's variable by name
// produces:
// - application/json
// parameters:
@@ -359,11 +359,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// CreateVariable create an org-level variable
+// CreateVariable creates a new variable in organization
func (Action) CreateVariable(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable
// ---
- // summary: Create an org-level variable
+ // summary: Create a new variable in organization
// consumes:
// - application/json
// produces:
@@ -423,11 +423,11 @@ func (Action) CreateVariable(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// UpdateVariable update an org-level variable
+// UpdateVariable updates variable in organization
func (Action) UpdateVariable(ctx *context.APIContext) {
// swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable
// ---
- // summary: Update an org-level variable
+ // summary: Update variable in organization
// consumes:
// - application/json
// produces:
diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go
index f11eb6c1cd..043da3186f 100644
--- a/routers/api/v1/org/avatar.go
+++ b/routers/api/v1/org/avatar.go
@@ -7,17 +7,17 @@ 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
+// UpdateAvatar updates an organization's avatar
func UpdateAvatar(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar
// ---
- // summary: Update Avatar
+ // summary: Update an organization's avatar
// produces:
// - application/json
// parameters:
@@ -52,11 +52,11 @@ func UpdateAvatar(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// DeleteAvatar deletes the Avatar of an Organisation
+// DeleteAvatar deletes an organization's avatar
func DeleteAvatar(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar
// ---
- // summary: Delete Avatar
+ // summary: Delete an organization's avatar. It will be replaced by a default one
// produces:
// - application/json
// parameters:
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 7d503c3ad7..a4ea924979 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) {
@@ -180,7 +180,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
func GetAll(ctx *context.APIContext) {
// swagger:operation Get /orgs organization orgGetAll
// ---
- // summary: Get list of organizations
+ // summary: List all organizations
// produces:
// - application/json
// parameters:
@@ -398,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 a98b5dbc69..03057c4feb 100644
--- a/routers/api/v1/packages/package.go
+++ b/routers/api/v1/packages/package.go
@@ -7,15 +7,15 @@ import (
"errors"
"net/http"
- "code.gitea.io/gitea/models/packages"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/optional"
- 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"
- "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
diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go
index 2ce9ad253c..fe29d534ff 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
@@ -614,7 +615,7 @@ func ListActionTasks(ctx *context.APIContext) {
// DispatchWorkflow dispatches a workflow
func DispatchWorkflow(ctx *context.APIContext) {
- // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowname}/dispatches repository DispatchWorkflow
+ // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowfilename}/dispatches repository DispatchWorkflow
// ---
// summary: Dispatches a workflow
// consumes:
@@ -630,7 +631,7 @@ func DispatchWorkflow(ctx *context.APIContext) {
// description: name of the repo
// type: string
// required: true
- // - name: workflowname
+ // - name: workflowfilename
// in: path
// description: name of the workflow
// type: string
@@ -648,13 +649,13 @@ func DispatchWorkflow(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
opt := web.GetForm(ctx).(*api.DispatchWorkflowOption)
- name := ctx.Params("workflowname")
+ name := ctx.Params("workflowfilename")
if len(opt.Ref) == 0 {
ctx.Error(http.StatusBadRequest, "ref", "ref is empty")
return
} else if len(name) == 0 {
- ctx.Error(http.StatusBadRequest, "workflowname", "workflow name is empty")
+ ctx.Error(http.StatusBadRequest, "workflowfilename", "workflow file name is empty")
return
}
@@ -694,3 +695,161 @@ func DispatchWorkflow(ctx *context.APIContext) {
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..12308ce2df 100644
--- a/routers/api/v1/repo/avatar.go
+++ b/routers/api/v1/repo/avatar.go
@@ -7,17 +7,17 @@ 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
+// UpdateVatar updates repo avatar
func UpdateAvatar(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar
// ---
- // summary: Update avatar
+ // summary: Update a repository's avatar
// produces:
// - application/json
// parameters:
@@ -56,11 +56,11 @@ func UpdateAvatar(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// UpdateAvatar deletes the Avatar of an Repo
+// DeleteAvatar deletes repo avatar
func DeleteAvatar(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar
// ---
- // summary: Delete avatar
+ // summary: Delete a repository's avatar
// produces:
// - application/json
// parameters:
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..e043448590 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
@@ -595,7 +594,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
isPlainRule := !git_model.IsRuleNameSpecial(ruleName)
var isBranchExist bool
if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName)
+ isBranchExist = ctx.Repo.GitRepo.IsBranchExist(ruleName)
}
protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName)
@@ -983,7 +982,7 @@ func EditBranchProtection(ctx *context.APIContext) {
isPlainRule := !git_model.IsRuleNameSpecial(bpName)
var isBranchExist bool
if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName)
+ isBranchExist = ctx.Repo.GitRepo.IsBranchExist(bpName)
}
if isBranchExist {
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index 14915d31ba..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
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 7781e996f5..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],
})
@@ -80,7 +80,7 @@ func CompareDiff(ctx *context.APIContext) {
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,
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 55b245676d..6c1671d21c 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -14,23 +14,23 @@ import (
"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 (
@@ -241,7 +241,7 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return nil, nil, nil
}
- if entry.IsDir() || entry.IsSubModule() {
+ if entry.IsDir() || entry.IsSubmodule() {
ctx.NotFound("getBlobForEntry", nil)
return nil, nil, nil
}
@@ -437,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) {
@@ -480,6 +480,8 @@ func ChangeFiles(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -584,6 +586,8 @@ func CreateFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -684,6 +688,8 @@ func UpdateFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
// "413":
// "$ref": "#/responses/quotaExceeded"
// "422":
@@ -692,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
}
@@ -757,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 57fc4b2f17..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 {
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 2fe7aaacb4..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
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..f2e79ea417 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)
@@ -384,7 +384,7 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(http.StatusForbidden)
- return nil, nil, nil
+ return nil, nil, errors.New("not issue/pull writer")
}
err = issue_service.SetIssueUpdateDate(ctx, issue, form.Updated, ctx.Doer)
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 723a696b92..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
}
@@ -224,9 +224,8 @@ func Migrate(ctx *context.APIContext) {
HasWiki: &opts.Wiki,
}
- // only enabling wiki could return an error
- if err = updateRepoUnits(ctx, repoOpt); err != nil {
- log.Error("Failed to enable wiki on %s/%s repo. %w", repoOwner.Name, form.RepoName, err)
+ 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)
}
}
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..08ef68cbfc 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
@@ -251,11 +251,11 @@ func GetPushMirrorByName(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, m)
}
-// AddPushMirror adds a push mirror to a repository
+// AddPushMirror sets up a new push mirror in a repository
func AddPushMirror(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror
// ---
- // summary: add a push mirror to the repository
+ // summary: Set up a new push mirror in a repository
// consumes:
// - application/json
// produces:
@@ -296,11 +296,11 @@ func AddPushMirror(ctx *context.APIContext) {
CreatePushMirror(ctx, pushMirror)
}
-// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName
+// DeletePushMirrorByRemoteName removes a push mirror from a repository by remoteName
func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror
// ---
- // summary: deletes a push mirror from a repository by remoteName
+ // summary: Remove a push mirror from a repository by remoteName
// produces:
// - application/json
// parameters:
@@ -389,6 +389,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
Interval: interval,
SyncOnCommit: mirrorOption.SyncOnCommit,
RemoteAddress: remoteAddress,
+ BranchFilter: mirrorOption.BranchFilter,
}
var plainPrivateKey []byte
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..812468f6b4 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
@@ -1016,6 +1016,9 @@ func MergePullRequest(ctx *context.APIContext) {
} else if models.IsErrMergeUnrelatedHistories(err) {
conflictError := err.(models.ErrMergeUnrelatedHistories)
ctx.JSON(http.StatusConflict, conflictError)
+ } else if models.IsErrPullRequestHasMerged(err) {
+ conflictError := err.(models.ErrPullRequestHasMerged)
+ ctx.JSON(http.StatusConflict, conflictError)
} else if git.IsErrPushOutOfDate(err) {
ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
} else if models.IsErrSHADoesNotMatch(err) {
@@ -1050,11 +1053,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 +1087,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 +1096,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 +1106,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 +1118,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 +1130,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 +1188,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 +1617,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..06b47fef70 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
@@ -715,7 +716,7 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues
func CreateReviewRequests(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests
// ---
- // summary: create review requests for a pull request
+ // summary: Create review requests for a pull request
// produces:
// - application/json
// parameters:
@@ -756,7 +757,7 @@ func CreateReviewRequests(ctx *context.APIContext) {
func DeleteReviewRequests(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests
// ---
- // summary: cancel review requests for a pull request
+ // summary: Cancel review requests for a pull request
// produces:
// - application/json
// parameters:
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..42385d54a6 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
}
@@ -864,14 +861,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if *opts.GloballyEditableWiki {
wikiPermissions = repo_model.UnitAccessModeWrite
} else {
- wikiPermissions = repo_model.UnitAccessModeRead
+ wikiPermissions = repo_model.UnitAccessModeUnset
}
}
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..8829e37bc3 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
@@ -253,17 +253,17 @@ func DeleteTopic(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// TopicSearch search for creating topic
+// TopicSearch searches known topics, i.e. when adding a topic to a repository
func TopicSearch(ctx *context.APIContext) {
// swagger:operation GET /topics/search repository topicSearch
// ---
- // summary: search topics via keyword
+ // summary: Search for topics by keyword
// produces:
// - application/json
// parameters:
// - name: q
// in: query
- // description: keywords to search
+ // description: keyword to search for
// required: true
// type: string
// - name: page
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 9c27078326..a7811a95b5 100644
--- a/routers/api/v1/shared/runners.go
+++ b/routers/api/v1/shared/runners.go
@@ -8,11 +8,11 @@ import (
"net/http"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/structs"
- "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
diff --git a/routers/api/v1/swagger/action.go b/routers/api/v1/swagger/action.go
index 2174b1ff17..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
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..a11fc4098c 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
@@ -13,3 +13,10 @@ type swaggerResponseActivityPub struct {
// in:body
Body api.ActivityPub `json:"body"`
}
+
+// Personfeed
+// swagger:response PersonFeed
+type swaggerResponsePersonFeed struct {
+ // in:body
+ Body []api.APPersonFollowItem `json:"body"`
+}
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 48c11c467f..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
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..65955913fd 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
@@ -71,11 +71,11 @@ func ListAccessTokens(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &apiTokens)
}
-// CreateAccessToken create access tokens
+// CreateAccessToken creates an access token for doer
func CreateAccessToken(ctx *context.APIContext) {
// swagger:operation POST /users/{username}/tokens user userCreateToken
// ---
- // summary: Create an access token
+ // summary: Generate an access token for the current user
// consumes:
// - application/json
// produces:
@@ -141,11 +141,11 @@ func CreateAccessToken(ctx *context.APIContext) {
})
}
-// DeleteAccessToken delete access tokens
+// DeleteAccessToken deletes an access token from doer's account
func DeleteAccessToken(ctx *context.APIContext) {
// swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken
// ---
- // summary: delete an access token
+ // summary: Delete an access token from current user's account
// produces:
// - application/json
// parameters:
@@ -214,7 +214,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
func CreateOauth2Application(ctx *context.APIContext) {
// swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application
// ---
- // summary: creates a new OAuth2 application
+ // summary: Creates a new OAuth2 application
// produces:
// - application/json
// parameters:
@@ -298,11 +298,11 @@ func ListOauth2Applications(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &apiApps)
}
-// DeleteOauth2Application delete OAuth2 Application
+// DeleteOauth2Application delete OAuth2 application
func DeleteOauth2Application(ctx *context.APIContext) {
// swagger:operation DELETE /user/applications/oauth2/{id} user userDeleteOAuth2Application
// ---
- // summary: delete an OAuth2 Application
+ // summary: Delete an OAuth2 application
// produces:
// - application/json
// parameters:
@@ -334,11 +334,11 @@ func DeleteOauth2Application(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// GetOauth2Application get OAuth2 Application
+// GetOauth2Application returns an OAuth2 application
func GetOauth2Application(ctx *context.APIContext) {
// swagger:operation GET /user/applications/oauth2/{id} user userGetOAuth2Application
// ---
- // summary: get an OAuth2 Application
+ // summary: Get an OAuth2 application
// produces:
// - application/json
// parameters:
@@ -377,11 +377,11 @@ func GetOauth2Application(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app))
}
-// UpdateOauth2Application update OAuth2 Application
+// UpdateOauth2Application updates an OAuth2 application
func UpdateOauth2Application(ctx *context.APIContext) {
// swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application
// ---
- // summary: update an OAuth2 Application, this includes regenerating the client secret
+ // summary: Update an OAuth2 application, this includes regenerating the client secret
// produces:
// - application/json
// parameters:
diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go
index d3833a32bb..2b0659c251 100644
--- a/routers/api/v1/user/avatar.go
+++ b/routers/api/v1/user/avatar.go
@@ -7,17 +7,17 @@ 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
+// UpdateAvatar updates doer's avatar
func UpdateAvatar(ctx *context.APIContext) {
// swagger:operation POST /user/avatar user userUpdateAvatar
// ---
- // summary: Update Avatar
+ // summary: Update avatar of the current user
// produces:
// - application/json
// parameters:
@@ -49,11 +49,11 @@ func UpdateAvatar(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
-// DeleteAvatar deletes the Avatar of an User
+// DeleteAvatar deletes doer's avatar
func DeleteAvatar(ctx *context.APIContext) {
// swagger:operation DELETE /user/avatar user userDeleteAvatar
// ---
- // summary: Delete Avatar
+ // summary: Delete avatar of the current user. It will be replaced by a default one
// produces:
// - application/json
// responses:
diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go
index 6bd7e10dd8..38da23442d 100644
--- a/routers/api/v1/user/email.go
+++ b/routers/api/v1/user/email.go
@@ -7,21 +7,21 @@ 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
+// ListEmails lists doer's all email addresses
// see https://github.com/gogits/go-gogs-client/wiki/Users-Emails#list-email-addresses-for-a-user
func ListEmails(ctx *context.APIContext) {
// swagger:operation GET /user/emails user userListEmails
// ---
- // summary: List the authenticated user's email addresses
+ // summary: List all email addresses of the current user
// produces:
// - application/json
// responses:
@@ -44,11 +44,11 @@ func ListEmails(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &apiEmails)
}
-// AddEmail add an email address
+// AddEmail adds an email address to doer's account
func AddEmail(ctx *context.APIContext) {
// swagger:operation POST /user/emails user userAddEmail
// ---
- // summary: Add email addresses
+ // summary: Add an email addresses to the current user's account
// produces:
// - application/json
// parameters:
@@ -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)
@@ -105,11 +102,11 @@ func AddEmail(ctx *context.APIContext) {
ctx.JSON(http.StatusCreated, apiEmails)
}
-// DeleteEmail delete email
+// DeleteEmail deletes an email address from doer's account
func DeleteEmail(ctx *context.APIContext) {
// swagger:operation DELETE /user/emails user userDeleteEmail
// ---
- // summary: Delete email addresses
+ // summary: Delete email addresses from the current user's account
// produces:
// - application/json
// parameters:
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 652007a78b..bb7f3d3522 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
}
@@ -248,11 +249,11 @@ type swaggerUserCurrentPostGPGKey struct {
Form api.CreateGPGKeyOption
}
-// CreateGPGKey create a GPG key belonging to the authenticated user
+// CreateGPGKey adds a GPG public key doer's account
func CreateGPGKey(ctx *context.APIContext) {
// swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey
// ---
- // summary: Create a GPG key
+ // summary: Add a GPG public key to current user's account
// consumes:
// - application/json
// produces:
@@ -273,11 +274,11 @@ func CreateGPGKey(ctx *context.APIContext) {
CreateUserGPGKey(ctx, *form, ctx.Doer.ID)
}
-// DeleteGPGKey remove a GPG key belonging to the authenticated user
+// DeleteGPGKey removes a GPG public key from doer's account
func DeleteGPGKey(ctx *context.APIContext) {
// swagger:operation DELETE /user/gpg_keys/{id} user userCurrentDeleteGPGKey
// ---
- // summary: Remove a GPG key
+ // summary: Remove a GPG public key from current user's account
// produces:
// - application/json
// parameters:
@@ -298,7 +299,7 @@ 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
}
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 7df13ee413..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.
@@ -155,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 5e8cdbeb58..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
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index 67ab0dd964..53455bcd75 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -6,19 +6,19 @@ 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
+// GetUserSettings returns doer's account settings
func GetUserSettings(ctx *context.APIContext) {
// swagger:operation GET /user/settings user getUserSettings
// ---
- // summary: Get user settings
+ // summary: Get current user's account settings
// produces:
// - application/json
// responses:
@@ -31,11 +31,11 @@ func GetUserSettings(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, convert.User2UserSettings(ctx.Doer))
}
-// UpdateUserSettings returns user settings
+// UpdateUserSettings updates settings in doer's account
func UpdateUserSettings(ctx *context.APIContext) {
// swagger:operation PATCH /user/settings user updateUserSettings
// ---
- // summary: Update user settings
+ // summary: Update settings in current user's account
// parameters:
// - name: body
// in: body
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..2bd2829d09 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
@@ -33,6 +33,11 @@ func Search(ctx *context.APIContext) {
// description: ID of the user to search for
// type: integer
// format: int64
+ // - name: sort
+ // in: query
+ // description: sort order of results
+ // type: string
+ // enum: [oldest, newest, alphabetically, reversealphabetically, recentupdate, leastupdate]
// - name: page
// in: query
// description: page number of results to return (1-based)
@@ -81,6 +86,7 @@ func Search(ctx *context.APIContext) {
SearchByEmail: true,
Visible: visible,
ListOptions: listOptions,
+ OrderBy: utils.GetDbSearchOrder(ctx),
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, map[string]any{
@@ -260,7 +266,7 @@ func ListBlockedUsers(ctx *context.APIContext) {
func BlockUser(ctx *context.APIContext) {
// swagger:operation PUT /user/block/{username} user userBlockUser
// ---
- // summary: Blocks a user from the doer.
+ // summary: Blocks a user from the doer
// produces:
// - application/json
// parameters:
@@ -293,7 +299,7 @@ func BlockUser(ctx *context.APIContext) {
func UnblockUser(ctx *context.APIContext) {
// swagger:operation PUT /user/unblock/{username} user userUnblockUser
// ---
- // summary: Unblocks a user from the doer.
+ // summary: Unblocks a user from the doer
// produces:
// - application/json
// parameters:
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/db_search_order.go b/routers/api/v1/utils/db_search_order.go
new file mode 100644
index 0000000000..f089ba5f16
--- /dev/null
+++ b/routers/api/v1/utils/db_search_order.go
@@ -0,0 +1,28 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "forgejo.org/models/db"
+ "forgejo.org/services/context"
+)
+
+func GetDbSearchOrder(ctx *context.APIContext) db.SearchOrderBy {
+ switch ctx.FormString("sort") {
+ case "oldest":
+ return db.SearchOrderByOldest
+ case "newest":
+ return db.SearchOrderByNewest
+ case "alphabetically":
+ return db.SearchOrderByAlphabetically
+ case "reversealphabetically":
+ return db.SearchOrderByAlphabeticallyReverse
+ case "recentupdate":
+ return db.SearchOrderByRecentUpdated
+ case "leastupdate":
+ return db.SearchOrderByLeastUpdated
+ default:
+ return db.SearchOrderByAlphabetically
+ }
+}
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 722c625e7b..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 {
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..4dc5a58858 100644
--- a/routers/common/errpage.go
+++ b/routers/common/errpage.go
@@ -7,15 +7,14 @@ 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"
)
const tplStatus500 base.TplName = "status/500"
@@ -36,8 +35,8 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) {
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
- tmplCtx := context.TemplateContext{}
- tmplCtx["Locale"] = middleware.Locale(w, req)
+ tmplCtx := templates.NewContext(req.Context())
+ tmplCtx.Locale = middleware.Locale(w, req)
ctxData := middleware.GetContextData(req.Context())
// This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much.
diff --git a/routers/common/errpage_test.go b/routers/common/errpage_test.go
index f15d3f1b35..3a492ea304 100644
--- a/routers/common/errpage_test.go
+++ b/routers/common/errpage_test.go
@@ -10,9 +10,9 @@ import (
"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"
)
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 ee2e504ff1..7bc4890a43 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -9,12 +9,12 @@ import (
"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"
@@ -46,7 +46,11 @@ func ProtocolMiddlewares() (handlers []any) {
defer finished()
trace.Log(ctx, "method", req.Method)
trace.Log(ctx, "url", req.RequestURI)
- next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
+
+ respWriter := context.WrapResponseWriter(resp)
+ next.ServeHTTP(respWriter, req.WithContext(cache.WithCacheContext(ctx)))
+
+ trace.Logf(ctx, "status", "%d", respWriter.WrittenStatus())
})
})
diff --git a/routers/common/middleware_test.go b/routers/common/middleware_test.go
index 6126e0afcc..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"
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..9a304527fa 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -8,50 +8,51 @@ 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"
+ federation_service "forgejo.org/services/federation"
+ 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) {
@@ -122,6 +123,7 @@ func InitWebInstalled(ctx context.Context) {
mailer.NewContext(ctx)
mustInit(cache.Init)
mustInit(feed_service.Init)
+ mustInit(federation_service.Init)
mustInit(uinotification.Init)
mustInitCtx(ctx, archiver.Init)
diff --git a/routers/install/install.go b/routers/install/install.go
index 86e342f1f9..f64f395a7f 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -15,25 +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/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"
)
@@ -361,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)
@@ -407,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")
@@ -482,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)
}
@@ -587,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 47e06245ed..35458f672e 100644
--- a/routers/private/hook_verification_test.go
+++ b/routers/private/hook_verification_test.go
@@ -6,8 +6,8 @@ package private
import (
"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"
)
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..90b48256df 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
@@ -145,6 +145,7 @@ func AddLogger(ctx *context.PrivateContext) {
writerMode.Prefix, _ = opts.Config["prefix"].(string)
writerMode.Expression, _ = opts.Config["expression"].(string)
+ writerMode.Exclusion, _ = opts.Config["exclusion"].(string)
switch writerType {
case "console":
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 311bfe6858..c831b44036 100644
--- a/routers/private/manager_unix.go
+++ b/routers/private/manager_unix.go
@@ -6,8 +6,8 @@ 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..7f08d4ca34 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -8,21 +8,42 @@ 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/auth"
+ "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"
)
+func checkTwoFactor(ctx *context.PrivateContext, user *user_model.User) {
+ if !user.MustHaveTwoFactor() {
+ return
+ }
+
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, user.ID)
+ if err != nil {
+ log.Error("Error getting 2fa: %s", err)
+ ctx.JSON(http.StatusInternalServerError, private.Response{
+ Err: fmt.Sprintf("Error getting 2fa: %s", err),
+ })
+ return
+ }
+ if !hasTwoFactor {
+ ctx.JSON(http.StatusForbidden, private.Response{
+ UserMsg: ctx.Locale.TrString("error.must_enable_2fa", fmt.Sprintf("%suser/settings/security", setting.AppURL)),
+ })
+ return
+ }
+}
+
// ServNoCommand returns information about the provided keyid
func ServNoCommand(ctx *context.PrivateContext) {
keyID := ctx.ParamsInt64(":keyid")
@@ -70,6 +91,12 @@ func ServNoCommand(ctx *context.PrivateContext) {
})
return
}
+
+ checkTwoFactor(ctx, user)
+ if ctx.Written() {
+ return
+ }
+
results.Owner = user
}
ctx.JSON(http.StatusOK, &results)
@@ -267,6 +294,11 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
+ checkTwoFactor(ctx, user)
+ if ctx.Written() {
+ return
+ }
+
results.UserName = user.Name
if !user.KeepEmailPrivate {
results.UserEmail = user.Email
@@ -296,8 +328,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 8af14f6d52..c352b6ad1a 100644
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -10,20 +10,20 @@ import (
"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/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/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"
)
@@ -190,6 +190,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
AdminGroup: form.Oauth2AdminGroup,
GroupTeamMap: form.Oauth2GroupTeamMap,
GroupTeamMapRemoval: form.Oauth2GroupTeamMapRemoval,
+ AllowUsernameChange: form.AllowUsernameChange,
}
}
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index 06d0ea60fb..e1c3a5f9ee 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"
)
@@ -127,6 +128,7 @@ func Config(ctx *context.Context) {
ctx.Data["AppBuiltWith"] = setting.AppBuiltWith
ctx.Data["Domain"] = setting.Domain
ctx.Data["OfflineMode"] = setting.OfflineMode
+ ctx.Data["GlobalTwoFactorRequirement"] = setting.GlobalTwoFactorRequirement
ctx.Data["RunUser"] = setting.RunUser
ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
ctx.Data["GitVersion"] = git.VersionInfo()
@@ -145,6 +147,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 959c9bc444..8b0ec45214 100644
--- a/routers/web/admin/diagnosis.go
+++ b/routers/web/admin/diagnosis.go
@@ -5,14 +5,16 @@ 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) {
@@ -45,14 +47,9 @@ func MonitorDiagnosis(ctx *context.Context) {
_, _ = f.Write([]byte(err.Error()))
}
- 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 := trace.Start(f); err != nil {
- _, _ = f.Write([]byte(err.Error()))
+ traceBuf := &bytes.Buffer{}
+ if err := trace.Start(traceBuf); err != nil {
+ _, _ = traceBuf.Write([]byte(err.Error()))
}
select {
@@ -62,6 +59,17 @@ func MonitorDiagnosis(ctx *context.Context) {
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)
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/reports.go b/routers/web/admin/reports.go
new file mode 100644
index 0000000000..ac43d1296f
--- /dev/null
+++ b/routers/web/admin/reports.go
@@ -0,0 +1,157 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package admin
+
+import (
+ "fmt"
+ "net/http"
+
+ "forgejo.org/models/issues"
+ "forgejo.org/models/moderation"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/base"
+ "forgejo.org/services/context"
+ moderation_service "forgejo.org/services/moderation"
+)
+
+const (
+ tplModerationReports base.TplName = "admin/moderation/reports"
+ tplModerationReportDetails base.TplName = "admin/moderation/report_details"
+)
+
+// AbuseReports renders the reports overview page from admin moderation section.
+func AbuseReports(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.moderation.reports")
+ ctx.Data["PageIsAdminModerationReports"] = true
+
+ reports, err := moderation.GetOpenReports(ctx)
+ if err != nil {
+ ctx.ServerError("Failed to load abuse reports", err)
+ return
+ }
+
+ ctx.Data["Reports"] = reports
+ ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys
+ ctx.Data["GhostUserName"] = user.GhostUserName
+
+ ctx.HTML(http.StatusOK, tplModerationReports)
+}
+
+// AbuseReportDetails renders a report details page opened from the reports overview from admin moderation section.
+func AbuseReportDetails(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.moderation.reports")
+ ctx.Data["PageIsAdminModerationReports"] = true
+
+ ctx.Data["Type"] = ctx.ParamsInt64(":type")
+ ctx.Data["ID"] = ctx.ParamsInt64(":id")
+
+ contentType := moderation.ReportedContentType(ctx.ParamsInt64(":type"))
+
+ if !contentType.IsValid() {
+ ctx.Flash.Error("Invalid content type")
+ return
+ }
+
+ reports, err := moderation.GetOpenReportsByTypeAndContentID(ctx, contentType, ctx.ParamsInt64(":id"))
+ if err != nil {
+ ctx.ServerError("Failed to load reports", err)
+ return
+ }
+ if len(reports) == 0 {
+ // something is wrong
+ ctx.HTML(http.StatusOK, tplModerationReportDetails)
+ return
+ }
+
+ ctx.Data["Reports"] = reports
+ ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys
+ ctx.Data["GhostUserName"] = user.GhostUserName
+
+ ctx.Data["GetShadowCopyMap"] = moderation_service.GetShadowCopyMap
+
+ if err = setReportedContentDetails(ctx, reports[0]); err != nil {
+ if user.IsErrUserNotExist(err) || issues.IsErrCommentNotExist(err) || issues.IsErrIssueNotExist(err) || repo_model.IsErrRepoNotExist(err) {
+ ctx.Data["ContentReference"] = ctx.Tr("admin.moderation.deleted_content_ref", reports[0].ContentType, reports[0].ContentID)
+ } else {
+ ctx.ServerError("Failed to load reported content details", err)
+ return
+ }
+ }
+
+ ctx.HTML(http.StatusOK, tplModerationReportDetails)
+}
+
+// setReportedContentDetails adds some values into context data for the given report
+// (icon name, a reference, the URL and in case of issues and comments also the poster name).
+func setReportedContentDetails(ctx *context.Context, report *moderation.AbuseReportDetailed) error {
+ contentReference := ""
+ var contentURL string
+ var poster string
+ contentType := report.ContentType
+ contentID := report.ContentID
+
+ ctx.Data["ContentTypeIconName"] = report.ContentTypeIconName()
+
+ switch contentType {
+ case moderation.ReportedContentTypeUser:
+ reportedUser, err := user.GetUserByID(ctx, contentID)
+ if err != nil {
+ return err
+ }
+
+ contentReference = reportedUser.Name
+ contentURL = reportedUser.HomeLink()
+ case moderation.ReportedContentTypeRepository:
+ repo, err := repo_model.GetRepositoryByID(ctx, contentID)
+ if err != nil {
+ return err
+ }
+
+ contentReference = repo.FullName()
+ contentURL = repo.Link()
+ case moderation.ReportedContentTypeIssue:
+ issue, err := issues.GetIssueByID(ctx, contentID)
+ if err != nil {
+ return err
+ }
+ if err = issue.LoadRepo(ctx); err != nil {
+ return err
+ }
+ if err = issue.LoadPoster(ctx); err != nil {
+ return err
+ }
+ if issue.Poster != nil {
+ poster = issue.Poster.Name
+ }
+
+ contentReference = fmt.Sprintf("%s#%d", issue.Repo.FullName(), issue.Index)
+ contentURL = issue.Link()
+ case moderation.ReportedContentTypeComment:
+ comment, err := issues.GetCommentByID(ctx, contentID)
+ if err != nil {
+ return err
+ }
+ if err = comment.LoadIssue(ctx); err != nil {
+ return err
+ }
+ if err = comment.Issue.LoadRepo(ctx); err != nil {
+ return err
+ }
+ if err = comment.LoadPoster(ctx); err != nil {
+ return err
+ }
+ if comment.Poster != nil {
+ poster = comment.Poster.Name
+ }
+
+ contentURL = comment.Link(ctx)
+ contentReference = contentURL
+ }
+
+ ctx.Data["ContentReference"] = contentReference
+ ctx.Data["ContentURL"] = contentURL
+ ctx.Data["Poster"] = poster
+ return nil
+}
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 36ce8d286c..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,11 +76,11 @@ 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,
@@ -187,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):
@@ -196,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)
}
@@ -249,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
}
@@ -322,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
@@ -416,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 1d00c97b6e..dbb6665398 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -11,30 +11,30 @@ import (
"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"
)
@@ -242,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
@@ -512,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)
@@ -528,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
}
@@ -551,9 +552,6 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us
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.IsErrEmailCharIsNotSupported(err):
- ctx.Data["Err_Email"] = true
- ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
case validation.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
@@ -766,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
@@ -783,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 e6c9089380..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"
)
@@ -155,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 62b7b0b6d3..1119e1947b 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -17,29 +17,29 @@ import (
"sort"
"strings"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "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"
@@ -225,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),
},
@@ -409,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)
}
@@ -489,7 +489,7 @@ func AuthorizeOAuth(ctx *context.Context) {
}, form.RedirectURI)
return
}
- if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil {
+ if err := ctx.Session.Set("CodeChallenge", form.CodeChallenge); err != nil {
handleAuthorizeError(ctx, AuthorizeError{
ErrorCode: ErrorCodeServerError,
ErrorDescription: "cannot set code challenge",
@@ -668,7 +668,13 @@ func GrantApplicationOAuth(ctx *context.Context) {
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
func OIDCWellKnown(ctx *context.Context) {
+ if !setting.OAuth2.Enabled {
+ ctx.Status(http.StatusNotFound)
+ return
+ }
+
ctx.Data["SigningKey"] = oauth2.DefaultSigningKey
+ ctx.Data["Issuer"] = strings.TrimSuffix(setting.AppURL, "/")
ctx.JSONTemplate("user/auth/oidc_wellknown")
}
@@ -1079,7 +1085,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
@@ -1243,12 +1249,11 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
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..c645bbdede 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
}
@@ -268,7 +264,7 @@ func ResetPasswdPost(ctx *context.Context) {
func MustChangePassword(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
- ctx.Data["MustChangePassword"] = true
+ ctx.Data["HideNavbarLinks"] = true
ctx.HTML(http.StatusOK, tplMustChangePassword)
}
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 0a4e828c71..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 (
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 241e5f61a1..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) {
@@ -130,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")
@@ -169,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 a8a001e0cd..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"
)
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 5f7687d803..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 {
diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go
index 48f87c7c62..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"
)
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..d24fa6ecc7 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"
)
@@ -29,7 +29,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas
if isReleasesOnly {
title = ctx.Locale.TrString("repo.release.releases_for", repo.FullName())
- link = &feeds.Link{Href: repo.HTMLURL() + "/release"}
+ link = &feeds.Link{Href: repo.HTMLURL() + "/releases"}
} else {
title = ctx.Locale.TrString("repo.release.tags_for", repo.FullName())
link = &feeds.Link{Href: repo.HTMLURL() + "/tags"}
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 4ea961c055..55dfe2538e 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,20 @@ 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"
+ auth_model "forgejo.org/models/auth"
+ "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 (
@@ -34,18 +36,37 @@ func Home(ctx *context.Context) {
if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(http.StatusOK, auth.TplActivate)
- } else if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin {
+ return
+ }
+ if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin {
log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr())
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
- } else if ctx.Doer.MustChangePassword {
+ return
+ }
+ if ctx.Doer.MustChangePassword {
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
- } else {
- user.Dashboard(ctx)
+ return
}
+ if ctx.Doer.MustHaveTwoFactor() {
+ hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
+ log.Error("Error getting 2fa: %s", err)
+ ctx.Error(http.StatusInternalServerError, "HasTwoFactorByUID", err.Error())
+ return
+ }
+ if !hasTwoFactor {
+ ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
+ ctx.Redirect(setting.AppSubURL + "/user/settings/security")
+ return
+ }
+ }
+
+ user.Dashboard(ctx)
return
// Check non-logged users landing page.
} else if setting.LandingPageURL != setting.LandingPageHome {
@@ -112,9 +133,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 1b58d8fde9..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 (
@@ -175,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{
@@ -188,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 034483aba2..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 (
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 1683728a8e..9b4e01597b 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -9,25 +9,25 @@ import (
"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 (
@@ -50,6 +50,9 @@ func Settings(ctx *context.Context) {
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 {
@@ -68,6 +71,9 @@ func SettingsPost(ctx *context.Context) {
ctx.Data["PageIsSettingsOptions"] = true
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
+ 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, tplSettingsOptions)
diff --git a/routers/web/org/setting/blocked_users.go b/routers/web/org/setting/blocked_users.go
index 2cf3f39ef4..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"
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
index 4b9bd02ca4..5714d7ee23 100644
--- a/routers/web/org/setting/storage_overview.go
+++ b/routers/web/org/setting/storage_overview.go
@@ -4,9 +4,9 @@
package setting
import (
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/routers/web/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/routers/web/shared"
+ "forgejo.org/services/context"
)
const (
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
index 181c1120db..94e6d506c5 100644
--- a/routers/web/repo/action_aggregator_test.go
+++ b/routers/web/repo/action_aggregator_test.go
@@ -7,10 +7,10 @@ import (
"strings"
"testing"
- issue_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/organization"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
+ 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"
)
@@ -94,6 +94,14 @@ func reqReview(t int64, name string, delReq bool) *issue_model.Comment {
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 {
@@ -179,12 +187,12 @@ func (kase *testCase) doTest(t *testing.T) {
if len(after) != len(issue.Comments) {
t.Logf("Expected %v comments, got %v", len(after), len(issue.Comments))
- t.Logf("Comments got after combination:")
+ 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.EqualValues(t, len(after), len(issue.Comments))
+ assert.Len(t, issue.Comments, len(after))
t.Fail()
return
}
@@ -214,7 +222,7 @@ func (kase *testCase) doTest(t *testing.T) {
l.AssigneeTeamID = 0
}
- assert.EqualValues(t, (after)[c], issue.Comments[c],
+ assert.Equal(t, (after)[c], issue.Comments[c],
"Comment %v is not equal", c,
)
}
@@ -588,6 +596,27 @@ func TestCombineReviewRequests(t *testing.T) {
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 {
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index e0ef709ea6..0c07e1968e 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"
+ "code.forgejo.org/forgejo/runner/v9/act/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)
@@ -108,7 +111,7 @@ func List(ctx *context.Context) {
ctx.ServerError("GetContentFromEntry", err)
return
}
- wf, err := model.ReadWorkflow(bytes.NewReader(content))
+ wf, err := model.ReadWorkflow(bytes.NewReader(content), true)
if err != nil {
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error())
workflows = append(workflows, workflow)
@@ -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 285dc7ab7e..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) {
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 857e34381e..408a2844de 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/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"
- "code.gitea.io/gitea/services/repository/gitgraph"
+ 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,8 +83,19 @@ 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)
+ commitIDs := make([]string, 0, len(commits))
+ for _, c := range commits {
+ commitIDs = append(commitIDs, c.ID.String())
+ }
+ commitTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...)
+ if err != nil {
+ log.Error("FindTagsByCommitIDs: %v", err)
+ ctx.Flash.Error(ctx.Tr("repo.commit.load_tags_failed"))
+ } else {
+ ctx.Data["CommitTagsMap"] = commitTagsMap
+ }
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["CommitCount"] = commitsCount
@@ -202,7 +212,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 +277,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
@@ -329,7 +339,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,
@@ -375,9 +385,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
@@ -456,20 +463,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 24785d867e..feedeef945 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 (
@@ -312,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
@@ -514,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
@@ -533,17 +520,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch
}
- if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) {
- if log.IsTrace() {
- log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
- ctx.Doer,
- baseRepo,
- permBase)
- }
- ctx.NotFound("ParseCompareInfo", nil)
- return nil
- }
-
baseBranchRef := ci.BaseBranch
if baseIsBranch {
baseBranchRef = git.BranchPrefix + ci.BaseBranch
@@ -597,7 +573,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
@@ -621,7 +597,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,
@@ -654,15 +630,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")
}
@@ -933,28 +909,33 @@ func ExcerptBlob(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, "getExcerptLines")
return
}
- if idxRight > lastRight {
+
+ // After the "up" or "down" expansion, check if there's any remaining content in the diff and add a line that will
+ // be rendered into a new expander at either the top, or bottom.
+ lineSection := &gitdiff.DiffLine{
+ Type: gitdiff.DiffLineSection,
+ SectionInfo: &gitdiff.DiffLineSectionInfo{
+ Path: filePath,
+ LastLeftIdx: lastLeft,
+ LastRightIdx: lastRight,
+ LeftIdx: idxLeft,
+ RightIdx: idxRight,
+ LeftHunkSize: leftHunkSize,
+ RightHunkSize: rightHunkSize,
+ },
+ }
+ if lineSection.GetExpandDirection() != gitdiff.DiffLineExpandNone {
lineText := " "
if rightHunkSize > 0 || leftHunkSize > 0 {
lineText = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize)
}
lineText = html.EscapeString(lineText)
- lineSection := &gitdiff.DiffLine{
- Type: gitdiff.DiffLineSection,
- Content: lineText,
- SectionInfo: &gitdiff.DiffLineSectionInfo{
- Path: filePath,
- LastLeftIdx: lastLeft,
- LastRightIdx: lastRight,
- LeftIdx: idxLeft,
- RightIdx: idxRight,
- LeftHunkSize: leftHunkSize,
- RightHunkSize: rightHunkSize,
- },
- }
- if direction == "up" {
+ lineSection.Content = lineText
+
+ switch direction {
+ case "up":
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...)
- } else if direction == "down" {
+ case "down":
section.Lines = append(section.Lines, lineSection)
}
}
@@ -966,7 +947,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 d7fe368474..9fb4d78fe3 100644
--- a/routers/web/repo/download.go
+++ b/routers/web/repo/download.go
@@ -7,15 +7,15 @@ package repo
import (
"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
@@ -92,7 +92,7 @@ func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
return nil, nil
}
- if entry.IsDir() || entry.IsSubModule() {
+ if entry.IsDir() || entry.IsSubmodule() {
ctx.NotFound("getBlobForEntry", nil)
return nil, nil
}
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index f27ad62982..3e3cb0016d 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 (
@@ -189,7 +189,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
buf = buf[:n]
// Only some file types are editable online as text.
- if !typesniffer.DetectContentType(buf).IsRepresentableAsText() {
+ if !typesniffer.DetectContentType(buf, blob.Name()).IsRepresentableAsText() {
ctx.NotFound("typesniffer.IsRepresentableAsText", nil)
return
}
@@ -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 bced8e61b1..a60c213113 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"
)
@@ -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)
@@ -194,24 +192,19 @@ func httpBase(ctx *context.Context) *serviceHandler {
ctx.ServerError("GetTaskByID", err)
return nil
}
- if task.RepoID != repo.ID {
+
+ p, err := access_model.GetActionRepoPermission(ctx, repo, task)
+ if err != nil {
+ ctx.ServerError("GetActionRepoPermission", err)
+ return nil
+ }
+
+ if !p.CanAccess(accessMode, unitType) {
ctx.PlainText(http.StatusForbidden, "User permission denied")
return nil
}
- if task.IsForkPullRequest {
- if accessMode > perm.AccessModeRead {
- ctx.PlainText(http.StatusForbidden, "User permission denied")
- return nil
- }
- environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeRead))
- } else {
- if accessMode > perm.AccessModeWrite {
- ctx.PlainText(http.StatusForbidden, "User permission denied")
- return nil
- }
- environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeWrite))
- }
+ environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, p.AccessMode))
} else {
p, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
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 e45abd3952..0373d06ea0 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -20,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"
)
@@ -187,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 {
@@ -347,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)
@@ -426,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 {
@@ -1311,7 +1308,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use
}
// Special user that can't have associated contributions and permissions in the repo.
- if poster.IsGhost() || poster.IsActions() || poster.IsAPActor() {
+ if poster.IsSystem() || poster.IsAPServerActor() {
return roleDescriptor, nil
}
@@ -1475,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")
@@ -1695,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 {
@@ -1796,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 {
@@ -2128,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)
@@ -2412,10 +2401,6 @@ func UpdateIssueMilestone(ctx *context.Context) {
}
if ctx.FormBool("htmx") {
- renderMilestones(ctx)
- if ctx.Written() {
- return
- }
prepareHiddenCommentType(ctx)
if ctx.Written() {
return
@@ -2429,6 +2414,7 @@ func UpdateIssueMilestone(ctx *context.Context) {
ctx.ServerError("GetMilestoneByRepoID", err)
return
}
+ ctx.Data["OpenMilestones"] = true
} else {
issue.Milestone = nil
}
@@ -2786,7 +2772,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 {
@@ -2815,9 +2801,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 {
@@ -2955,7 +2942,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)
@@ -3117,7 +3104,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
@@ -3253,11 +3240,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) {
- if issue.IsPull {
- ctx.JSONError(ctx.Tr("repo.pulls.comment.blocked_by_user"))
- } else {
- ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user"))
- }
+ ctx.JSONError(ctx.Tr("repo.comment.blocked_by_user"))
} else {
ctx.ServerError("CreateIssueComment", err)
}
@@ -3607,9 +3590,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)
}
@@ -3632,7 +3615,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
}
@@ -3642,13 +3625,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)
}
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_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 0689b0a721..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)
}
@@ -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"))
@@ -262,7 +262,7 @@ 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"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, projectID)
@@ -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..18ba2b2a9f 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"
)
@@ -356,7 +360,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
ctx.Data["Issue"] = issue
if !issue.IsPull {
- ctx.NotFound("ViewPullCommits", nil)
+ ctx.Redirect(issue.Link())
return nil, false
}
@@ -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
}
@@ -624,7 +638,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
if pull.Flow == issues_model.PullRequestFlowGithub {
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
} else {
- headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
+ headBranchExist = baseGitRepo.IsReferenceExist(pull.GetGitRefName())
}
if headBranchExist {
@@ -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...)
@@ -1053,7 +1152,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.ServerError("GetUserRepoPermission", err)
return
}
- ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer)
+ ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer) && pull.Flow != issues_model.PullRequestFlowAGit
ctx.Data["SourceRepoLink"] = pull.HeadRepo.Link()
ctx.Data["HeadBranch"] = pull.HeadBranch
}
@@ -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 {
@@ -1348,6 +1445,10 @@ func MergePullRequest(ctx *context.Context) {
log.Debug("MergeUnrelatedHistories error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
ctx.JSONRedirect(issue.Link())
+ } else if models.IsErrPullRequestHasMerged(err) {
+ log.Debug("MergePullRequestHasMerged error: %v", err)
+ ctx.Flash.Error(ctx.Tr("repo.pulls.already_merged"))
+ ctx.JSONRedirect(issue.Link())
} else if git.IsErrPushOutOfDate(err) {
log.Debug("MergePushOutOfDate error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
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..05eeadc519 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
@@ -41,7 +41,7 @@ func RenderFile(ctx *context.Context) {
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
- st := typesniffer.DetectContentType(buf)
+ st := typesniffer.DetectContentType(buf, blob.Name())
isTextFile := st.IsText()
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 2e8ca61bf5..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 (
@@ -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 d10eb67528..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"
diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go
index 504f57cfc2..20e211316d 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
@@ -45,8 +45,8 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
if err != nil {
return fmt.Errorf("io.ReadAll: %w", err)
}
- st := typesniffer.DetectContentType(data)
- if !(st.IsImage() && !st.IsSvgImage()) {
+ st := typesniffer.DetectContentType(data, "")
+ 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..9930d03e8e 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 (
@@ -291,7 +291,7 @@ func LFSFileGet(ctx *context.Context) {
}
buf = buf[:n]
- st := typesniffer.DetectContentType(buf)
+ st := typesniffer.DetectContentType(buf, "")
ctx.Data["IsTextFile"] = st.IsText()
isRepresentableAsText := st.IsRepresentableAsText()
@@ -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..595fdace83 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -6,6 +6,7 @@
package setting
import (
+ go_context "context"
"errors"
"fmt"
"net/http"
@@ -13,34 +14,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 +65,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 +109,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 +154,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,
@@ -584,6 +590,23 @@ func SettingsPost(ctx *context.Context) {
ctx.ServerError("UpdatePushMirrorInterval", err)
return
}
+
+ if m.BranchFilter != form.PushMirrorBranchFilter {
+ // replace `remote..push` in config and db
+ m.BranchFilter = form.PushMirrorBranchFilter
+ if err := db.WithTx(ctx, func(ctx go_context.Context) error {
+ // Update the DB
+ if err = repo_model.UpdatePushMirrorBranchFilter(ctx, m); err != nil {
+ return err
+ }
+ // Update the repo config
+ return mirror_service.UpdatePushMirrorBranchFilter(ctx, m)
+ }); err != nil {
+ ctx.ServerError("UpdatePushMirrorBranchFilter", err)
+ return
+ }
+ }
+
// Background why we are adding it to Queue
// If we observed its implementation in the context of `push-mirror-sync` where it
// is evident that pushing to the queue is necessary for updates.
@@ -679,6 +702,7 @@ func SettingsPost(ctx *context.Context) {
SyncOnCommit: form.PushMirrorSyncOnCommit,
Interval: interval,
RemoteAddress: remoteAddress,
+ BranchFilter: form.PushMirrorBranchFilter,
}
var plainPrivateKey []byte
@@ -772,6 +796,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 +1056,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..20ea9babbe 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"
)
@@ -42,7 +42,7 @@ func isExcludedEntry(entry *git.TreeEntry) bool {
return true
}
- if entry.IsSubModule() {
+ if entry.IsSubmodule() {
return true
}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 9030b03a90..cd0af75b8f 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,37 +25,38 @@ 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"
+ "code.forgejo.org/forgejo/runner/v9/act/model"
_ "golang.org/x/image/bmp" // for processing bmp images
_ "golang.org/x/image/webp" // for processing webp images
@@ -226,7 +228,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte,
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
- st := typesniffer.DetectContentType(buf)
+ st := typesniffer.DetectContentType(buf, blob.Name())
isTextFile := st.IsText()
// FIXME: what happens when README file is an image?
@@ -260,7 +262,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte,
}
buf = buf[:n]
- st = typesniffer.DetectContentType(buf)
+ st = typesniffer.DetectContentType(buf, blob.Name())
return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
}
@@ -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
@@ -435,13 +434,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
if err != nil {
log.Error("actions.GetContentFromEntry: %v", err)
}
- _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content))
+ _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content), true)
if workFlowErr != nil {
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)
+ } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"}, ctx.Repo.TreePath) {
+ 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")
}
@@ -625,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
@@ -1044,7 +1057,14 @@ func renderHomeCode(ctx *context.Context) {
return
}
- if entry.IsDir() {
+ if entry.IsSubmodule() {
+ submodule, err := ctx.Repo.Commit.GetSubmodule(ctx.Repo.TreePath, entry)
+ if err != nil {
+ HandleGitError(ctx, "Repo.Commit.GetSubmodule", err)
+ return
+ }
+ ctx.Redirect(submodule.ResolveUpstreamURL(ctx.Repo.Repository.HTMLURL()))
+ } else if entry.IsDir() {
renderDirectory(ctx)
} else {
renderFile(ctx, entry)
@@ -1146,6 +1166,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()
@@ -1187,7 +1221,7 @@ func checkOutdatedBranch(ctx *context.Context) {
}
if dbBranch.CommitID != commit.ID.String() {
- ctx.Flash.Warning(ctx.Tr("repo.error.broken_git_hook", "https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running"), true)
+ ctx.Flash.Warning(ctx.Tr("warning.repository.out_of_sync"), true)
}
}
@@ -1217,6 +1251,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) {
@@ -1228,6 +1263,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 070d07cdf3..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)
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 66dce1412b..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.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
index 3bebdfb688..fac4aa99e5 100644
--- a/routers/web/shared/storage_overview.go
+++ b/routers/web/shared/storage_overview.go
@@ -7,10 +7,10 @@ import (
"html/template"
"net/http"
- quota_model "code.gitea.io/gitea/models/quota"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/context"
+ 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
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 019249e3e0..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 (
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index a0841c0227..d980fa393a 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -13,27 +13,26 @@ import (
"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"
@@ -611,11 +610,6 @@ 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.
@@ -655,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 {
diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go
index c09f609161..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,15 +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.EqualValues(t, "user2/glob", ctx.Data["Repos"].(repo_model.RepositoryList)[0].FullName())
- assert.EqualValues(t, "user2/repo1", ctx.Data["Repos"].(repo_model.RepositoryList)[1].FullName())
+ 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) {
@@ -113,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"])
@@ -144,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))
@@ -163,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 70ea20d388..2862c6684b 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -8,28 +8,28 @@ import (
"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/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 (
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index de1c6850aa..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 (
@@ -69,17 +70,6 @@ func userProfile(ctx *context.Context) {
ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL()
ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description
- // 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)
- }
-
profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
@@ -170,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{
@@ -253,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{
@@ -269,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 4dfd859a44..e73239b79b 100644
--- a/routers/web/user/setting/applications.go
+++ b/routers/web/user/setting/applications.go
@@ -7,14 +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/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"
+ 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 (
@@ -49,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,
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 271621872f..e0ce88b582 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -15,23 +15,23 @@ import (
"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 (
@@ -51,6 +51,9 @@ func Profile(ctx *context.Context) {
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
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)
}
@@ -63,6 +66,9 @@ func ProfilePost(ctx *context.Context) {
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
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)
@@ -145,8 +151,8 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *
return fmt.Errorf("io.ReadAll: %w", err)
}
- st := typesniffer.DetectContentType(data)
- if !(st.IsImage() && !st.IsSvgImage()) {
+ st := typesniffer.DetectContentType(data, "")
+ 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 {
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..d23917f8b2 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)
@@ -60,6 +56,21 @@ func DisableTwoFactor(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsSecurity"] = true
+ if ctx.Doer.MustHaveTwoFactor() {
+ ctx.NotFound("DisableTwoFactor", nil)
+ return
+ }
+
+ disableTwoFactor(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
+ ctx.Redirect(setting.AppSubURL + "/user/settings/security")
+}
+
+func disableTwoFactor(ctx *context.Context) {
t, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID)
if err != nil {
if auth.IsErrTwoFactorNotEnrolled(err) {
@@ -86,9 +97,6 @@ func DisableTwoFactor(ctx *context.Context) {
ctx.ServerError("SendDisabledTOTP", err)
return
}
-
- ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
- ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
func twofaGenerateSecretAndQr(ctx *context.Context) bool {
@@ -176,7 +184,6 @@ func EnrollTwoFactor(ctx *context.Context) {
// EnrollTwoFactorPost handles enrolling the user into 2FA.
func EnrollTwoFactorPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.TwoFactorAuthForm)
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsSecurity"] = true
@@ -192,6 +199,12 @@ func EnrollTwoFactorPost(ctx *context.Context) {
return
}
+ enrollTwoFactor(ctx)
+}
+
+func enrollTwoFactor(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.TwoFactorAuthForm)
+
if ctx.HasError() {
if !twofaGenerateSecretAndQr(ctx) {
return
@@ -217,14 +230,10 @@ func EnrollTwoFactorPost(ctx *context.Context) {
return
}
- t = &auth.TwoFactor{
+ twoFactor := &auth.TwoFactor{
UID: ctx.Doer.ID,
}
- token, err := t.GenerateScratchToken()
- if err != nil {
- ctx.ServerError("SettingsTwoFactor: Failed to generate scratch token", err)
- return
- }
+ token := twoFactor.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
@@ -246,7 +255,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
return
}
- if err = auth.NewTwoFactor(ctx, t, secret); err != nil {
+ if err := auth.NewTwoFactor(ctx, twoFactor, secret); err != nil {
// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us.
// If there is a unique constraint fail we should just tolerate the error
ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err)
@@ -256,3 +265,41 @@ func EnrollTwoFactorPost(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", token))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
+
+// ReenrollTwoFactor shows the page where the user can reenroll 2FA.
+func ReenrollTwoFactor(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings")
+ ctx.Data["PageIsSettingsSecurity"] = true
+ ctx.Data["ReenrollTwofa"] = true
+
+ _, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID)
+ if auth.IsErrTwoFactorNotEnrolled(err) {
+ ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
+ ctx.Redirect(setting.AppSubURL + "/user/settings/security")
+ return
+ }
+ if err != nil {
+ ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err)
+ return
+ }
+
+ if !twofaGenerateSecretAndQr(ctx) {
+ return
+ }
+
+ ctx.HTML(http.StatusOK, tplSettingsTwofaEnroll)
+}
+
+// ReenrollTwoFactorPost handles reenrolling the user 2FA.
+func ReenrollTwoFactorPost(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings")
+ ctx.Data["PageIsSettingsSecurity"] = true
+ ctx.Data["ReenrollTwofa"] = true
+
+ disableTwoFactor(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ enrollTwoFactor(ctx)
+}
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
index 8a0c773077..4586600572 100644
--- a/routers/web/user/setting/storage_overview.go
+++ b/routers/web/user/setting/storage_overview.go
@@ -4,9 +4,9 @@
package setting
import (
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/routers/web/shared"
- "code.gitea.io/gitea/services/context"
+ "forgejo.org/modules/base"
+ "forgejo.org/routers/web/shared"
+ "forgejo.org/services/context"
)
const (
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 15264ccc89..f1cbc6b7ad 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1,53 +1,57 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package web
import (
gocontext "context"
+ "fmt"
"net/http"
"strings"
- "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"
+ auth_model "forgejo.org/models/auth"
+ "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"
@@ -115,7 +119,7 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
return func(ctx *context.Context) {
ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod)
if err != nil {
- log.Error("Failed to verify user: %v", err)
+ log.Info("Failed to verify user: %v", err)
ctx.Error(http.StatusUnauthorized, ctx.Locale.TrString("auth.unauthorized_credentials", "https://codeberg.org/forgejo/forgejo/issues/2809"))
return
}
@@ -167,6 +171,19 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
ctx.Redirect(setting.AppSubURL + "/")
return
}
+
+ if ctx.Doer.MustHaveTwoFactor() && !strings.HasPrefix(ctx.Req.URL.Path, "/user/settings/security") {
+ hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ if err != nil {
+ log.Error("Error getting 2fa: %s", err)
+ ctx.Error(http.StatusInternalServerError, "HasTwoFactorByUID", err.Error())
+ return
+ }
+ if !hasTwoFactor {
+ ctx.Redirect(setting.AppSubURL + "/user/settings/security")
+ return
+ }
+ }
}
// Redirect to dashboard (or alternate location) if user tries to visit any non-login page.
@@ -307,6 +324,20 @@ func registerRoutes(m *web.Route) {
}
}
+ requiredTwoFactor := func(ctx *context.Context) {
+ if !ctx.Doer.MustHaveTwoFactor() {
+ return
+ }
+
+ hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Error getting 2fa: %s", err))
+ return
+ }
+ ctx.Data["MustEnableTwoFactor"] = !hasTwoFactor
+ ctx.Data["HideNavbarLinks"] = !hasTwoFactor
+ }
+
openIDSignInEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignIn {
ctx.Error(http.StatusForbidden)
@@ -473,6 +504,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)
@@ -557,6 +593,8 @@ func registerRoutes(m *web.Route) {
m.Post("/disable", security.DisableTwoFactor)
m.Get("/enroll", security.EnrollTwoFactor)
m.Post("/enroll", web.Bind(forms.TwoFactorAuthForm{}), security.EnrollTwoFactorPost)
+ m.Get("/reenroll", security.ReenrollTwoFactor)
+ m.Post("/reenroll", web.Bind(forms.TwoFactorAuthForm{}), security.ReenrollTwoFactorPost)
})
m.Group("/webauthn", func() {
m.Post("/request_register", web.Bind(forms.WebauthnRegistrationForm{}), security.WebAuthnRegister)
@@ -569,7 +607,7 @@ func registerRoutes(m *web.Route) {
m.Post("/toggle_visibility", security.ToggleOpenIDVisibility)
}, openIDSignInEnabled)
m.Post("/account_link", linkAccountEnabled, security.DeleteAccountLink)
- })
+ }, requiredTwoFactor)
m.Group("/applications", func() {
// oauth2 applications
@@ -774,7 +812,14 @@ func registerRoutes(m *web.Route) {
addSettingsRunnersRoutes()
addSettingsVariablesRoutes()
})
- }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled))
+
+ if setting.Moderation.Enabled {
+ m.Group("/moderation/reports", func() {
+ m.Get("", admin.AbuseReports)
+ m.Get("/type/{type:1|2|3|4}/id/{id}", admin.AbuseReportDetails)
+ })
+ }
+ }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "EnableModeration", setting.Moderation.Enabled))
// ***** END: Admin *****
m.Group("", func() {
@@ -811,13 +856,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
@@ -1392,7 +1437,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)
@@ -1454,7 +1499,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))
@@ -1503,7 +1548,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)
@@ -1591,6 +1639,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)
@@ -1661,6 +1711,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..5f67e436bf 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
@@ -58,7 +58,33 @@ func WebfingerQuery(ctx *context.Context) {
return
}
+ // Instance actor
+ if parts[0] == "ghost" {
+ aliases := []string{
+ appURL.String() + "api/v1/activitypub/actor",
+ }
+
+ links := []*webfingerLink{
+ {
+ Rel: "self",
+ Type: "application/activity+json",
+ Href: appURL.String() + "api/v1/activitypub/actor",
+ },
+ }
+
+ ctx.Resp.Header().Add("Access-Control-Allow-Origin", "*")
+ ctx.JSON(http.StatusOK, &webfingerJRD{
+ Subject: fmt.Sprintf("acct:%s@%s", "ghost", appURL.Host),
+ Aliases: aliases,
+ Links: links,
+ })
+ ctx.Resp.Header().Set("Content-Type", "application/jrd+json")
+
+ return
+ }
+
u, err = user_model.GetUserByName(ctx, parts[0])
+
case "mailto":
u, err = user_model.GetUserByEmail(ctx, resource.Opaque)
if u != nil && u.KeepEmailPrivate {
@@ -153,7 +179,7 @@ func WebfingerQuery(ctx *context.Context) {
},
{
Rel: "http://openid.net/specs/connect/1.0/issuer",
- Href: appURL.String(),
+ Href: strings.TrimSuffix(appURL.String(), "/"),
},
}
diff --git a/services/actions/TestServiceActions_startTask/action_schedule.yml b/services/actions/TestServiceActions_startTask/action_schedule.yml
new file mode 100644
index 0000000000..d0e7234475
--- /dev/null
+++ b/services/actions/TestServiceActions_startTask/action_schedule.yml
@@ -0,0 +1,41 @@
+# A corrupted cron spec with a valid schedule workflow
+-
+ id: 1
+ title: schedule_title1
+ specs:
+ - '* * * * *'
+ repo_id: 4
+ owner_id: 2
+ workflow_id: 'workflow1.yml'
+ trigger_user_id: 2
+ ref: main
+ commit_sha: shashasha
+ event: "schedule"
+ event_payload: "fakepayload"
+ content: |
+ jobs:
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: true
+
+# A valid cron spec with a corrupted schedule workflow
+-
+ id: 2
+ title: schedule_title2
+ specs:
+ - '* * * * *'
+ repo_id: 4
+ owner_id: 2
+ workflow_id: 'workflow2.yml'
+ trigger_user_id: 2
+ ref: main
+ commit_sha: shashasha
+ event: "schedule"
+ event_payload: "fakepayload"
+ content: |
+ jobs:
+ job2: { invalid yaml
+ runs-on: ubuntu-latest
+ steps:
+ - run: true
diff --git a/services/actions/TestServiceActions_startTask/action_schedule_spec.yml b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml
new file mode 100644
index 0000000000..7bcc78f010
--- /dev/null
+++ b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml
@@ -0,0 +1,15 @@
+# A corrupted cron spec with a valid schedule workflow
+-
+ id: 1
+ repo_id: 4
+ schedule_id: 1
+ next: 1
+ spec: 'corrupted * *'
+
+# A valid cron spec with a corrupted schedule workflow
+-
+ id: 2
+ repo_id: 4
+ schedule_id: 2
+ next: 1
+ spec: '* * * * *'
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 f146c22372..c36dda55b2 100644
--- a/services/actions/clear_tasks.go
+++ b/services/actions/clear_tasks.go
@@ -8,12 +8,12 @@ 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
@@ -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..d588c0f178 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -5,20 +5,21 @@ 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"
+ "code.forgejo.org/forgejo/runner/v9/act/jobparser"
)
// CreateCommitStatus creates a commit status for the given job.
@@ -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:
@@ -79,7 +80,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
repo := run.Repo
// TODO: store workflow name as a field in ActionRun to avoid parsing
runName := path.Base(run.WorkflowID)
- if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 {
+ if wfs, err := jobparser.Parse(job.WorkflowPayload, false); err == nil && len(wfs) > 0 {
runName = wfs[0].Name
}
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
diff --git a/services/actions/context.go b/services/actions/context.go
index be1c85522b..bf187c56bf 100644
--- a/services/actions/context.go
+++ b/services/actions/context.go
@@ -7,13 +7,13 @@ import (
"context"
"fmt"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
+ 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
diff --git a/services/actions/context_test.go b/services/actions/context_test.go
index 4cd8825870..c96094ade8 100644
--- a/services/actions/context_test.go
+++ b/services/actions/context_test.go
@@ -6,8 +6,8 @@ package actions
import (
"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"
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 76bee6f153..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 {
diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go
index 1f859fcf70..03428736b8 100644
--- a/services/actions/job_emitter.go
+++ b/services/actions/job_emitter.go
@@ -8,12 +8,12 @@ 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"
+ "code.forgejo.org/forgejo/runner/v9/act/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)
@@ -142,7 +142,7 @@ func (r *jobStatusResolver) resolve() map[int64]actions_model.Status {
} else {
// Check if the job has an "if" condition
hasIf := false
- if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload); len(wfJobs) == 1 {
+ if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload, false); len(wfJobs) == 1 {
_, wfJob := wfJobs[0].Job()
hasIf = len(wfJob.If.Value) > 0
}
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/job_parser.go b/services/actions/job_parser.go
new file mode 100644
index 0000000000..294f1c4a0e
--- /dev/null
+++ b/services/actions/job_parser.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "fmt"
+
+ "code.forgejo.org/forgejo/runner/v9/act/jobparser"
+)
+
+func jobParser(workflow []byte, options ...jobparser.ParseOption) ([]*jobparser.SingleWorkflow, error) {
+ singleWorkflows, err := jobparser.Parse(workflow, false, options...)
+ if err != nil {
+ return nil, err
+ }
+ nameToSingleWorkflows := make(map[string][]*jobparser.SingleWorkflow, len(singleWorkflows))
+ duplicates := make(map[string]int, len(singleWorkflows))
+ for _, singleWorkflow := range singleWorkflows {
+ id, job := singleWorkflow.Job()
+ nameToSingleWorkflows[job.Name] = append(nameToSingleWorkflows[job.Name], singleWorkflow)
+ if len(nameToSingleWorkflows[job.Name]) > 1 {
+ duplicates[job.Name]++
+ job.Name = fmt.Sprintf("%s-%d", job.Name, duplicates[job.Name])
+ if err := singleWorkflow.SetJob(id, job); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return singleWorkflows, nil
+}
diff --git a/services/actions/job_parser_test.go b/services/actions/job_parser_test.go
new file mode 100644
index 0000000000..9c1361d74e
--- /dev/null
+++ b/services/actions/job_parser_test.go
@@ -0,0 +1,212 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestServiceActions_jobParser(t *testing.T) {
+ for _, testCase := range []struct {
+ name string
+ workflow string
+ singleWorkflows []string
+ }{
+ {
+ name: "OneJobNoDuplicate",
+ workflow: `
+jobs:
+ job1:
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ singleWorkflows: []string{
+ `jobs:
+ job1:
+ name: job1
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ },
+ },
+ {
+ name: "MatrixTwoJobsWithSameJobName",
+ workflow: `
+name: test
+jobs:
+ job1:
+ name: shadowdefaultmatrixgeneratednames
+ strategy:
+ matrix:
+ version: [1.17, 1.19]
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ singleWorkflows: []string{
+ `name: test
+jobs:
+ job1:
+ name: shadowdefaultmatrixgeneratednames
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.17
+`,
+ `name: test
+jobs:
+ job1:
+ name: shadowdefaultmatrixgeneratednames-1
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.19
+`,
+ },
+ },
+ {
+ name: "MatrixTwoJobsWithMatrixGeneratedNames",
+ workflow: `
+name: test
+jobs:
+ job1:
+ strategy:
+ matrix:
+ version: [1.17, 1.19]
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ singleWorkflows: []string{
+ `name: test
+jobs:
+ job1:
+ name: job1 (1.17)
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.17
+`,
+ `name: test
+jobs:
+ job1:
+ name: job1 (1.19)
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.19
+`,
+ },
+ },
+ {
+ name: "MatrixTwoJobsWithDistinctInterpolatedNames",
+ workflow: `
+name: test
+jobs:
+ job1:
+ name: myname-${{ matrix.version }}
+ strategy:
+ matrix:
+ version: [1.17, 1.19]
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ singleWorkflows: []string{
+ `name: test
+jobs:
+ job1:
+ name: myname-1.17
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.17
+`,
+ `name: test
+jobs:
+ job1:
+ name: myname-1.19
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.19
+`,
+ },
+ },
+ {
+ name: "MatrixTwoJobsWithIdenticalInterpolatedNames",
+ workflow: `
+name: test
+jobs:
+ job1:
+ name: myname-${{ matrix.typo }}
+ strategy:
+ matrix:
+ version: [1.17, 1.19]
+ runs-on: docker
+ steps:
+ - run: echo OK
+`,
+ singleWorkflows: []string{
+ `name: test
+jobs:
+ job1:
+ name: myname-
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.17
+`,
+ `name: test
+jobs:
+ job1:
+ name: myname--1
+ runs-on: docker
+ steps:
+ - run: echo OK
+ strategy:
+ matrix:
+ version:
+ - 1.19
+`,
+ },
+ },
+ } {
+ t.Run(testCase.name, func(t *testing.T) {
+ sw, err := jobParser([]byte(testCase.workflow))
+ require.NoError(t, err)
+ for i, sw := range sw {
+ actual, err := sw.Marshal()
+ require.NoError(t, err)
+ assert.Equal(t, testCase.singleWorkflows[i], string(actual))
+ }
+ })
+ }
+}
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..a5bac730be 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)
+ 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..e1df5776fb 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -11,27 +11,27 @@ 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"
+ "code.forgejo.org/forgejo/runner/v9/act/jobparser"
+ "code.forgejo.org/forgejo/runner/v9/act/model"
)
type methodCtx struct{}
@@ -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), false); 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)
@@ -364,16 +372,19 @@ func handleWorkflows(
continue
}
- jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars))
+ jobs, err := jobParser(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)
}
}
@@ -526,7 +537,7 @@ func handleSchedules(
crons := make([]*actions_model.ActionSchedule, 0, len(detectedWorkflows))
for _, dwf := range detectedWorkflows {
// Check cron job condition. Only working in default branch
- workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content))
+ workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content), false)
if err != nil {
log.Error("ReadWorkflow: %v", err)
continue
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..1e90ad4b3f 100644
--- a/services/actions/schedule_tasks.go
+++ b/services/actions/schedule_tasks.go
@@ -4,19 +4,24 @@
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"
+ "code.forgejo.org/forgejo/runner/v9/act/jobparser"
+ act_model "code.forgejo.org/forgejo/runner/v9/act/model"
+ "github.com/robfig/cron/v3"
+ "xorm.io/builder"
)
// StartScheduleTasks start the task
@@ -55,7 +60,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,
@@ -79,20 +84,33 @@ func startTasks(ctx context.Context) error {
}
return fmt.Errorf("GetUnit: %w", err)
}
- if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) {
+ actionConfig := cfg.ActionsConfig()
+ if actionConfig.IsWorkflowDisabled(row.Schedule.WorkflowID) {
continue
}
- if err := CreateScheduleTask(ctx, row.Schedule); err != nil {
- log.Error("CreateScheduleTask: %v", err)
- return err
+ createAndSchedule := func(row *actions_model.ActionScheduleSpec) (cron.Schedule, error) {
+ if err := CreateScheduleTask(ctx, row.Schedule); err != nil {
+ return nil, fmt.Errorf("CreateScheduleTask: %v", err)
+ }
+
+ // Parse the spec
+ schedule, err := row.Parse()
+ if err != nil {
+ return nil, fmt.Errorf("Parse(Spec=%v): %v", row.Spec, err)
+ }
+ return schedule, nil
}
- // Parse the spec
- schedule, err := row.Parse()
+ schedule, err := createAndSchedule(row)
if err != nil {
- log.Error("Parse: %v", err)
- return err
+ log.Error("RepoID=%v WorkflowID=%v: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err)
+ actionConfig.DisableWorkflow(row.Schedule.WorkflowID)
+ if err := repo_model.UpdateRepoUnit(ctx, cfg); err != nil {
+ log.Error("RepoID=%v WorkflowID=%v: CreateScheduleTask: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err)
+ return err
+ }
+ continue
}
// Update the spec's next run time and previous run time
@@ -138,8 +156,18 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
return err
}
+ workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content), false)
+ 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))
+ workflows, err := jobParser(cron.Content, jobparser.WithVars(vars))
if err != nil {
return err
}
@@ -152,3 +180,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..31ed5ec813
--- /dev/null
+++ b/services/actions/schedule_tasks_test.go
@@ -0,0 +1,175 @@
+// 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"
+ "forgejo.org/models/db"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unit"
+ "forgejo.org/models/unittest"
+ webhook_module "forgejo.org/modules/webhook"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestServiceActions_startTask(t *testing.T) {
+ defer unittest.OverrideFixtures("services/actions/TestServiceActions_startTask")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ // Load fixtures that are corrupted and create one valid scheduled workflow
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+
+ workflowID := "some.yml"
+ schedules := []*actions_model.ActionSchedule{
+ {
+ Title: "scheduletitle1",
+ RepoID: repo.ID,
+ OwnerID: repo.OwnerID,
+ WorkflowID: workflowID,
+ TriggerUserID: repo.OwnerID,
+ Ref: "branch",
+ CommitSHA: "fakeSHA",
+ Event: webhook_module.HookEventSchedule,
+ EventPayload: "fakepayload",
+ Specs: []string{"* * * * *"},
+ Content: []byte(
+ `
+jobs:
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: true
+`),
+ },
+ }
+
+ require.Equal(t, 2, unittest.GetCount(t, actions_model.ActionScheduleSpec{}))
+ require.NoError(t, actions_model.CreateScheduleTask(t.Context(), schedules))
+ require.Equal(t, 3, unittest.GetCount(t, actions_model.ActionScheduleSpec{}))
+ _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE `action_schedule_spec` SET next = 1")
+ require.NoError(t, err)
+
+ // After running startTasks an ActionRun row is created for the valid scheduled workflow
+ require.Empty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID}))
+ require.NoError(t, startTasks(t.Context()))
+ require.NotEmpty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID}))
+
+ // The invalid workflows loaded from the fixtures are disabled
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ actionUnit, err := repo.GetUnit(t.Context(), unit.TypeActions)
+ require.NoError(t, err)
+ actionConfig := actionUnit.ActionsConfig()
+ assert.True(t, actionConfig.IsWorkflowDisabled("workflow2.yml"))
+ assert.True(t, actionConfig.IsWorkflowDisabled("workflow1.yml"))
+ assert.False(t, actionConfig.IsWorkflowDisabled("some.yml"))
+}
+
+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
index bc54ade347..bb319c7d05 100644
--- a/services/actions/task.go
+++ b/services/actions/task.go
@@ -5,14 +5,18 @@ package actions
import (
"context"
+ "errors"
"fmt"
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
+ 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) {
@@ -105,3 +109,144 @@ func findTaskNeeds(ctx context.Context, taskJob *actions_model.ActionRunJob) (ma
}
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..a9f42105a3 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) {
@@ -87,7 +87,7 @@ func GetVariable(ctx context.Context, opts actions_model.FindVariablesOpts) (*ac
// https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables
// https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets
var (
- forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI")
+ forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI$")
)
func envNameCIRegexMatch(name string) error {
diff --git a/services/actions/variables_test.go b/services/actions/variables_test.go
new file mode 100644
index 0000000000..f69bc674e1
--- /dev/null
+++ b/services/actions/variables_test.go
@@ -0,0 +1,17 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package actions
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestServicesAction_envNameCIRegexMatch(t *testing.T) {
+ require.ErrorContains(t, envNameCIRegexMatch("ci"), "cannot be ci")
+ require.ErrorContains(t, envNameCIRegexMatch("CI"), "cannot be ci")
+ assert.NoError(t, envNameCIRegexMatch("CI_SOMETHING"))
+}
diff --git a/services/actions/workflows.go b/services/actions/workflows.go
index e3e342264d..1bc46bfadc 100644
--- a/services/actions/workflows.go
+++ b/services/actions/workflows.go
@@ -10,22 +10,22 @@ 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/util"
- "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"
+ "code.forgejo.org/forgejo/runner/v9/act/jobparser"
+ act_model "code.forgejo.org/forgejo/runner/v9/act/model"
)
type InputRequiredErr struct {
@@ -56,7 +56,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
return nil, nil, err
}
- wf, err := act_model.ReadWorkflow(bytes.NewReader(content))
+ wf, err := act_model.ReadWorkflow(bytes.NewReader(content), false)
if err != nil {
return nil, nil, err
}
@@ -111,6 +111,11 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
return nil, nil, err
}
+ notifications, err := wf.Notifications()
+ if err != nil {
+ return nil, nil, err
+ }
+
run := &actions_model.ActionRun{
Title: title,
RepoID: repo.ID,
@@ -125,6 +130,7 @@ 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)
@@ -132,7 +138,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
return nil, nil, err
}
- jobs, err := jobparser.Parse(content, jobparser.WithVars(vars))
+ jobs, err := jobParser(content, jobparser.WithVars(vars))
if err != nil {
return nil, nil, err
}
diff --git a/services/agit/agit.go b/services/agit/agit.go
index a18f9ef728..8ef641629a 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
@@ -221,7 +221,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
}
// Validate pull request.
- pull_service.ValidatePullRequest(ctx, pr, oldCommitID, opts.NewCommitIDs[i], pusher)
+ pull_service.ValidatePullRequest(ctx, pr, opts.NewCommitIDs[i], oldCommitID, pusher)
// TODO: call `InvalidateCodeComments`
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 83a36bef23..e776ccbbed 100644
--- a/services/auth/httpsign.go
+++ b/services/auth/httpsign.go
@@ -11,11 +11,11 @@ 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/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
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..fa13c20a7f 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -11,15 +11,16 @@ 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/util"
+ "forgejo.org/modules/web/middleware"
+ "forgejo.org/services/actions"
+ "forgejo.org/services/auth/source/oauth2"
)
// Ensure the struct implements the interface.
@@ -137,7 +138,7 @@ func parseToken(req *http.Request) (string, bool) {
// check header token
if auHead := req.Header.Get("Authorization"); auHead != "" {
auths := strings.Fields(auHead)
- if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
+ if len(auths) == 2 && (util.ASCIIEqualFold(auths[0], "token") || util.ASCIIEqualFold(auths[0], "bearer")) {
return auths[1], true
}
}
diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go
index 90e2fe4517..3fce7df50b 100644
--- a/services/auth/oauth2_test.go
+++ b/services/auth/oauth2_test.go
@@ -4,12 +4,13 @@
package auth
import (
+ "net/http"
"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"
@@ -52,3 +53,30 @@ func TestCheckTaskIsRunning(t *testing.T) {
})
}
}
+
+func TestParseToken(t *testing.T) {
+ cases := map[string]struct {
+ Header string
+ ExpectedToken string
+ Expected bool
+ }{
+ "Token Uppercase": {Header: "Token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
+ "Token Lowercase": {Header: "token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
+ "Token Unicode": {Header: "to\u212Aen 1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false},
+ "Bearer Uppercase": {Header: "Bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
+ "Bearer Lowercase": {Header: "bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
+ "Missing type": {Header: "1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false},
+ "Three Parts": {Header: "abc 1234567890 test", ExpectedToken: "", Expected: false},
+ }
+
+ for name := range cases {
+ c := cases[name]
+ t.Run(name, func(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/", nil)
+ req.Header.Add("Authorization", c.Header)
+ ActualToken, ActualSuccess := parseToken(req)
+ assert.Equal(t, c.ExpectedToken, ActualToken)
+ assert.Equal(t, c.Expected, ActualSuccess)
+ })
+ }
+}
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 7c69da8f94..495b3d387e 100644
--- a/services/auth/signin.go
+++ b/services/auth/signin.go
@@ -7,17 +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
+ _ "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 63318b84ef..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
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 f606581271..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"
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 3f8616c6ff..a3126cf353 100644
--- a/services/auth/source/oauth2/source.go
+++ b/services/auth/source/oauth2/source.go
@@ -6,8 +6,8 @@ package oauth2
import (
"strings"
- "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/json"
+ "forgejo.org/models/auth"
+ "forgejo.org/modules/json"
)
// Source holds configuration for the OAuth2 login source.
@@ -29,6 +29,7 @@ type Source struct {
GroupTeamMapRemoval bool
RestrictedGroup string
SkipLocalTwoFA bool `json:",omitempty"`
+ AllowUsernameChange bool
// reference to the authSource
authSource *auth.Source
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/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..0cdc113379 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
}
@@ -161,7 +162,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
return
}
case issues_model.PullRequestFlowAGit:
- headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName())
+ headBranchExist := baseGitRepo.IsReferenceExist(pr.GetGitRefName())
if !headBranchExist {
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch)
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 871a2f012d..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"
)
@@ -186,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 = ""
}
}
@@ -285,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") {
@@ -334,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
}
@@ -365,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..68074964c8 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"
@@ -41,7 +41,7 @@ type Render interface {
type Context struct {
*Base
- TemplateContext TemplateContext
+ TemplateContext *templates.Context
Render Render
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
@@ -64,8 +64,6 @@ type Context struct {
Package *Package
}
-type TemplateContext map[string]any
-
func init() {
web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
return req.Context().Value(WebContextKey).(*Context)
@@ -98,10 +96,11 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
return ctx
}
-func NewTemplateContextForWeb(ctx *Context) TemplateContext {
- tmplCtx := NewTemplateContext(ctx)
- tmplCtx["Locale"] = ctx.Base.Locale
- tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
+func NewTemplateContextForWeb(ctx *Context) *templates.Context {
+ tmplCtx := templates.NewContext(ctx)
+ tmplCtx.Locale = ctx.Locale
+ tmplCtx.AvatarUtils = templates.NewAvatarUtils(ctx)
+ tmplCtx.Data = ctx.Data
return tmplCtx
}
@@ -121,6 +120,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 +162,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 +219,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..386bdd2652 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,14 @@ 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"
+ "forgejo.org/models/auth"
+ 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 +68,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) {
@@ -125,6 +130,21 @@ func (ctx *Context) RenderWithErr(msg any, tpl base.TplName, form any) {
ctx.HTML(http.StatusOK, tpl)
}
+// validateTwoFactorRequirement sets ctx-data to hide/show ui-elements depending on the GlobalTwoFactorRequirement
+func (ctx *Context) validateTwoFactorRequirement() {
+ if ctx.Doer == nil || !ctx.Doer.MustHaveTwoFactor() {
+ return
+ }
+
+ hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, ctx.Doer.ID)
+ if err != nil {
+ log.ErrorWithSkip(2, "Error getting 2fa: %s", err)
+ // fallthrough to set the variables
+ }
+ ctx.Data["MustEnableTwoFactor"] = !hasTwoFactor
+ ctx.Data["HideNavbarLinks"] = !hasTwoFactor
+}
+
// NotFound displays a 404 (Not Found) page and prints the given error, if any.
func (ctx *Context) NotFound(logMsg string, logErr error) {
ctx.notFoundInternal(logMsg, logErr)
@@ -152,9 +172,10 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
return
}
+ ctx.validateTwoFactorRequirement()
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 +198,7 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
}
}
- ctx.Data["Title"] = "Internal Server Error"
+ ctx.validateTwoFactorRequirement()
ctx.HTML(http.StatusInternalServerError, tplStatus500)
}
diff --git a/services/context/context_template.go b/services/context/context_template.go
deleted file mode 100644
index 7878d409ca..0000000000
--- a/services/context/context_template.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "time"
-)
-
-var _ context.Context = TemplateContext(nil)
-
-func NewTemplateContext(ctx context.Context) TemplateContext {
- return TemplateContext{"_ctx": ctx}
-}
-
-func (c TemplateContext) parentContext() context.Context {
- return c["_ctx"].(context.Context)
-}
-
-func (c TemplateContext) Deadline() (deadline time.Time, ok bool) {
- return c.parentContext().Deadline()
-}
-
-func (c TemplateContext) Done() <-chan struct{} {
- return c.parentContext().Done()
-}
-
-func (c TemplateContext) Err() error {
- return c.parentContext().Err()
-}
-
-func (c TemplateContext) Value(key any) any {
- return c.parentContext().Value(key)
-}
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..50ffa8eb7c 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
@@ -97,7 +97,7 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A
return perm.AccessModeNone, nil
}
- if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
+ if doer != nil && !doer.IsGhost() && !doer.IsAccessAllowed(ctx) {
return perm.AccessModeNone, nil
}
@@ -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 ff03844c03..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 {
@@ -641,7 +644,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
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)
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/activitypub_person.go b/services/convert/activitypub_person.go
new file mode 100644
index 0000000000..2c05f8c1c0
--- /dev/null
+++ b/services/convert/activitypub_person.go
@@ -0,0 +1,62 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package convert
+
+import (
+ "context"
+
+ "forgejo.org/models/activities"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+func ToActivityPubPersonFeedItem(item *activities.FederatedUserActivity) ap.Note {
+ return ap.Note{
+ AttributedTo: ap.IRI(item.ActorURI),
+ Content: ap.NaturalLanguageValues{{Value: ap.Content(item.NoteContent), Ref: ap.NilLangRef}},
+ ID: ap.IRI(item.NoteURL),
+ URL: ap.IRI(item.OriginalNote),
+ }
+}
+
+func ToActivityPubPerson(ctx context.Context, user *user_model.User) (*ap.Person, error) {
+ link := user.APActorID()
+ person := ap.PersonNew(ap.IRI(link))
+
+ person.Name = ap.NaturalLanguageValuesNew()
+ err := person.Name.Set("en", ap.Content(user.FullName))
+ if err != nil {
+ return nil, err
+ }
+
+ person.PreferredUsername = ap.NaturalLanguageValuesNew()
+ err = person.PreferredUsername.Set("en", ap.Content(user.Name))
+ if err != nil {
+ return nil, err
+ }
+
+ person.URL = ap.IRI(user.HTMLURL())
+
+ person.Icon = ap.Image{
+ Type: ap.ImageType,
+ MediaType: "image/png",
+ URL: ap.IRI(user.AvatarLink(ctx)),
+ }
+
+ person.Inbox = ap.IRI(link + "/inbox")
+ person.Outbox = ap.IRI(link + "/outbox")
+
+ person.PublicKey.ID = ap.IRI(link + "#main-key")
+ person.PublicKey.Owner = ap.IRI(link)
+
+ publicKeyPem, err := activitypub.GetPublicKey(ctx, user)
+ if err != nil {
+ return nil, err
+ }
+ person.PublicKey.PublicKeyPem = publicKeyPem
+
+ return person, nil
+}
diff --git a/services/convert/activitypub_user_action.go b/services/convert/activitypub_user_action.go
new file mode 100644
index 0000000000..9c62e6f25c
--- /dev/null
+++ b/services/convert/activitypub_user_action.go
@@ -0,0 +1,177 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package convert
+
+import (
+ "context"
+ "fmt"
+ "html"
+ "net/url"
+ "time"
+
+ activities_model "forgejo.org/models/activities"
+ issues_model "forgejo.org/models/issues"
+ fm "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/markup"
+ "forgejo.org/modules/markup/markdown"
+)
+
+func ActionToForgeUserActivity(ctx context.Context, action *activities_model.Action) (fm.ForgeUserActivity, error) {
+ render := func(format string, args ...any) string {
+ return fmt.Sprintf(`%s %s`, action.ActUser.HTMLURL(), action.GetActDisplayName(ctx), fmt.Sprintf(format, args...))
+ }
+ renderIssue := func(issue *issues_model.Issue) string {
+ return fmt.Sprintf(`%s#%d `,
+ issue.HTMLURL(),
+ action.GetRepoPath(ctx),
+ issue.Index,
+ )
+ }
+ renderRepo := func() string {
+ return fmt.Sprintf(`%s `, action.Repo.HTMLURL(), action.GetRepoPath(ctx))
+ }
+ renderBranch := func() string {
+ return fmt.Sprintf(`%s `, action.GetRefLink(ctx), action.GetBranch())
+ }
+ renderTag := func() string {
+ return fmt.Sprintf(`%s `, action.GetRefLink(ctx), action.GetTag())
+ }
+
+ makeUserActivity := func(format string, args ...any) (fm.ForgeUserActivity, error) {
+ return fm.NewForgeUserActivity(action.ActUser, action.ID, render(format, args...))
+ }
+
+ switch action.OpType {
+ case activities_model.ActionCreateRepo:
+ return makeUserActivity("created a new repository: %s", renderRepo())
+ case activities_model.ActionRenameRepo:
+ return makeUserActivity("renamed a repository: %s", renderRepo())
+ case activities_model.ActionStarRepo:
+ return makeUserActivity("starred a repository: %s", renderRepo())
+ case activities_model.ActionWatchRepo:
+ return makeUserActivity("started watching a repository: %s", renderRepo())
+ case activities_model.ActionCommitRepo:
+ type PushCommit struct {
+ Sha1 string
+ Message string
+ AuthorEmail string
+ AuthorName string
+ CommitterEmail string
+ CommitterName string
+ Timestamp time.Time
+ }
+ type PushCommits struct {
+ Commits []*PushCommit
+ HeadCommit *PushCommit
+ CompareURL string
+ Len int
+ }
+
+ commits := &PushCommits{}
+ if err := json.Unmarshal([]byte(action.GetContent()), commits); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ commitsHTML := ""
+ renderCommit := func(commit *PushCommit) string {
+ return fmt.Sprintf(`%s %s `,
+ fmt.Sprintf("%s/commit/%s", action.GetRepoAbsoluteLink(ctx), url.PathEscape(commit.Sha1)),
+ commit.Sha1,
+ html.EscapeString(commit.Message),
+ )
+ }
+ for _, commit := range commits.Commits {
+ commitsHTML += renderCommit(commit)
+ }
+ return makeUserActivity("pushed to %s at %s: ", renderBranch(), renderRepo(), commitsHTML)
+ case activities_model.ActionCreateIssue:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("opened issue %s", renderIssue(action.Issue))
+ case activities_model.ActionCreatePullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("opened pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionTransferRepo:
+ return makeUserActivity("transferred %s", renderRepo())
+ case activities_model.ActionPushTag:
+ return makeUserActivity("pushed %s at %s", renderTag(), renderRepo())
+ case activities_model.ActionCommentIssue:
+ renderedComment, err := markdown.RenderString(&markup.RenderContext{
+ Ctx: ctx,
+ }, action.Comment.Content)
+ if err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+
+ return makeUserActivity(`commented on %s: %s `,
+ action.GetCommentHTMLURL(ctx),
+ renderIssue(action.Comment.Issue),
+ renderedComment,
+ )
+ case activities_model.ActionMergePullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("merged pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionCloseIssue:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("closed issue %s", renderIssue(action.Issue))
+ case activities_model.ActionReopenIssue:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("reopened issue %s", renderIssue(action.Issue))
+ case activities_model.ActionClosePullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("closed pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionReopenPullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("reopened pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionDeleteTag:
+ return makeUserActivity("deleted tag %s at %s", action.GetTag(), renderRepo())
+ case activities_model.ActionDeleteBranch:
+ return makeUserActivity("deleted branch %s at %s", action.GetBranch(), renderRepo())
+ case activities_model.ActionApprovePullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("approved pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionRejectPullRequest:
+ if err := action.LoadIssue(ctx); err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+ return makeUserActivity("rejected pull request %s", renderIssue(action.Issue))
+ case activities_model.ActionCommentPull:
+ renderedComment, err := markdown.RenderString(&markup.RenderContext{
+ Ctx: ctx,
+ }, action.Comment.Content)
+ if err != nil {
+ return fm.ForgeUserActivity{}, err
+ }
+
+ return makeUserActivity(`commented on %s: %s `,
+ action.GetCommentHTMLURL(ctx),
+ renderIssue(action.Comment.Issue),
+ renderedComment,
+ )
+ case activities_model.ActionMirrorSyncPush:
+ case activities_model.ActionMirrorSyncCreate:
+ case activities_model.ActionMirrorSyncDelete:
+ case activities_model.ActionPublishRelease:
+ case activities_model.ActionPullReviewDismissed:
+ case activities_model.ActionPullRequestReadyForReview:
+ case activities_model.ActionAutoMergePullRequest:
+ }
+
+ return makeUserActivity("performed an unrecognised action: %s", action.OpType.String())
+}
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..6a691966b8 100644
--- a/services/convert/git_commit.go
+++ b/services/convert/git_commit.go
@@ -8,14 +8,13 @@ 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"
)
// ToCommitUser convert a git.Signature to an api.CommitUser
@@ -210,17 +209,15 @@ 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{
- AfterCommitID: commit.ID.String(),
- })
+ _, totalAdditions, totalDeletions, err := gitRepo.GetCommitShortStat(commit.ID.String())
if err != nil {
return nil, err
}
res.Stats = &api.CommitStats{
- Total: diff.TotalAddition + diff.TotalDeletion,
- Additions: diff.TotalAddition,
- Deletions: diff.TotalDeletion,
+ Total: totalAdditions + totalDeletions,
+ Additions: totalAdditions,
+ Deletions: totalDeletions,
}
}
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..ea8ad9b7ef 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"
@@ -24,10 +24,11 @@ func TestLabel_ToLabel(t *testing.T) {
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID})
assert.Equal(t, &api.Label{
- ID: label.ID,
- Name: label.Name,
- Color: "abcdef",
- URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID),
+ ID: label.ID,
+ Name: label.Name,
+ Color: "abcdef",
+ Description: label.Description,
+ URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID),
}, ToLabel(label, repo, nil))
}
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..5a815f3a5c 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
@@ -23,5 +23,6 @@ func ToPushMirror(ctx context.Context, pm *repo_model.PushMirror) (*api.PushMirr
Interval: pm.Interval.String(),
SyncOnCommit: pm.SyncOnCommit,
PublicKey: pm.GetPublicKey(),
+ BranchFilter: pm.BranchFilter,
}, nil
}
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/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 7b6775dfb4..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
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..70c5f5cc18 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.
@@ -38,6 +38,8 @@ func ToGitServiceType(value string) structs.GitServiceType {
return structs.GitBucketService
case "forgejo":
return structs.ForgejoService
+ case "pagure":
+ return structs.PagureService
default:
return structs.PlainGitService
}
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 23eb0dd291..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() {
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index e1ba5274e6..3006601366 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -7,17 +7,18 @@ 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"
+ moderation_service "forgejo.org/services/moderation"
+ repo_service "forgejo.org/services/repository"
+ archiver_service "forgejo.org/services/repository/archiver"
+ user_service "forgejo.org/services/user"
)
func registerDeleteInactiveUsers() {
@@ -225,6 +226,24 @@ func registerRebuildIssueIndexer() {
})
}
+func registerRemoveResolvedReports() {
+ type ReportConfig struct {
+ BaseConfig
+ ConfigKeepResolvedReportsFor time.Duration
+ }
+ RegisterTaskFatal("remove_resolved_reports", &ReportConfig{
+ BaseConfig: BaseConfig{
+ Enabled: false,
+ RunAtStart: false,
+ Schedule: "@every 24h",
+ },
+ ConfigKeepResolvedReportsFor: setting.Moderation.KeepResolvedReportsFor,
+ }, func(ctx context.Context, _ *user_model.User, config Config) error {
+ reportConfig := config.(*ReportConfig)
+ return moderation_service.RemoveResolvedReports(ctx, reportConfig.ConfigKeepResolvedReportsFor)
+ })
+}
+
func initExtendedTasks() {
registerDeleteInactiveUsers()
registerDeleteRepositoryArchives()
@@ -240,4 +259,7 @@ func initExtendedTasks() {
registerDeleteOldSystemNotices()
registerGCLFS()
registerRebuildIssueIndexer()
+ if setting.Moderation.Enabled {
+ registerRemoveResolvedReports()
+ }
}
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 61e571d1b6..0000000000
--- a/services/f3/driver/asset.go
+++ /dev/null
@@ -1,172 +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_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 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) 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))
- }
-
- 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 f3_id.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 166bfcd328..bd924930b5 100644
--- a/services/f3/driver/comment.go
+++ b/services/f3/driver/comment.go
@@ -8,10 +8,10 @@ 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"
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/forge.go b/services/f3/driver/forge.go
index c232882753..03acb41450 100644
--- a/services/f3/driver/forge.go
+++ b/services/f3/driver/forge.go
@@ -8,7 +8,7 @@ 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"
diff --git a/services/f3/driver/issue.go b/services/f3/driver/issue.go
index 7e10f3a9db..6308c4cc2d 100644
--- a/services/f3/driver/issue.go
+++ b/services/f3/driver/issue.go
@@ -8,13 +8,13 @@ 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"
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 509a69cf71..707ac2bab3 100644
--- a/services/f3/driver/label.go
+++ b/services/f3/driver/label.go
@@ -9,8 +9,8 @@ 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"
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 e57fee95a7..d10e6918ac 100644
--- a/services/f3/driver/milestone.go
+++ b/services/f3/driver/milestone.go
@@ -9,10 +9,10 @@ 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"
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 8e818a231a..af1eea4dda 100644
--- a/services/f3/driver/organization.go
+++ b/services/f3/driver/organization.go
@@ -8,9 +8,9 @@ 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"
diff --git a/services/f3/driver/organizations.go b/services/f3/driver/organizations.go
index adebdbbe95..eca6bfb9d4 100644
--- a/services/f3/driver/organizations.go
+++ b/services/f3/driver/organizations.go
@@ -8,9 +8,9 @@ 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"
diff --git a/services/f3/driver/project.go b/services/f3/driver/project.go
index 2400663426..5a3ec81e40 100644
--- a/services/f3/driver/project.go
+++ b/services/f3/driver/project.go
@@ -9,9 +9,9 @@ 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"
diff --git a/services/f3/driver/projects.go b/services/f3/driver/projects.go
index fb447f3f01..0c76854f43 100644
--- a/services/f3/driver/projects.go
+++ b/services/f3/driver/projects.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_id "code.forgejo.org/f3/gof3/v3/id"
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
diff --git a/services/f3/driver/pullrequest.go b/services/f3/driver/pullrequest.go
index b8cb06c4d5..664ee6b13b 100644
--- a/services/f3/driver/pullrequest.go
+++ b/services/f3/driver/pullrequest.go
@@ -9,13 +9,13 @@ 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"
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 4f12fa41db..b959206074 100644
--- a/services/f3/driver/reaction.go
+++ b/services/f3/driver/reaction.go
@@ -8,9 +8,9 @@ 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"
@@ -89,7 +89,7 @@ func (o *reaction) Patch(ctx context.Context) {
}
func (o *reaction) Put(ctx context.Context) f3_id.NodeID {
- o.Error("%v", o.forgejoReaction.User)
+ o.Trace("%v", o.forgejoReaction.User)
sess := db.GetEngine(ctx)
@@ -110,7 +110,7 @@ func (o *reaction) Put(ctx context.Context) f3_id.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)
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 86490e8b02..df38bd8bc0 100644
--- a/services/f3/driver/release.go
+++ b/services/f3/driver/release.go
@@ -9,12 +9,12 @@ 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"
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 118d5f2f2a..3cd9aa7f2e 100644
--- a/services/f3/driver/repository.go
+++ b/services/f3/driver/repository.go
@@ -7,7 +7,7 @@ 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"
@@ -72,7 +72,7 @@ func (o *repository) upsert(ctx context.Context) f3_id.NodeID {
return f3_id.NewNodeID(o.f.Name)
}
-func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination string, internalRefs []string)) {
+func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination, internalRef string)) {
o.f.FetchFunc = fetchFunc
}
@@ -93,10 +93,16 @@ func (o *repository) GetRepositoryPushURL() string {
return o.getURL()
}
-func (o *repository) GetRepositoryInternalRefs() []string {
- return []string{}
+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 d180ea96be..f4f5ff44b8 100644
--- a/services/f3/driver/review.go
+++ b/services/f3/driver/review.go
@@ -8,10 +8,10 @@ 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"
diff --git a/services/f3/driver/reviewcomment.go b/services/f3/driver/reviewcomment.go
index 7ba0e15802..22759b6df3 100644
--- a/services/f3/driver/reviewcomment.go
+++ b/services/f3/driver/reviewcomment.go
@@ -9,10 +9,10 @@ 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"
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/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 dc6ac437e6..2f5c6c64db 100644
--- a/services/f3/driver/tests/new.go
+++ b/services/f3/driver/tests/new.go
@@ -7,7 +7,7 @@ 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"
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 eeb387cf93..cc94aa35fa 100644
--- a/services/f3/driver/topic.go
+++ b/services/f3/driver/topic.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"
"code.forgejo.org/f3/gof3/v3/f3"
f3_id "code.forgejo.org/f3/gof3/v3/id"
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 2377d3794d..fe11b15f6e 100644
--- a/services/f3/driver/tree.go
+++ b/services/f3/driver/tree.go
@@ -8,7 +8,7 @@ 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"
@@ -49,10 +49,10 @@ func (o *treeDriver) Factory(ctx context.Context, kind f3_kind.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:
diff --git a/services/f3/driver/user.go b/services/f3/driver/user.go
index 0ba6cbb7c6..bf8bfaf9c9 100644
--- a/services/f3/driver/user.go
+++ b/services/f3/driver/user.go
@@ -9,9 +9,9 @@ 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"
diff --git a/services/f3/driver/users.go b/services/f3/driver/users.go
index 59b10fc51d..cb413ae05d 100644
--- a/services/f3/driver/users.go
+++ b/services/f3/driver/users.go
@@ -8,8 +8,8 @@ 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"
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/delivery_queue.go b/services/federation/delivery_queue.go
new file mode 100644
index 0000000000..f71467e9f0
--- /dev/null
+++ b/services/federation/delivery_queue.go
@@ -0,0 +1,76 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "fmt"
+ "io"
+
+ "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ "forgejo.org/modules/graceful"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/process"
+ "forgejo.org/modules/queue"
+)
+
+type deliveryQueueItem struct {
+ Doer *user.User
+ InboxURL string
+ Payload []byte
+ DeliveryCount int
+}
+
+var deliveryQueue *queue.WorkerPoolQueue[deliveryQueueItem]
+
+func initDeliveryQueue() error {
+ deliveryQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "activitypub_inbox_delivery", deliveryQueueHandler)
+ if deliveryQueue == nil {
+ return fmt.Errorf("unable to create activitypub_inbox_delivery queue")
+ }
+ go graceful.GetManager().RunWithCancel(deliveryQueue)
+
+ return nil
+}
+
+func deliveryQueueHandler(items ...deliveryQueueItem) (unhandled []deliveryQueueItem) {
+ for _, item := range items {
+ item.DeliveryCount++
+ err := deliverToInbox(item)
+ if err != nil && item.DeliveryCount < 10 {
+ unhandled = append(unhandled, item)
+ }
+ }
+ return unhandled
+}
+
+func deliverToInbox(item deliveryQueueItem) error {
+ ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(),
+ fmt.Sprintf("Delivering an Activity via user[%d] (%s), to %s", item.Doer.ID, item.Doer.Name, item.InboxURL))
+ defer finished()
+
+ clientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return err
+ }
+ apclient, err := clientFactory.WithKeys(ctx, item.Doer, item.Doer.APActorID()+"#main-key")
+ if err != nil {
+ return err
+ }
+
+ log.Debug("Delivering %s to %s", item.Payload, item.InboxURL)
+ res, err := apclient.Post(item.Payload, item.InboxURL)
+ if err != nil {
+ return err
+ }
+ if res.StatusCode >= 400 {
+ defer res.Body.Close()
+ body, _ := io.ReadAll(io.LimitReader(res.Body, 16*1024))
+
+ log.Warn("Delivering to %s failed: %d %s, %v times", item.InboxURL, res.StatusCode, string(body), item.DeliveryCount)
+ return fmt.Errorf("delivery failed")
+ }
+
+ return nil
+}
diff --git a/services/federation/error.go b/services/federation/error.go
new file mode 100644
index 0000000000..425035d0d5
--- /dev/null
+++ b/services/federation/error.go
@@ -0,0 +1,44 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "fmt"
+ "net/http"
+)
+
+type ErrNotAcceptable struct {
+ Message string
+}
+
+func NewErrNotAcceptablef(format string, a ...any) ErrNotAcceptable {
+ message := fmt.Sprintf(format, a...)
+ return ErrNotAcceptable{Message: message}
+}
+
+func (err ErrNotAcceptable) Error() string {
+ return fmt.Sprintf("NotAcceptable: %v", err.Message)
+}
+
+type ErrInternal struct {
+ Message string
+}
+
+func NewErrInternalf(format string, a ...any) ErrInternal {
+ message := fmt.Sprintf(format, a...)
+ return ErrInternal{Message: message}
+}
+
+func (err ErrInternal) Error() string {
+ return fmt.Sprintf("InternalServerError: %v", err.Message)
+}
+
+func HTTPStatus(err error) int {
+ switch err.(type) {
+ case ErrNotAcceptable:
+ return http.StatusNotAcceptable
+ default:
+ return http.StatusInternalServerError
+ }
+}
diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go
index 4c6f5ca0ca..ccdb9bbab0 100644
--- a/services/federation/federation_service.go
+++ b/services/federation/federation_service.go
@@ -1,151 +1,45 @@
-// 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"
+ "database/sql"
"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/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"
)
-// ProcessLikeActivity receives a ForgeLike activity and does the following:
-// Validation of the activity
-// Creation of a (remote) federationHost if not existing
-// Creation of a forgefed Person if not existing
-// Validation of incoming RepositoryID against Local RepositoryID
-// Star the repo if it wasn't already stared
-// Do some mitigation against out of order attacks
-func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int, string, error) {
- activity := form.(*fm.ForgeLike)
- if res, err := validation.IsValid(activity); !res {
- return http.StatusNotAcceptable, "Invalid activity", err
+func Init() error {
+ if !setting.Federation.Enabled {
+ return nil
}
- log.Info("Activity validated:%v", activity)
-
- // parse actorID (person)
- actorURI := activity.Actor.GetID().String()
- log.Info("actorURI was: %v", actorURI)
- federationHost, err := GetFederationHostForURI(ctx, actorURI)
- if err != nil {
- return http.StatusInternalServerError, "Wrong FederationHost", err
- }
- if !activity.IsNewer(federationHost.LatestActivity) {
- return http.StatusNotAcceptable, "Activity out of order.", fmt.Errorf("Activity already processed")
- }
- actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName))
- if err != nil {
- return http.StatusNotAcceptable, "Invalid PersonID", err
- }
- log.Info("Actor accepted:%v", actorID)
-
- // parse objectID (repository)
- objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType))
- if err != nil {
- return http.StatusNotAcceptable, "Invalid objectId", err
- }
- if objectID.ID != fmt.Sprint(repositoryID) {
- return http.StatusNotAcceptable, "Invalid objectId", err
- }
- log.Info("Object accepted:%v", objectID)
-
- // Check if user already exists
- user, _, err := user.FindFederatedUser(ctx, actorID.ID, federationHost.ID)
- if err != nil {
- return http.StatusInternalServerError, "Searching for user failed", err
- }
- if user != nil {
- log.Info("Found local federatedUser: %v", user)
- } else {
- user, _, err = CreateUserFromAP(ctx, actorID, federationHost.ID)
- if err != nil {
- return http.StatusInternalServerError, "Error creating federatedUser", err
- }
- log.Info("Created federatedUser from ap: %v", user)
- }
- log.Info("Got user:%v", user.Name)
-
- // execute the activity if the repo was not stared already
- alreadyStared := repo.IsStaring(ctx, user.ID, repositoryID)
- if !alreadyStared {
- err = repo.StarRepo(ctx, user.ID, repositoryID, true)
- if err != nil {
- return http.StatusNotAcceptable, "Error staring", err
- }
- }
- federationHost.LatestActivity = activity.StartTime
- err = forgefed.UpdateFederationHost(ctx, federationHost)
- if err != nil {
- return http.StatusNotAcceptable, "Error updating federatedHost", err
- }
-
- return 0, "", nil
+ return initDeliveryQueue()
}
-func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) {
- actionsUser := user.NewActionsUser()
- clientFactory, err := activitypub.GetClientFactory(ctx)
- if err != nil {
- return nil, err
- }
- client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.")
- 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)
- if err != nil {
- return nil, err
- }
- err = forgefed.CreateFederationHost(ctx, &result)
- if err != nil {
- return nil, err
- }
- return &result, nil
-}
-
-func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) {
- log.Info("Input was: %v", actorURI)
+func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) {
rawActorID, err := fm.NewActorID(actorURI)
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
}
if federationHost == nil {
- result, err := CreateFederationHostFromAP(ctx, rawActorID)
+ result, err := createFederationHostFromAP(ctx, rawActorID)
if err != nil {
return nil, err
}
@@ -154,19 +48,109 @@ func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.Fe
return federationHost, nil
}
-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()
+func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) {
+ user, federatedUser, federationHost, err := findFederatedUser(ctx, actorURI)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ personID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ if user != nil {
+ log.Trace("Local ActivityPub user found (actorURI: %#v, user: %#v)", actorURI, user)
+ } else {
+ log.Trace("Attempting to create new user and federatedUser for actorURI: %#v", actorURI)
+ user, federatedUser, err = createUserFromAP(ctx, personID, federationHost.ID)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ log.Trace("Created user %#v with federatedUser %#v from distant server", user, federatedUser)
+ }
+ log.Trace("Got user: %v", user.Name)
+
+ return user, federatedUser, federationHost, nil
+}
+
+func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) {
+ federationHost, err := FindOrCreateFederationHost(ctx, actorURI)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ user, federatedUser, err := user.FindFederatedUser(ctx, actorID.ID, federationHost.ID)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return user, federatedUser, federationHost, nil
+}
+
+func createFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) {
+ actionsUser := user.NewAPServerActor()
+
+ clientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID())
+ 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
+ }
+
+ // TODO: we should get key material here also to have it immediately
+ 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
+}
+
+func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) {
+ 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.KeyID())
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 +160,42 @@ 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)
+
+ log.Info("Fetched valid person from distant server: %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
+ }
+
+ pubKeyBytes, err := decodePublicKeyPem(person.PublicKey.PublicKeyPem)
+ if err != nil {
+ return nil, nil, err
+ }
+
newUser := user.User{
LowerName: strings.ToLower(name),
Name: name,
@@ -207,89 +207,37 @@ 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,
- }
- err = user.CreateFederatedUser(ctx, &newUser, &federatedUser)
- if err != nil {
- return nil, nil, err
- }
- log.Info("Created federatedUser:%q", federatedUser)
+ federatedUser := user.FederatedUser{
+ ExternalID: personID.ID,
+ FederationHostID: federationHostID,
+ InboxPath: inbox.Path,
+ NormalizedOriginalURL: personID.AsURI(),
+ KeyID: sql.NullString{
+ String: person.PublicKey.ID.String(),
+ Valid: true,
+ },
+ PublicKey: sql.Null[sql.RawBytes]{
+ V: pubKeyBytes,
+ Valid: true,
+ },
+ }
+
+ log.Info("Fetched person's %q federatedUser from distant server: %q", person, federatedUser)
return &newUser, &federatedUser, nil
}
-// Create or update a list of FollowingRepo structs
-func StoreFollowingRepoList(ctx context.Context, localRepoID int64, followingRepoList []string) (int, string, error) {
- followingRepos := make([]*repo.FollowingRepo, 0, len(followingRepoList))
- for _, uri := range followingRepoList {
- federationHost, err := GetFederationHostForURI(ctx, uri)
- if err != nil {
- return http.StatusInternalServerError, "Wrong FederationHost", err
- }
- followingRepoID, err := fm.NewRepositoryID(uri, string(federationHost.NodeInfo.SoftwareName))
- if err != nil {
- return http.StatusNotAcceptable, "Invalid federated repo", err
- }
- followingRepo, err := repo.NewFollowingRepo(localRepoID, followingRepoID.ID, federationHost.ID, uri)
- if err != nil {
- return http.StatusNotAcceptable, "Invalid federated repo", err
- }
- followingRepos = append(followingRepos, &followingRepo)
- }
-
- if err := repo.StoreFollowingRepos(ctx, localRepoID, followingRepos); err != nil {
- return 0, "", err
- }
-
- return 0, "", nil
-}
-
-func DeleteFollowingRepos(ctx context.Context, localRepoID int64) error {
- return repo.StoreFollowingRepos(ctx, localRepoID, []*repo.FollowingRepo{})
-}
-
-func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error {
- followingRepos, err := repo.FindFollowingReposByRepoID(ctx, repoID)
- log.Info("Federated Repos is: %v", followingRepos)
+func createUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) {
+ newUser, federatedUser, err := fetchUserFromAP(ctx, personID, federationHostID)
if err != nil {
- return err
+ return nil, nil, err
}
-
- likeActivityList := make([]fm.ForgeLike, 0)
- for _, followingRepo := range followingRepos {
- log.Info("Found following repo: %v", followingRepo)
- target := followingRepo.URI
- likeActivity, err := fm.NewForgeLike(doer.APActorID(), target, time.Now())
- if err != nil {
- return err
- }
- likeActivityList = append(likeActivityList, likeActivity)
- }
-
- apclientFactory, err := activitypub.GetClientFactory(ctx)
+ err = user.CreateFederatedUser(ctx, newUser, federatedUser)
if err != nil {
- return err
- }
- apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID())
- if err != nil {
- return err
- }
- for i, activity := range likeActivityList {
- activity.StartTime = activity.StartTime.Add(time.Duration(i) * time.Second)
- json, err := activity.MarshalJSON()
- if err != nil {
- return err
- }
-
- _, err = apclient.Post(json, fmt.Sprintf("%v/inbox/", activity.Object))
- if err != nil {
- log.Error("error %v while sending activity: %q", err, activity)
- }
+ return nil, nil, err
}
- return nil
+ log.Info("Created federatedUser: %q", federatedUser)
+ return newUser, federatedUser, nil
}
diff --git a/services/federation/person_inbox_accept.go b/services/federation/person_inbox_accept.go
new file mode 100644
index 0000000000..d0a840bd2d
--- /dev/null
+++ b/services/federation/person_inbox_accept.go
@@ -0,0 +1,22 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "net/http"
+
+ "forgejo.org/modules/log"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+func processPersonInboxAccept(activity *ap.Activity) (ServiceResult, error) {
+ if activity.Object.GetType() != ap.FollowType {
+ log.Error("Invalid object type for Accept activity: %v", activity.Object.GetType())
+ return ServiceResult{}, NewErrNotAcceptablef("invalid object type for Accept activity: %v", activity.Object.GetType())
+ }
+
+ // We currently do not do anything here, we just drop it.
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+}
diff --git a/services/federation/person_inbox_create.go b/services/federation/person_inbox_create.go
new file mode 100644
index 0000000000..2132c7ede1
--- /dev/null
+++ b/services/federation/person_inbox_create.go
@@ -0,0 +1,55 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "net/http"
+
+ "forgejo.org/models/activities"
+ "forgejo.org/models/user"
+ fm "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+func processPersonInboxCreate(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) {
+ createAct, err := fm.NewForgeUserActivityFromAp(*activity)
+ if err != nil {
+ log.Error("Invalid user activity: %v, %v", activity, err)
+ return ServiceResult{}, NewErrNotAcceptablef("Invalid user activity: %v", err)
+ }
+
+ actorURI := createAct.Actor.GetLink().String()
+ federatedBaseUser, _, _, err := findFederatedUser(ctx, actorURI)
+ if err != nil {
+ log.Error("Federated user not found (%s): %v", actorURI, err)
+ return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err)
+ }
+ if federatedBaseUser == nil {
+ log.Error("Federated user not found (%s): %v", actorURI, err)
+ return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err)
+ }
+
+ federatedUserActivity, err := activities.NewFederatedUserActivity(
+ user.ID,
+ federatedBaseUser.ID,
+ createAct.Actor.GetLink().String(),
+ createAct.Note.Content.String(),
+ createAct.Note.URL.GetID().String(),
+ *activity,
+ )
+ if err != nil {
+ log.Error("Error creating federatedUserActivity (%s): %v", actorURI, err)
+ return ServiceResult{}, NewErrNotAcceptablef("Error creating federatedUserActivity: %v", err)
+ }
+
+ if err := activities.CreateUserActivity(ctx, &federatedUserActivity); err != nil {
+ log.Error("Unable to record activity: %v", err)
+ return ServiceResult{}, NewErrNotAcceptablef("Unable to record activity: %v", err)
+ }
+
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+}
diff --git a/services/federation/person_inbox_follow.go b/services/federation/person_inbox_follow.go
new file mode 100644
index 0000000000..baa7934ad5
--- /dev/null
+++ b/services/federation/person_inbox_follow.go
@@ -0,0 +1,73 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "forgejo.org/models/user"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+
+ ap "github.com/go-ap/activitypub"
+ "github.com/go-ap/jsonld"
+)
+
+func processPersonFollow(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) {
+ follow, err := forgefed.NewForgeFollowFromAp(*activity)
+ if err != nil {
+ log.Error("Invalid follow activity: %s", err)
+ return ServiceResult{}, NewErrNotAcceptablef("Invalid follow activity: %v", err)
+ }
+
+ actorURI := follow.Actor.GetLink().String()
+ _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI)
+ if err != nil {
+ log.Error("Error finding or creating federated user (%s): %v", actorURI, err)
+ return ServiceResult{}, NewErrNotAcceptablef("Federated user not found: %v", err)
+ }
+
+ following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser)
+ if err != nil {
+ log.Error("forgefed.IsFollowing: %v", err)
+ return ServiceResult{}, NewErrNotAcceptablef("forgefed.IsFollowing: %v", err)
+ }
+ if following {
+ // If the user is already following, we're good, nothing to do.
+ log.Trace("Local user[%d] is already following federated user[%d]", ctxUser.ID, federatedUser.ID)
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+ }
+
+ follower, err := user.AddFollower(ctx, ctxUser, federatedUser)
+ if err != nil {
+ log.Error("Unable to add follower: %v", err)
+ return ServiceResult{}, NewErrNotAcceptablef("Unable to add follower: %v", err)
+ }
+
+ accept := ap.AcceptNew(ap.IRI(fmt.Sprintf(
+ "%s#accepts/follow/%d", ctxUser.APActorID(), follower.ID,
+ )), follow)
+ accept.Actor = ap.IRI(ctxUser.APActorID())
+ payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)).Marshal(accept)
+ if err != nil {
+ log.Error("Unable to Marshal JSON: %v", err)
+ return ServiceResult{}, NewErrInternalf("MarshalJSON: %v", err)
+ }
+
+ hostURL := federationHost.AsURL()
+ if err := deliveryQueue.Push(deliveryQueueItem{
+ InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(),
+ Doer: ctxUser,
+ Payload: payload,
+ }); err != nil {
+ log.Error("Unable to push to pending queue: %v", err)
+ return ServiceResult{}, NewErrInternalf("Unable to push to pending queue: %v", err)
+ }
+
+ // Respond back with an accept
+ result := NewServiceResultWithBytes(http.StatusAccepted, []byte(`{"status":"Accepted"}`))
+ return result, nil
+}
diff --git a/services/federation/person_inbox_undo.go b/services/federation/person_inbox_undo.go
new file mode 100644
index 0000000000..4379cf242a
--- /dev/null
+++ b/services/federation/person_inbox_undo.go
@@ -0,0 +1,47 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "net/http"
+
+ "forgejo.org/models/user"
+ "forgejo.org/modules/log"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+func processPersonInboxUndo(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) {
+ if activity.Object.GetType() != ap.FollowType {
+ log.Error("Invalid object type for Undo activity: %v", activity.Object.GetType())
+ return ServiceResult{}, NewErrNotAcceptablef("Invalid object type for Undo activity: %v", activity.Object.GetType())
+ }
+
+ actorURI := activity.Actor.GetLink().String()
+ _, federatedUser, _, err := findFederatedUser(ctx, actorURI)
+ if err != nil {
+ log.Error("User not found: %v", err)
+ return ServiceResult{}, NewErrInternalf("User not found: %v", err)
+ }
+
+ if federatedUser != nil {
+ following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser)
+ if err != nil {
+ log.Error("forgefed.IsFollowing: %v", err)
+ return ServiceResult{}, NewErrInternalf("forgefed.IsFollowing: %v", err)
+ }
+ if !following {
+ // The local user is not following the federated one, nothing to do.
+ log.Trace("Local user[%d] is not following federated user[%d]", ctxUser.ID, federatedUser.ID)
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+ }
+ if err := user.RemoveFollower(ctx, ctxUser, federatedUser); err != nil {
+ log.Error("Unable to remove follower", err)
+ return ServiceResult{}, NewErrInternalf("Unable to remove follower: %v", err)
+ }
+ }
+
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+}
diff --git a/services/federation/person_service.go b/services/federation/person_service.go
new file mode 100644
index 0000000000..d6482d013c
--- /dev/null
+++ b/services/federation/person_service.go
@@ -0,0 +1,60 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "net/http"
+
+ "forgejo.org/models/user"
+ "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ context_service "forgejo.org/services/context"
+
+ ap "github.com/go-ap/activitypub"
+ "github.com/go-ap/jsonld"
+)
+
+func ProcessPersonInbox(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) {
+ switch activity.Type {
+ case ap.CreateType:
+ return processPersonInboxCreate(ctx, user, activity)
+ case ap.FollowType:
+ return processPersonFollow(ctx, user, activity)
+ case ap.UndoType:
+ return processPersonInboxUndo(ctx, user, activity)
+ case ap.AcceptType:
+ return processPersonInboxAccept(activity)
+ }
+
+ log.Error("Unsupported PersonInbox activity: %v", activity.Type)
+ return ServiceResult{}, NewErrNotAcceptablef("unsupported activity: %v", activity.Type)
+}
+
+func FollowRemoteActor(ctx *context_service.APIContext, localUser *user.User, actorURI string) error {
+ _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI)
+ if err != nil {
+ log.Error("Federated user not found (%s): %v", actorURI, err)
+ ctx.Error(http.StatusNotAcceptable, "Federated user not found", err)
+ return err
+ }
+
+ followReq, err := forgefed.NewForgeFollow(localUser.APActorID(), actorURI)
+ if err != nil {
+ return err
+ }
+
+ payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)).
+ Marshal(followReq)
+ if err != nil {
+ return err
+ }
+
+ hostURL := federationHost.AsURL()
+ return deliveryQueue.Push(deliveryQueueItem{
+ InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(),
+ Doer: localUser,
+ Payload: payload,
+ })
+}
diff --git a/services/federation/repository_inbox_like.go b/services/federation/repository_inbox_like.go
new file mode 100644
index 0000000000..478a12d92c
--- /dev/null
+++ b/services/federation/repository_inbox_like.go
@@ -0,0 +1,147 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "forgejo.org/models/forgefed"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ fm "forgejo.org/modules/forgefed"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/validation"
+ context_service "forgejo.org/services/context"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+// ProcessLikeActivity receives a ForgeLike activity and does the following:
+// Validation of the activity
+// Creation of a (remote) federationHost if not existing
+// Creation of a forgefed Person if not existing
+// Validation of incoming RepositoryID against Local RepositoryID
+// Star the repo if it wasn't already stared
+// Do some mitigation against out of order attacks
+func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) {
+ constructorLikeActivity, _ := fm.NewForgeLike(activity.Actor.GetLink().String(), activity.Object.GetLink().String(), activity.StartTime)
+ if res, err := validation.IsValid(constructorLikeActivity); !res {
+ return ServiceResult{}, NewErrNotAcceptablef("Invalid activity: %v", err)
+ }
+ log.Trace("Activity validated: %#v", activity)
+
+ // parse actorID (person)
+ actorURI := activity.Actor.GetID().String()
+ user, _, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI)
+ if err != nil {
+ log.Error("Federated user not found (%s): %v", actorURI, err)
+ return ServiceResult{}, NewErrNotAcceptablef("FindOrCreateFederatedUser failed: %v", err)
+ }
+
+ if !constructorLikeActivity.IsNewer(federationHost.LatestActivity) {
+ return ServiceResult{}, NewErrNotAcceptablef("LatestActivity: activity already processed: %v", err)
+ }
+
+ // parse objectID (repository)
+ objectID, err := fm.NewRepositoryID(constructorLikeActivity.Object.GetID().String(), string(forgefed.ForgejoSourceType))
+ if err != nil {
+ return ServiceResult{}, NewErrNotAcceptablef("Parsing repo objectID failed: %v", err)
+ }
+ if objectID.ID != fmt.Sprint(repositoryID) {
+ return ServiceResult{}, NewErrNotAcceptablef("Invalid repoId: %v", err)
+ }
+ log.Trace("Object accepted: %#v", objectID)
+
+ // execute the activity if the repo was not stared already
+ alreadyStared := repo.IsStaring(ctx, user.ID, repositoryID)
+ if !alreadyStared {
+ err = repo.StarRepo(ctx, user.ID, repositoryID, true)
+ if err != nil {
+ return ServiceResult{}, NewErrNotAcceptablef("Staring failed: %v", err)
+ }
+ }
+ federationHost.LatestActivity = activity.StartTime
+ err = forgefed.UpdateFederationHost(ctx, federationHost)
+ if err != nil {
+ return ServiceResult{}, NewErrNotAcceptablef("Updating federatedHost failed: %v", err)
+ }
+
+ return NewServiceResultStatusOnly(http.StatusNoContent), nil
+}
+
+// Create or update a list of FollowingRepo structs
+func StoreFollowingRepoList(ctx *context_service.Context, localRepoID int64, followingRepoList []string) (int, string, error) {
+ followingRepos := make([]*repo.FollowingRepo, 0, len(followingRepoList))
+ for _, uri := range followingRepoList {
+ federationHost, err := FindOrCreateFederationHost(ctx.Base, uri)
+ if err != nil {
+ return http.StatusInternalServerError, "Wrong FederationHost", err
+ }
+ followingRepoID, err := fm.NewRepositoryID(uri, string(federationHost.NodeInfo.SoftwareName))
+ if err != nil {
+ return http.StatusNotAcceptable, "Invalid federated repo", err
+ }
+ followingRepo, err := repo.NewFollowingRepo(localRepoID, followingRepoID.ID, federationHost.ID, uri)
+ if err != nil {
+ return http.StatusNotAcceptable, "Invalid federated repo", err
+ }
+ followingRepos = append(followingRepos, &followingRepo)
+ }
+
+ if err := repo.StoreFollowingRepos(ctx, localRepoID, followingRepos); err != nil {
+ return 0, "", err
+ }
+
+ return 0, "", nil
+}
+
+func DeleteFollowingRepos(ctx context.Context, localRepoID int64) error {
+ return repo.StoreFollowingRepos(ctx, localRepoID, []*repo.FollowingRepo{})
+}
+
+func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error {
+ followingRepos, err := repo.FindFollowingReposByRepoID(ctx, repoID)
+ log.Trace("Federated Repos is: %#v", followingRepos)
+ if err != nil {
+ return err
+ }
+
+ likeActivityList := make([]fm.ForgeLike, 0)
+ for _, followingRepo := range followingRepos {
+ log.Trace("Found following repo: %#v", followingRepo)
+ target := followingRepo.URI
+ likeActivity, err := fm.NewForgeLike(doer.APActorID(), target, time.Now())
+ if err != nil {
+ return err
+ }
+ likeActivityList = append(likeActivityList, likeActivity)
+ }
+
+ apclientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return err
+ }
+ apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID()+"#main-key")
+ if err != nil {
+ return err
+ }
+ for i, activity := range likeActivityList {
+ activity.StartTime = activity.StartTime.Add(time.Duration(i) * time.Second)
+ json, err := activity.MarshalJSON()
+ if err != nil {
+ return err
+ }
+
+ _, err = apclient.Post(json, fmt.Sprintf("%v/inbox", activity.Object))
+ if err != nil {
+ log.Error("error %v while sending activity: %#v", err, activity)
+ }
+ }
+
+ return nil
+}
diff --git a/services/federation/repository_service.go b/services/federation/repository_service.go
new file mode 100644
index 0000000000..7891d786e2
--- /dev/null
+++ b/services/federation/repository_service.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+func ProcessRepositoryInbox(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) {
+ switch activity.Type {
+ case ap.LikeType:
+ return ProcessLikeActivity(ctx, activity, repositoryID)
+ default:
+ return ServiceResult{}, NewErrNotAcceptablef("Not a like activity: %v", activity.Type)
+ }
+}
diff --git a/services/federation/result.go b/services/federation/result.go
new file mode 100644
index 0000000000..47afb2bdf6
--- /dev/null
+++ b/services/federation/result.go
@@ -0,0 +1,35 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import "github.com/go-ap/activitypub"
+
+type ServiceResult struct {
+ HTTPStatus int
+ Bytes []byte
+ Activity activitypub.Activity
+ withBytes bool
+ withActivity bool
+ statusOnly bool
+}
+
+func NewServiceResultStatusOnly(status int) ServiceResult {
+ return ServiceResult{HTTPStatus: status, statusOnly: true}
+}
+
+func NewServiceResultWithBytes(status int, bytes []byte) ServiceResult {
+ return ServiceResult{HTTPStatus: status, Bytes: bytes, withBytes: true}
+}
+
+func (serviceResult ServiceResult) WithBytes() bool {
+ return serviceResult.withBytes
+}
+
+func (serviceResult ServiceResult) WithActivity() bool {
+ return serviceResult.withActivity
+}
+
+func (serviceResult ServiceResult) StatusOnly() bool {
+ return serviceResult.statusOnly
+}
diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go
new file mode 100644
index 0000000000..fd8cbb39cd
--- /dev/null
+++ b/services/federation/signature_service.go
@@ -0,0 +1,235 @@
+// Copyright 2024, 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+ "crypto/x509"
+ "database/sql"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "net/url"
+
+ "forgejo.org/models/forgefed"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/activitypub"
+ fm "forgejo.org/modules/forgefed"
+
+ ap "github.com/go-ap/activitypub"
+)
+
+// Factory function for ActorID. Created struct is asserted to be valid
+func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) {
+ parsedURI, err := url.Parse(uri)
+ parsedURI.Fragment = ""
+ if err != nil {
+ return fm.ActorID{}, err
+ }
+
+ actionsUser := user.NewAPServerActor()
+ clientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return fm.ActorID{}, err
+ }
+
+ apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID())
+ if err != nil {
+ return fm.ActorID{}, err
+ }
+
+ userResponse, err := apClient.GetBody(parsedURI.String())
+ if err != nil {
+ return fm.ActorID{}, err
+ }
+
+ var actor ap.Actor
+ err = actor.UnmarshalJSON(userResponse)
+ if err != nil {
+ return fm.ActorID{}, err
+ }
+
+ result, err := fm.NewActorID(actor.PublicKey.Owner.String())
+ return result, err
+}
+
+func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) {
+ var federatedUser *user.FederatedUser
+ var keyURL *url.URL
+
+ keyURL, err = url.Parse(keyID)
+ if err != nil {
+ return nil, err
+ }
+
+ // Try if the signing actor is an already known federated user
+ _, federatedUser, err = user.FindFederatedUserByKeyID(ctx, keyURL.String())
+ if err != nil {
+ return nil, err
+ }
+
+ if federatedUser == nil {
+ rawActorID, err := NewActorIDFromKeyID(ctx, keyID)
+ if err != nil {
+ return nil, err
+ }
+
+ _, federatedUser, _, err = FindOrCreateFederatedUser(ctx, rawActorID.AsURI())
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ _, err = forgefed.GetFederationHost(ctx, federatedUser.FederationHostID)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if federatedUser.PublicKey.Valid {
+ pubKey, err := x509.ParsePKIXPublicKey(federatedUser.PublicKey.V)
+ if err != nil {
+ return nil, err
+ }
+ return pubKey, nil
+ }
+
+ // Fetch missing public key
+ pubKey, pubKeyBytes, apPerson, err := fetchKeyFromAp(ctx, *keyURL)
+ if err != nil {
+ return nil, err
+ }
+ if apPerson.Type == ap.ActivityVocabularyType("Person") {
+ // Check federatedUser.id = person.id
+ if federatedUser.ExternalID != apPerson.ID.String() {
+ return nil, fmt.Errorf("federated user fetched (%v) does not match the stored one %v", apPerson, federatedUser)
+ }
+ // update federated user
+ federatedUser.KeyID = sql.NullString{
+ String: apPerson.PublicKey.ID.String(),
+ Valid: true,
+ }
+ federatedUser.PublicKey = sql.Null[sql.RawBytes]{
+ V: pubKeyBytes,
+ Valid: true,
+ }
+ err = user.UpdateFederatedUser(ctx, federatedUser)
+ if err != nil {
+ return nil, err
+ }
+ return pubKey, nil
+ }
+ return nil, nil
+}
+
+func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) {
+ keyURL, err := url.Parse(keyID)
+ if err != nil {
+ return nil, err
+ }
+ rawActorID, err := NewActorIDFromKeyID(ctx, keyID)
+ if err != nil {
+ return nil, err
+ }
+
+ // Is there an already known federation host?
+ federationHost, err := forgefed.FindFederationHostByKeyID(ctx, keyURL.String())
+ if err != nil {
+ return nil, err
+ }
+
+ if federationHost == nil {
+ federationHost, err = FindOrCreateFederationHost(ctx, rawActorID.AsURI())
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Is there an already an key?
+ if federationHost.PublicKey.Valid {
+ pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V)
+ if err != nil {
+ return nil, err
+ }
+ return pubKey, nil
+ }
+
+ // If not, fetch missing public key
+ pubKey, pubKeyBytes, apPerson, err := fetchKeyFromAp(ctx, *keyURL)
+ if err != nil {
+ return nil, err
+ }
+ if apPerson.Type == ap.ActivityVocabularyType("Application") {
+ // Check federationhost.id = person.id
+ if federationHost.HostPort != rawActorID.HostPort || federationHost.HostFqdn != rawActorID.Host ||
+ federationHost.HostSchema != rawActorID.HostSchema {
+ return nil, fmt.Errorf("federation host fetched (%v) does not match the stored one %v", apPerson, federationHost)
+ }
+ // update federation host
+ federationHost.KeyID = sql.NullString{
+ String: apPerson.PublicKey.ID.String(),
+ Valid: true,
+ }
+ federationHost.PublicKey = sql.Null[sql.RawBytes]{
+ V: pubKeyBytes,
+ Valid: true,
+ }
+ err = forgefed.UpdateFederationHost(ctx, federationHost)
+ if err != nil {
+ return nil, err
+ }
+ return pubKey, nil
+ }
+ return nil, nil
+}
+
+func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) {
+ actionsUser := user.NewAPServerActor()
+
+ clientFactory, err := activitypub.GetClientFactory(ctx)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ b, err := apClient.GetBody(keyURL.String())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ person := ap.PersonNew(ap.IRI(keyURL.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)
+ }
+
+ pubKeyFromAp := person.PublicKey
+ if pubKeyFromAp.ID.String() != keyURL.String() {
+ return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %v in %v", keyURL, string(b))
+ }
+
+ pubKeyBytes, err = decodePublicKeyPem(pubKeyFromAp.PublicKeyPem)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ pubKey, err = x509.ParsePKIXPublicKey(pubKeyBytes)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return pubKey, pubKeyBytes, person, err
+}
+
+func decodePublicKeyPem(pubKeyPem string) ([]byte, error) {
+ block, _ := pem.Decode([]byte(pubKeyPem))
+ if block == nil || block.Type != "PUBLIC KEY" {
+ return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
+ }
+
+ return block.Bytes, nil
+}
diff --git a/services/federation/user_activity.go b/services/federation/user_activity.go
new file mode 100644
index 0000000000..0db2aee4ec
--- /dev/null
+++ b/services/federation/user_activity.go
@@ -0,0 +1,83 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package federation
+
+import (
+ "context"
+
+ activities_model "forgejo.org/models/activities"
+ "forgejo.org/models/forgefed"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/structs"
+ "forgejo.org/services/convert"
+
+ ap "github.com/go-ap/activitypub"
+ "github.com/go-ap/jsonld"
+)
+
+func SendUserActivity(ctx context.Context, doer *user.User, activity *activities_model.Action) error {
+ followers, err := user.GetFollowersForUser(ctx, doer)
+ if err != nil {
+ return err
+ }
+
+ userActivity, err := convert.ActionToForgeUserActivity(ctx, activity)
+ if err != nil {
+ return err
+ }
+
+ payload, err := jsonld.WithContext(
+ jsonld.IRI(ap.ActivityBaseURI),
+ ).Marshal(userActivity)
+ if err != nil {
+ return err
+ }
+
+ for _, follower := range followers {
+ _, federatedUserFollower, err := user.GetFederatedUserByUserID(ctx, follower.FollowingUserID)
+ if err != nil {
+ return err
+ }
+
+ federationHost, err := forgefed.GetFederationHost(ctx, federatedUserFollower.FederationHostID)
+ if err != nil {
+ return err
+ }
+
+ hostURL := federationHost.AsURL()
+ if err := deliveryQueue.Push(deliveryQueueItem{
+ InboxURL: hostURL.JoinPath(federatedUserFollower.InboxPath).String(),
+ Doer: doer,
+ Payload: payload,
+ }); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func NotifyActivityPubFollowers(ctx context.Context, actions []activities_model.Action) error {
+ if !setting.Federation.Enabled {
+ return nil
+ }
+ for _, act := range actions {
+ if act.Repo != nil {
+ if act.Repo.IsPrivate {
+ continue
+ }
+ if act.Repo.Owner.KeepActivityPrivate || act.Repo.Owner.Visibility != structs.VisibleTypePublic {
+ continue
+ }
+ }
+ if act.ActUser.KeepActivityPrivate || act.ActUser.Visibility != structs.VisibleTypePublic {
+ continue
+ }
+ if err := SendUserActivity(ctx, act.ActUser, &act); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/services/feed/action.go b/services/feed/action.go
index 2d6a6cb09a..2d07f39284 100644
--- a/services/feed/action.go
+++ b/services/feed/action.go
@@ -9,17 +9,18 @@ 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/setting"
- "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"
+ federation_service "forgejo.org/services/federation"
+ notify_service "forgejo.org/services/notify"
)
type actionNotifier struct {
@@ -39,6 +40,22 @@ func NewNotifier() notify_service.Notifier {
return &actionNotifier{}
}
+func notifyAll(ctx context.Context, action *activities_model.Action) error {
+ out, err := activities_model.NotifyWatchers(ctx, action)
+ if err != nil {
+ return err
+ }
+ return federation_service.NotifyActivityPubFollowers(ctx, out)
+}
+
+func notifyAllActions(ctx context.Context, acts []*activities_model.Action) error {
+ out, err := activities_model.NotifyWatchersActions(ctx, acts)
+ if err != nil {
+ return err
+ }
+ 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)
@@ -50,11 +67,11 @@ 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,
- Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
+ Content: encodeContent(fmt.Sprintf("%d", issue.Index), issue.Title),
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -70,7 +87,7 @@ func (a *actionNotifier) IssueChangeStatus(ctx context.Context, doer *user_model
act := &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- Content: fmt.Sprintf("%d|%s", issue.Index, ""),
+ Content: encodeContent(fmt.Sprintf("%d", issue.Index), ""),
RepoID: issue.Repo.ID,
Repo: issue.Repo,
Comment: actionComment,
@@ -91,7 +108,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)
}
}
@@ -108,18 +125,9 @@ func (a *actionNotifier) CreateIssueComment(ctx context.Context, doer *user_mode
Comment: comment,
CommentID: comment.ID,
IsPrivate: issue.Repo.IsPrivate,
+ Content: encodeContent(fmt.Sprintf("%d", issue.Index), abbreviatedComment(comment.Content)),
}
- truncatedContent, truncatedRight := util.SplitStringAtByteN(comment.Content, 200)
- if truncatedRight != "" {
- // in case the content is in a Latin family language, we remove the last broken word.
- lastSpaceIdx := strings.LastIndex(truncatedContent, " ")
- if lastSpaceIdx != -1 && (len(truncatedContent)-lastSpaceIdx < 15) {
- truncatedContent = truncatedContent[:lastSpaceIdx] + "…"
- }
- }
- act.Content = fmt.Sprintf("%d|%s", issue.Index, truncatedContent)
-
if issue.IsPull {
act.OpType = activities_model.ActionCommentPull
} else {
@@ -127,7 +135,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)
}
}
@@ -146,11 +154,11 @@ 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,
- Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
+ Content: encodeContent(fmt.Sprintf("%d", pull.Issue.Index), pull.Issue.Title),
RepoID: pull.Issue.Repo.ID,
Repo: pull.Issue.Repo,
IsPrivate: pull.Issue.Repo.IsPrivate,
@@ -160,7 +168,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,
@@ -174,7 +182,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,
@@ -188,7 +196,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,
@@ -201,7 +209,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,
@@ -230,7 +238,7 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model
actions = append(actions, &activities_model.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
- Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
+ Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), abbreviatedComment(comm.Content)),
OpType: activities_model.ActionCommentPull,
RepoID: review.Issue.RepoID,
Repo: review.Issue.Repo,
@@ -246,7 +254,7 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model
action := &activities_model.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
- Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
+ Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), abbreviatedComment(comment.Content)),
RepoID: review.Issue.RepoID,
Repo: review.Issue.Repo,
IsPrivate: review.Issue.Repo.IsPrivate,
@@ -266,17 +274,17 @@ 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,
- Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
+ Content: encodeContent(fmt.Sprintf("%d", pr.Issue.Index), pr.Issue.Title),
RepoID: pr.Issue.Repo.ID,
Repo: pr.Issue.Repo,
IsPrivate: pr.Issue.Repo.IsPrivate,
@@ -286,11 +294,11 @@ 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,
- Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
+ Content: encodeContent(fmt.Sprintf("%d", pr.Issue.Index), pr.Issue.Title),
RepoID: pr.Issue.Repo.ID,
Repo: pr.Issue.Repo,
IsPrivate: pr.Issue.Repo.IsPrivate,
@@ -299,16 +307,16 @@ func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_mode
}
}
-func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
+func (*actionNotifier) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
reviewerName := review.Reviewer.Name
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,
- Content: fmt.Sprintf("%d|%s|%s", review.Issue.Index, reviewerName, comment.Content),
+ Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), reviewerName, abbreviatedComment(comment.Content)),
RepoID: review.Issue.Repo.ID,
Repo: review.Issue.Repo,
IsPrivate: review.Issue.Repo.IsPrivate,
@@ -342,7 +350,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,
@@ -362,7 +370,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,
@@ -381,7 +389,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,
@@ -405,7 +413,7 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model
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,
@@ -420,7 +428,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,
@@ -434,7 +442,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,
@@ -452,7 +460,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,
@@ -465,3 +473,35 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
log.Error("NotifyWatchers: %v", err)
}
}
+
+// ... later decoded in models/activities/action.go:GetIssueInfos
+func encodeContent(params ...string) string {
+ contentEncoded, err := json.Marshal(params)
+ if err != nil {
+ log.Error("encodeContent: Unexpected json encoding error: %v", err)
+ }
+ return string(contentEncoded)
+}
+
+// Given a comment of arbitrary-length Markdown text, create an abbreviated Markdown text appropriate for the
+// activity feed.
+func abbreviatedComment(comment string) string {
+ firstLine := strings.Split(comment, "\n")[0]
+
+ if strings.HasPrefix(firstLine, "```") {
+ // First line is is a fenced code block... with no special abbreviate we would display a blank block, or in the
+ // worst-case a ```mermaid would display an error. Better to omit the comment.
+ return ""
+ }
+
+ truncatedContent, truncatedRight := util.SplitStringAtByteN(firstLine, 200)
+ if truncatedRight != "" {
+ // in case the content is in a Latin family language, we remove the last broken word.
+ lastSpaceIdx := strings.LastIndex(truncatedContent, " ")
+ if lastSpaceIdx != -1 && (len(truncatedContent)-lastSpaceIdx < 15) {
+ truncatedContent = truncatedContent[:lastSpaceIdx] + "…"
+ }
+ }
+
+ return truncatedContent
+}
diff --git a/services/feed/action_test.go b/services/feed/action_test.go
index 037cf08dfe..48de7995bf 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,18 +8,18 @@ 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"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
+ 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"
@@ -102,7 +103,7 @@ func TestSyncPushCommits(t *testing.T) {
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","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","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ 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) {
@@ -112,7 +113,7 @@ func TestSyncPushCommits(t *testing.T) {
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","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ 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)
})
}
@@ -129,7 +130,7 @@ func TestPushCommits(t *testing.T) {
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","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","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ 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) {
@@ -139,6 +140,67 @@ func TestPushCommits(t *testing.T) {
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","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
+ 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 TestAbbreviatedComment(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ }{
+ {
+ name: "short single line comment",
+ input: "This is a short comment",
+ expected: "This is a short comment",
+ },
+ {
+ name: "empty comment",
+ input: "",
+ expected: "",
+ },
+ {
+ name: "multiline comment - only first line",
+ input: "First line of comment\nSecond line\nThird line",
+ expected: "First line of comment",
+ },
+ {
+ name: "before clip boundry",
+ input: strings.Repeat("abc ", 50),
+ expected: strings.Repeat("abc ", 50),
+ },
+ {
+ name: "after clip boundry",
+ input: strings.Repeat("abc ", 51),
+ expected: "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc…",
+ },
+ {
+ name: "byte-split would land in middle of a rune",
+ input: strings.Repeat("🎉", 200),
+ expected: "🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉…",
+ },
+ {
+ name: "mermaid block",
+ input: "Interesting point, here's a digram with my thoughts:\n```mermaid\ngraph LR\n a -->|some text| b\n```",
+ expected: "Interesting point, here's a digram with my thoughts:",
+ },
+ {
+ name: "block start",
+ input: "```\n# This file describes the expected reviewers for a PR based on the changed\n# files.\n```\n\nI think this comment is wrong...",
+ expected: "",
+ },
+ {
+ name: "labeled block start",
+ input: "```mermaid\ngraph LR\n a -->|some text| b\n```",
+ expected: "",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := abbreviatedComment(tt.input)
+ assert.Equal(t, tt.expected, result, "abbreviatedComment(%q)", tt.input)
+ })
+ }
+}
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 21443ff6a5..b89e87f749 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"
)
@@ -79,6 +79,7 @@ type AuthenticationForm struct {
SkipLocalTwoFA bool
GroupTeamMap string `binding:"ValidGroupTeamMap"`
GroupTeamMapRemoval bool
+ AllowUsernameChange bool
}
// Validate validates fields
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 7a7d8752cf..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"
)
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 8253a8957b..d040b41395 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"
)
@@ -141,6 +141,7 @@ type RepoSettingForm struct {
PushMirrorSyncOnCommit bool
PushMirrorInterval string
PushMirrorUseSSH bool
+ PushMirrorBranchFilter string `binding:"MaxSize(2048)" preprocess:"TrimSpace"`
Private bool
Template bool
EnablePrune bool
@@ -188,8 +189,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 +278,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 +729,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
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 d76e97ceb1..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"
)
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 f2070983e6..1f2a7f232f 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"
@@ -85,11 +85,20 @@ type DiffLine struct {
// DiffLineSectionInfo represents diff line section meta data
type DiffLineSectionInfo struct {
- Path string
- LastLeftIdx int
- LastRightIdx int
- LeftIdx int
- RightIdx int
+ Path string
+
+ // Last(Left/Right)Idx do not directly relate to this diff section, but indicate the last line number in the
+ // previous diff section. Set to 0 for the first diff section of a file, and 1 for the first line of code in the
+ // file.
+ LastLeftIdx int
+ LastRightIdx int
+
+ // (Left/Right)Idx are the first line number in this diff section
+ LeftIdx int
+ RightIdx int
+
+ // Number of lines contained within each diff section. In the UI, these fields are set to 0 in cases where a
+ // section is being used as a placeholder at the end of a diff to allow expansion into the remainder of the file.
LeftHunkSize int
RightHunkSize int
}
@@ -157,7 +166,7 @@ func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
}
if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 {
return DiffLineExpandUp
- } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 {
+ } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx-1 > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 {
return DiffLineExpandUpDown
} else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 {
return DiffLineExpandDown
@@ -440,11 +449,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 +1087,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,62 +1115,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, err := commit.Parent(0)
+ // 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, err
+ return nil, nil, fmt.Errorf("not able to get first parent of %q: %w", afterCommit.ID.String(), err)
}
- actualBeforeCommitID = parentCommit.ID.String()
+ 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
-
- 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()
@@ -1153,6 +1181,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{
@@ -1167,14 +1196,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)
@@ -1210,7 +1258,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)
}
@@ -1272,7 +1320,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
}
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index f2c099d554..d4d1cd4460 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,229 @@ 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 TestDiffLine_GetExpandDirection(t *testing.T) {
+ tests := []struct {
+ name string
+ diffLine *DiffLine
+ expectedResult DiffLineExpandDirection
+ }{
+ {
+ name: "non-section line - no expansion",
+ diffLine: &DiffLine{
+ Type: DiffLineAdd,
+ SectionInfo: &DiffLineSectionInfo{},
+ },
+ expectedResult: DiffLineExpandNone,
+ },
+ {
+ name: "nil section info - no expansion",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: nil,
+ },
+ expectedResult: DiffLineExpandNone,
+ },
+ {
+ name: "no lines between",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Previous section of the diff displayed up to line 530...
+ LastRightIdx: 530,
+ LastLeftIdx: 530,
+ // This section of the diff starts at line 531...
+ RightIdx: 531,
+ LeftIdx: 531,
+ },
+ },
+ // There are zero lines between 530 and 531, so we should have nothing to expand.
+ expectedResult: DiffLineExpandNone,
+ },
+ {
+ name: "first diff section is the start of the file",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be
+ // the first section -is- the first line of the file).
+ LastRightIdx: 0,
+ LastLeftIdx: 0,
+ // The diff section is showing line 1, the top of th efile.
+ RightIdx: 1,
+ LeftIdx: 1,
+ },
+ },
+ // We're at the top of the file; no expansion.
+ expectedResult: DiffLineExpandNone,
+ },
+ {
+ name: "first diff section doesn't start at the top of the file",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be
+ // the first section -is- the first line of the file).
+ LastRightIdx: 0,
+ LastLeftIdx: 0,
+ RightIdx: 531,
+ LeftIdx: 531,
+ },
+ },
+ // We're at the top of the diff but there's content above, so can only expand up.
+ expectedResult: DiffLineExpandUp,
+ },
+ {
+ name: "middle of the file with single expansion",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Previous section ended at ~500...
+ LastRightIdx: 500,
+ LastLeftIdx: 500,
+ // Next section starts one line away...
+ RightIdx: 502,
+ LeftIdx: 502,
+ // The next block has content (> 0)
+ RightHunkSize: 50,
+ LeftHunkSize: 50,
+ },
+ },
+ // Can be expanded in a single direction, displaying the missing line (501).
+ expectedResult: DiffLineExpandSingle,
+ },
+ {
+ name: "middle of the file with multi line expansion",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Previous section ended at ~500...
+ LastRightIdx: 500,
+ LastLeftIdx: 500,
+ // Lines 501-520 are hidden, exactly 20 lines, matching BlobExcerptChunkSize (20)...
+ RightIdx: 521,
+ LeftIdx: 521,
+ // The next block has content (> 0)
+ RightHunkSize: 50,
+ LeftHunkSize: 50,
+ },
+ },
+ // Can be expanded in a single direction, displaying all the hidden 20 lines.
+ expectedResult: DiffLineExpandSingle,
+ },
+ {
+ name: "middle of the file with multi direction expansion",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // Previous section ended at ~500...
+ LastRightIdx: 500,
+ LastLeftIdx: 500,
+ // Lines 501-521 are hidden, exactly 21 lines, exceeding BlobExcerptChunkSize (20)...
+ RightIdx: 522,
+ LeftIdx: 522,
+ // The next block has content (> 0)
+ RightHunkSize: 50,
+ LeftHunkSize: 50,
+ },
+ },
+ // Now can be expanded down to display from 501-520 (521 remains hidden), or up to display 502-521 (501
+ // remains hidden).
+ expectedResult: DiffLineExpandUpDown,
+ },
+ {
+ name: "end of the diff but still file content to display",
+ diffLine: &DiffLine{
+ Type: DiffLineSection,
+ SectionInfo: &DiffLineSectionInfo{
+ // We had a previous diff section, of any size/location...
+ LastRightIdx: 200,
+ LastLeftIdx: 200,
+ RightIdx: 531,
+ LeftIdx: 531,
+ // Hunk size size 0 is a placeholder value for the end or beginning of a file...
+ RightHunkSize: 0,
+ LeftHunkSize: 0,
+ },
+ },
+ // Combination of conditions says we're at the end of the last diff section, can only expand down.
+ expectedResult: DiffLineExpandDown,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.diffLine.GetExpandDirection()
+ assert.Equal(t, tt.expectedResult, result)
+ })
+ }
+}
+
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..fcf06d9ec8 100644
--- a/services/issue/comments_test.go
+++ b/services/issue/comments_test.go
@@ -6,17 +6,19 @@ 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/moderation"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ webhook_model "forgejo.org/models/webhook"
+ "forgejo.org/modules/json"
+ "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 +50,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 +61,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 +71,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 +110,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 +125,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,12 +141,49 @@ 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})
})
}
+
+func TestCreateShadowCopyOnCommentUpdate(t *testing.T) {
+ defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ userAlexSmithID := int64(1002)
+ spamCommentID := int64(18) // posted by @alexsmith
+ abuseReportID := int64(1) // submitted for above comment
+ newCommentContent := "If anyone needs help, just contact me."
+
+ // Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment.
+ poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID})
+ comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID})
+ report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{
+ ID: abuseReportID,
+ ContentType: moderation.ReportedContentTypeComment,
+ ContentID: comment.ID,
+ })
+ // The report should not already have a shadow copy linked.
+ assert.False(t, report.ShadowCopyID.Valid)
+
+ // The abusive user is updating their comment.
+ oldContent := comment.Content
+ comment.Content = newCommentContent
+ require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent))
+
+ // Reload the report.
+ report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID})
+ // A shadow copy should have been created and linked to our report.
+ assert.True(t, report.ShadowCopyID.Valid)
+ // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values.
+ shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64})
+ shadowCopyCommentData := new(issues_model.CommentData)
+ require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData))
+ // Check to see if the initial content of the comment was stored within the shadow copy.
+ assert.Equal(t, oldContent, shadowCopyCommentData.Content)
+}
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..6245344ccb 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", ".forgejo/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 1b1be4c656..b1b9191df3 100644
--- a/services/mailer/incoming/incoming.go
+++ b/services/mailer/incoming/incoming.go
@@ -14,10 +14,10 @@ import (
"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"
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/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..a99af823b3
--- /dev/null
+++ b/services/mailer/mail_actions.go
@@ -0,0 +1,87 @@
+// 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"
+)
+
+var MailActionRun = mailActionRun // make it mockable
+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]
+ }
+
+ 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,
+ "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..e84441f460
--- /dev/null
+++ b/services/mailer/mail_actions_now_done_test.go
@@ -0,0 +1,325 @@
+// 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_sha", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on")
+}
+
+func TestActionRunNowDoneStatusMatrix(t *testing.T) {
+ successStatuses := []actions_model.Status{
+ actions_model.StatusSuccess,
+ actions_model.StatusSkipped,
+ actions_model.StatusCancelled,
+ }
+ failureStatuses := []actions_model.Status{
+ actions_model.StatusFailure,
+ }
+
+ for _, testCase := range []struct {
+ name string
+ statuses []actions_model.Status
+ hasLastRun bool
+ lastStatuses []actions_model.Status
+ run bool
+ }{
+ {
+ name: "FailureNoLastRun",
+ statuses: failureStatuses,
+ run: true,
+ },
+ {
+ name: "SuccessNoLastRun",
+ statuses: successStatuses,
+ run: false,
+ },
+ {
+ name: "FailureLastRunSuccess",
+ statuses: failureStatuses,
+ hasLastRun: true,
+ lastStatuses: successStatuses,
+ run: true,
+ },
+ {
+ name: "FailureLastRunFailure",
+ statuses: failureStatuses,
+ hasLastRun: true,
+ lastStatuses: failureStatuses,
+ run: true,
+ },
+ {
+ name: "SuccessLastRunFailure",
+ statuses: successStatuses,
+ hasLastRun: true,
+ lastStatuses: failureStatuses,
+ run: true,
+ },
+ {
+ name: "SuccessLastRunSuccess",
+ statuses: successStatuses,
+ hasLastRun: true,
+ lastStatuses: successStatuses,
+ run: false,
+ },
+ } {
+ t.Run(testCase.name, func(t *testing.T) {
+ var called bool
+ defer test.MockVariableValue(&MailActionRun, func(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error {
+ called = true
+ return nil
+ })()
+ for _, status := range testCase.statuses {
+ for _, lastStatus := range testCase.lastStatuses {
+ called = false
+ n := NewNotifier()
+ var lastRun *actions_model.ActionRun
+ if testCase.hasLastRun {
+ lastRun = &actions_model.ActionRun{
+ Status: lastStatus,
+ }
+ }
+ n.ActionRunNowDone(t.Context(),
+ &actions_model.ActionRun{
+ Status: status,
+ },
+ actions_model.StatusUnknown,
+ lastRun)
+ assert.Equal(t, testCase.run, called, "status = %s, lastStatus = %s", status, lastStatus)
+ }
+ }
+ })
+ }
+}
+
+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 765c8cb6c9..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 := 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 43e5d83890..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"
@@ -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..640de31fcc 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.IsFailure() && (lastRun == nil || !lastRun.Status.IsFailure()) {
+ 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 4d103048b5..8195451746 100644
--- a/services/markup/processorhelper_test.go
+++ b/services/markup/processorhelper_test.go
@@ -8,11 +8,11 @@ import (
"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"
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 fbd4e70143..315c7be709 100644
--- a/services/migrations/codebase_test.go
+++ b/services/migrations/codebase_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
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 cb13cd3e5c..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"
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 b42c7aa4da..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"
)
@@ -504,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 {
@@ -511,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 {
@@ -598,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 b9ddb9b431..5acc3b86a9 100644
--- a/services/migrations/gitea_downloader_test.go
+++ b/services/migrations/gitea_downloader_test.go
@@ -9,8 +9,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"
"github.com/stretchr/testify/require"
@@ -45,7 +45,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
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 7bd6538ff2..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"
)
@@ -766,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,
@@ -802,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 e01f4664ba..85e733cc51 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -12,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"
@@ -64,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,
@@ -173,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) {
@@ -224,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) {
@@ -504,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 7025354f77..317eb568b5 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)
@@ -364,7 +364,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
// Prevent open redirect
if !hasBaseURL(redirectURL, g.baseURL) &&
- !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") {
+ !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") &&
+ !hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") {
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL)
return io.NopCloser(strings.NewReader(redirectURL)), nil
@@ -885,3 +886,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 080fd497ca..c5e24ebbcd 100644
--- a/services/migrations/github_test.go
+++ b/services/migrations/github_test.go
@@ -9,8 +9,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"
"github.com/stretchr/testify/require"
@@ -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 2eb3e6629d..f54f682c47 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -15,12 +15,12 @@ import (
"strings"
"time"
- 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"
- 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"
gitlab "gitlab.com/gitlab-org/api/client-go"
)
@@ -99,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()))
}
@@ -213,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
diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go
index f1404d946d..30b24f09e8 100644
--- a/services/migrations/gitlab_test.go
+++ b/services/migrations/gitlab_test.go
@@ -12,9 +12,9 @@ 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"
@@ -49,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)
@@ -352,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)
@@ -361,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) {
@@ -531,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,
@@ -623,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 1fef4808b0..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"
)
diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go
index 450aeab5ef..bf0d063ca4 100644
--- a/services/migrations/gogs_test.go
+++ b/services/migrations/gogs_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,7 +25,7 @@ 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
}
@@ -215,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 f78d75e4db..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) {
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 46e3eb8d18..5bb2e2bb5c 100644
--- a/services/migrations/onedev_test.go
+++ b/services/migrations/onedev_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"time"
- base "code.gitea.io/gitea/modules/migration"
+ base "forgejo.org/modules/migration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
diff --git a/services/migrations/pagure.go b/services/migrations/pagure.go
new file mode 100644
index 0000000000..f9433671c0
--- /dev/null
+++ b/services/migrations/pagure.go
@@ -0,0 +1,600 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package migrations
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ base "forgejo.org/modules/migration"
+ "forgejo.org/modules/proxy"
+ "forgejo.org/modules/structs"
+ "forgejo.org/modules/util"
+)
+
+var (
+ _ base.Downloader = &PagureDownloader{}
+ _ base.DownloaderFactory = &PagureDownloaderFactory{}
+)
+
+func init() {
+ RegisterDownloaderFactory(&PagureDownloaderFactory{})
+}
+
+// PagureDownloaderFactory defines a downloader factory
+type PagureDownloaderFactory struct{}
+
+// PagureUser defines a user on Pagure to be migrated over to Forgejo
+type PagureUser struct {
+ FullURL string `json:"full_url"`
+ Fullname string `json:"fullname"`
+ Name string `json:"name"`
+ URLPath string `json:"url_path"`
+}
+
+// PagureRepoInfo describes the repository with preliminary information
+type PagureRepoInfo struct {
+ ID int64 `json:"id"`
+ Name string `json:"name"`
+ FullName string `json:"fullname"`
+ Description string `json:"description"`
+ Topics []string `json:"tags"`
+ CloseStatuses []string `json:"close_status"`
+ Priorities map[string]string `json:"priorities"`
+ Milestones map[string]struct {
+ Active bool `json:"active"`
+ Date *string `json:"date"`
+ } `json:"milestones"`
+}
+
+// PagureDownloader implements a Downloader interface to get repository information from Pagure
+type PagureDownloader struct {
+ base.NullDownloader
+ ctx context.Context
+ client *http.Client
+ baseURL *url.URL
+ meta PagureRepoInfo
+ repoName string
+ token string
+ privateIssuesOnlyRepo bool
+ repoID int64
+ maxIssueIndex int64
+ userMap map[string]*PagureUser
+ milestoneMap map[int64]string
+ priorities map[string]string
+}
+
+// PagureLabelsList defines a list of labels under an issue tracker
+type PagureLabelsList struct {
+ Labels []string `json:"tags"`
+}
+
+// PagureLabel defines a label under the issue tracker labels list
+type PagureLabel struct {
+ Label string `json:"tag"`
+ LabelColor string `json:"tag_color"`
+ LabelDescription string `json:"tag_description"`
+}
+
+// PagureIssueContext confirms if a said unit is an issue ticket or a pull request
+type PagureIssueContext struct {
+ IsPullRequest bool
+}
+
+// PagureIssuesResponse describes a list of issue tickets under an issue tracker
+type PagureIssuesResponse struct {
+ Issues []struct {
+ Assignee any `json:"assignee"`
+ Blocks []string `json:"blocks"`
+ CloseStatus string `json:"close_status"`
+ ClosedAt string `json:"closed_at"`
+ ClosedBy any `json:"closed_by"`
+ Comments []any `json:"comments"`
+ Content string `json:"content"`
+ CustomFields []any `json:"custom_fields"`
+ DateCreated string `json:"date_created"`
+ Depends []any `json:"depends"`
+ ID int64 `json:"id"`
+ LastUpdated string `json:"last_updated"`
+ Milestone string `json:"milestone"`
+ Priority int64 `json:"priority"`
+ Private bool `json:"private"`
+ Status string `json:"status"`
+ Tags []string `json:"tags"`
+ Title string `json:"title"`
+ User PagureUser `json:"user"`
+ } `json:"issues"`
+ Pagination struct {
+ First string `json:"first"`
+ Last string `json:"last"`
+ Next *string `json:"next"`
+ Page int `json:"page"`
+ Pages int `json:"pages"`
+ PerPage int `json:"per_page"`
+ Prev string `json:"prev"`
+ }
+}
+
+// PagureIssueDetail describes a list of issue comments under an issue ticket
+type PagureIssueDetail struct {
+ Comment string `json:"comment"`
+ DateCreated string `json:"date_created"`
+ ID int64 `json:"id"`
+ Notification bool `json:"notification"`
+ User PagureUser `json:"user"`
+}
+
+// PagureCommitInfo describes a commit
+type PagureCommitInfo struct {
+ Author string `json:"author"`
+ CommitTime int64 `json:"commit_time"`
+ CommitTimeOffset int `json:"commit_time_offset"`
+ Committer string `json:"committer"`
+ Hash string `json:"hash"`
+ Message string `json:"message"`
+ ParentIDs []string `json:"parent_ids"`
+ TreeID string `json:"tree_id"`
+}
+
+// PagurePRRresponse describes a list of pull requests under an issue tracker
+type PagurePRResponse struct {
+ Pagination struct {
+ Next *string `json:"next"`
+ } `json:"pagination"`
+ Requests []struct {
+ Branch string `json:"branch"`
+ BranchFrom string `json:"branch_from"`
+ CommitStop string `json:"commit_stop"`
+ DateCreated string `json:"date_created"`
+ FullURL string `json:"full_url"`
+ ID int `json:"id"`
+ InitialComment string `json:"initial_comment"`
+ LastUpdated string `json:"last_updated"`
+ Status string `json:"status"`
+ Tags []string `json:"tags"`
+ Title string `json:"title"`
+ User PagureUser `json:"user"`
+ ClosedAt string `json:"closed_at"`
+ ClosedBy PagureUser `json:"closed_by"`
+ RepoFrom struct {
+ FullURL string `json:"full_url"`
+ } `json:"repo_from"`
+ } `json:"requests"`
+}
+
+// processDate converts epoch time string to Go formatted time
+func processDate(dateStr *string) time.Time {
+ date := time.Time{}
+ if dateStr == nil || *dateStr == "" {
+ return date
+ }
+
+ unix, err := strconv.Atoi(*dateStr)
+ if err != nil {
+ log.Error("Error:", err)
+ return date
+ }
+
+ date = time.Unix(int64(unix), 0)
+ return date
+}
+
+// New returns a downloader related to this factory according MigrateOptions
+func (f *PagureDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
+ u, err := url.Parse(opts.CloneAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ var repoName string
+
+ fields := strings.Split(strings.Trim(u.Path, "/"), "/")
+ if len(fields) == 2 {
+ repoName = fields[0] + "/" + strings.TrimSuffix(fields[1], ".git")
+ } else if len(fields) == 1 {
+ repoName = strings.TrimSuffix(fields[0], ".git")
+ } else {
+ return nil, fmt.Errorf("invalid path: %s", u.Path)
+ }
+
+ u.Path, u.Fragment = "", ""
+ log.Info("Create Pagure downloader. BaseURL: %v RepoName: %s", u, repoName)
+
+ return NewPagureDownloader(ctx, u, opts.AuthToken, repoName), nil
+}
+
+// GitServiceType returns the type of Git service
+func (f *PagureDownloaderFactory) GitServiceType() structs.GitServiceType {
+ return structs.PagureService
+}
+
+// SetContext sets context
+func (d *PagureDownloader) SetContext(ctx context.Context) {
+ d.ctx = ctx
+}
+
+// NewPagureDownloader creates a new downloader object
+func NewPagureDownloader(ctx context.Context, baseURL *url.URL, token, repoName string) *PagureDownloader {
+ var privateIssuesOnlyRepo bool
+ if token != "" {
+ privateIssuesOnlyRepo = true
+ }
+
+ downloader := &PagureDownloader{
+ ctx: ctx,
+ baseURL: baseURL,
+ repoName: repoName,
+ client: &http.Client{
+ Transport: &http.Transport{
+ Proxy: proxy.Proxy(),
+ },
+ },
+ token: token,
+ privateIssuesOnlyRepo: privateIssuesOnlyRepo,
+ userMap: make(map[string]*PagureUser),
+ milestoneMap: make(map[int64]string),
+ priorities: make(map[string]string),
+ }
+
+ return downloader
+}
+
+// String sets the default text for information purposes
+func (d *PagureDownloader) String() string {
+ return fmt.Sprintf("migration from Pagure server %s [%d]/%s", d.baseURL, d.repoID, d.repoName)
+}
+
+// LogString sets the default text for logging purposes
+func (d *PagureDownloader) LogString() string {
+ if d == nil {
+ return ""
+ }
+ return fmt.Sprintf("", d.baseURL, d.repoID, d.repoName)
+}
+
+// callAPI handles all the requests made against Pagure
+func (d *PagureDownloader) callAPI(endpoint string, parameter map[string]string, result any) error {
+ u, err := d.baseURL.Parse(endpoint)
+ if err != nil {
+ return err
+ }
+
+ if parameter != nil {
+ query := u.Query()
+ for k, v := range parameter {
+ query.Set(k, v)
+ }
+ u.RawQuery = query.Encode()
+ }
+
+ req, err := http.NewRequestWithContext(d.ctx, "GET", u.String(), nil)
+ if err != nil {
+ return err
+ }
+ if d.privateIssuesOnlyRepo {
+ req.Header.Set("Authorization", "token "+d.token)
+ }
+
+ resp, err := d.client.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ decoder := json.NewDecoder(resp.Body)
+ return decoder.Decode(&result)
+}
+
+// GetRepoInfo returns repository information from Pagure
+func (d *PagureDownloader) GetRepoInfo() (*base.Repository, error) {
+ err := d.callAPI("/api/0/"+d.repoName, nil, &d.meta)
+ if err != nil {
+ return nil, err
+ }
+
+ d.repoID, d.priorities = d.meta.ID, d.meta.Priorities
+
+ cloneURL, err := d.baseURL.Parse(d.meta.FullName)
+ if err != nil {
+ return nil, err
+ }
+ originalURL, err := d.baseURL.Parse(d.meta.FullName)
+ if err != nil {
+ return nil, err
+ }
+
+ return &base.Repository{
+ Name: d.meta.Name,
+ Description: d.meta.Description,
+ CloneURL: cloneURL.String() + ".git",
+ OriginalURL: originalURL.String(),
+ }, nil
+}
+
+// GetMilestones returns milestones information from Pagure
+func (d *PagureDownloader) GetMilestones() ([]*base.Milestone, error) {
+ milestones := make([]*base.Milestone, 0, len(d.meta.Milestones))
+ for name, details := range d.meta.Milestones {
+ state := "closed"
+ if details.Active {
+ state = "open"
+ }
+
+ deadline := processDate(details.Date)
+ milestones = append(milestones, &base.Milestone{
+ Title: name,
+ Description: "",
+ Deadline: &deadline,
+ State: state,
+ })
+ }
+
+ return milestones, nil
+}
+
+// GetLabels returns labels information from Pagure
+func (d *PagureDownloader) GetLabels() ([]*base.Label, error) {
+ rawLabels := PagureLabelsList{}
+
+ err := d.callAPI("/api/0/"+d.repoName+"/tags", nil, &rawLabels)
+ if err != nil {
+ return nil, err
+ }
+
+ labels := make([]*base.Label, 0, len(rawLabels.Labels)+len(d.meta.CloseStatuses))
+
+ for _, label := range rawLabels.Labels {
+ rawLabel := PagureLabel{}
+ err = d.callAPI("/api/0/"+d.repoName+"/tag/"+label, nil, &rawLabel)
+ if err != nil {
+ return nil, err
+ }
+ labels = append(labels, &base.Label{
+ Name: label,
+ Description: rawLabel.LabelDescription,
+ Color: strings.TrimPrefix(rawLabel.LabelColor, "#"),
+ })
+ }
+
+ for _, closeStatus := range d.meta.CloseStatuses {
+ labels = append(labels, &base.Label{
+ Name: "Closed As/" + closeStatus,
+ Description: "Closed with the reason of " + closeStatus,
+ Color: "FF0000",
+ Exclusive: true,
+ })
+ }
+
+ for _, value := range d.priorities {
+ if value != "" {
+ labels = append(labels, &base.Label{
+ Name: "Priority/" + value,
+ Description: "Priority of " + value,
+ Color: "FF00FF",
+ Exclusive: true,
+ })
+ }
+ }
+
+ return labels, nil
+}
+
+// GetIssues returns issue tickets from Pagure
+func (d *PagureDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+ rawIssues := PagureIssuesResponse{}
+
+ err := d.callAPI(
+ "/api/0/"+d.repoName+"/issues",
+ map[string]string{
+ "page": strconv.Itoa(page),
+ "per_page": strconv.Itoa(perPage),
+ "status": "all",
+ },
+ &rawIssues,
+ )
+ if err != nil {
+ return nil, false, err
+ }
+
+ issues := make([]*base.Issue, 0, len(rawIssues.Issues))
+ for _, issue := range rawIssues.Issues {
+ log.Debug("Processing issue %d", issue.ID)
+ if d.privateIssuesOnlyRepo && !issue.Private {
+ log.Info("Skipping issue %d because it is not private and we are only downloading private issues", issue.ID)
+ continue
+ }
+ labels := []*base.Label{}
+ for _, tag := range issue.Tags {
+ labels = append(labels, &base.Label{Name: tag})
+ }
+
+ if issue.CloseStatus != "" {
+ labels = append(labels, &base.Label{Name: "Closed As/" + issue.CloseStatus})
+ }
+
+ priorityStr := ""
+ if issue.Priority != 0 {
+ priorityStr = strconv.FormatInt(issue.Priority, 10)
+ }
+
+ if priorityStr != "" {
+ priorityValue, ok := d.priorities[priorityStr]
+ if ok {
+ labels = append(labels, &base.Label{Name: "Priority/" + priorityValue})
+ }
+ }
+ log.Trace("Adding issue: %d", issue.ID)
+
+ closedat := processDate(&issue.ClosedAt)
+
+ issues = append(issues, &base.Issue{
+ Title: issue.Title,
+ Number: issue.ID,
+ PosterName: issue.User.Name,
+ PosterID: -1,
+ Content: issue.Content,
+ Milestone: issue.Milestone,
+ State: strings.ToLower(issue.Status),
+ Created: processDate(&issue.DateCreated),
+ Updated: processDate(&issue.LastUpdated),
+ Closed: &closedat,
+ Labels: labels,
+ ForeignIndex: issue.ID,
+ Context: PagureIssueContext{IsPullRequest: false},
+ })
+
+ if d.maxIssueIndex < issue.ID {
+ d.maxIssueIndex = issue.ID
+ }
+ }
+ hasNext := rawIssues.Pagination.Next == nil
+
+ return issues, hasNext, nil
+}
+
+// GetComments returns issue comments from Pagure
+func (d *PagureDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+ context, ok := commentable.GetContext().(PagureIssueContext)
+ if !ok {
+ return nil, false, fmt.Errorf("unexpected context: %+v", commentable.GetContext())
+ }
+
+ list := struct {
+ Comments []PagureIssueDetail `json:"comments"`
+ }{}
+ var endpoint string
+
+ if context.IsPullRequest {
+ endpoint = fmt.Sprintf("/api/0/%s/pull-request/%d", d.repoName, commentable.GetForeignIndex())
+ } else {
+ endpoint = fmt.Sprintf("/api/0/%s/issue/%d", d.repoName, commentable.GetForeignIndex())
+ }
+
+ err := d.callAPI(endpoint, nil, &list)
+ if err != nil {
+ log.Error("Error calling API: %v", err)
+ return nil, false, err
+ }
+
+ comments := make([]*base.Comment, 0, len(list.Comments))
+ for _, unit := range list.Comments {
+ if len(unit.Comment) == 0 {
+ log.Error("Empty comment")
+ continue
+ }
+
+ log.Trace("Adding comment: %d", unit.ID)
+ c := &base.Comment{
+ IssueIndex: commentable.GetLocalIndex(),
+ Index: unit.ID,
+ PosterName: unit.User.Name,
+ PosterID: -1,
+ Content: unit.Comment,
+ Created: processDate(&unit.DateCreated),
+ }
+ comments = append(comments, c)
+ }
+
+ return comments, true, nil
+}
+
+// GetPullRequests returns pull requests from Pagure
+func (d *PagureDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+ // Could not figure out how to disable this in opts, so if a private issues only repo,
+ // We just return an empty list
+ if d.privateIssuesOnlyRepo {
+ pullRequests := make([]*base.PullRequest, 0)
+ return pullRequests, true, nil
+ }
+
+ rawPullRequests := PagurePRResponse{}
+ commit := PagureCommitInfo{}
+
+ err := d.callAPI(
+ "/api/0/"+d.repoName+"/pull-requests",
+ map[string]string{
+ "page": strconv.Itoa(page),
+ "per_page": strconv.Itoa(perPage),
+ "status": "all",
+ },
+ &rawPullRequests,
+ )
+ if err != nil {
+ return nil, false, err
+ }
+
+ pullRequests := make([]*base.PullRequest, 0, len(rawPullRequests.Requests))
+
+ for _, pr := range rawPullRequests.Requests {
+ var state, baseSHA string
+ var merged bool
+ labels := []*base.Label{}
+
+ for _, tag := range pr.Tags {
+ labels = append(labels, &base.Label{Name: tag})
+ }
+ mergedtime := processDate(&pr.ClosedAt)
+
+ err = d.callAPI("/api/0/"+d.repoName+"/c/"+pr.CommitStop+"/info", nil, &commit)
+ if err != nil {
+ return nil, false, err
+ }
+
+ if util.ASCIIEqualFold(pr.Status, "merged") {
+ state, merged, baseSHA = "closed", true, commit.ParentIDs[0]
+ } else if util.ASCIIEqualFold(pr.Status, "open") {
+ state, merged, baseSHA = "open", false, commit.ParentIDs[0]
+ } else {
+ state, merged, baseSHA = "closed", false, commit.ParentIDs[0]
+ }
+
+ pullRequests = append(pullRequests, &base.PullRequest{
+ Title: pr.Title,
+ Number: int64(pr.ID),
+ PosterName: pr.User.Name,
+ PosterID: -1,
+ Content: pr.InitialComment,
+ State: state,
+ Created: processDate(&pr.DateCreated),
+ Updated: processDate(&pr.LastUpdated),
+ MergedTime: &mergedtime,
+ Closed: &mergedtime,
+ Merged: merged,
+ Labels: labels,
+ Head: base.PullRequestBranch{
+ Ref: pr.BranchFrom,
+ SHA: pr.CommitStop,
+ RepoName: d.repoName,
+ CloneURL: pr.RepoFrom.FullURL + ".git",
+ },
+ Base: base.PullRequestBranch{
+ Ref: pr.Branch,
+ SHA: baseSHA,
+ RepoName: d.repoName,
+ },
+ ForeignIndex: int64(pr.ID),
+ PatchURL: pr.FullURL + ".patch",
+ Context: PagureIssueContext{IsPullRequest: true},
+ })
+
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(pullRequests[len(pullRequests)-1], d.baseURL.String(), d)
+ }
+
+ hasNext := rawPullRequests.Pagination.Next == nil
+
+ return pullRequests, hasNext, nil
+}
+
+// GetTopics return repository topics from Pagure
+func (d *PagureDownloader) GetTopics() ([]string, error) {
+ return d.meta.Topics, nil
+}
diff --git a/services/migrations/pagure_test.go b/services/migrations/pagure_test.go
new file mode 100644
index 0000000000..c81bd28904
--- /dev/null
+++ b/services/migrations/pagure_test.go
@@ -0,0 +1,616 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package migrations
+
+import (
+ "fmt"
+ "net/url"
+ "os"
+ "strings"
+ "testing"
+ "time"
+
+ "forgejo.org/models/unittest"
+ base "forgejo.org/modules/migration"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestPagureDownloadRepoWithPublicIssues(t *testing.T) {
+ // Skip tests if Pagure token is not found
+ cloneUser := os.Getenv("PAGURE_CLONE_USER")
+ clonePassword := os.Getenv("PAGURE_CLONE_PASSWORD")
+ apiUser := os.Getenv("PAGURE_API_USER")
+ apiPassword := os.Getenv("PAGURE_API_TOKEN")
+
+ cloneAddr := fmt.Sprintf("https://%s:%s@pagure.io/protop2g-test-srce.git", cloneUser, clonePassword)
+ u, _ := url.Parse(cloneAddr)
+
+ fixtPath := "./testdata/pagure/full_download/unauthorized"
+ server := unittest.NewMockWebServer(t, "https://pagure.io", fixtPath, false)
+ defer server.Close()
+
+ factory := &PagureDownloaderFactory{}
+ downloader, err := factory.New(t.Context(), base.MigrateOptions{
+ CloneAddr: u.String(),
+ AuthUsername: apiUser,
+ AuthPassword: apiPassword,
+ })
+ require.NoError(t, err)
+
+ repo, err := downloader.GetRepoInfo()
+ require.NoError(t, err)
+
+ // Testing repository contents migration
+ assertRepositoryEqual(t, &base.Repository{
+ Name: "protop2g-test-srce",
+ Owner: "",
+ Description: "The source namespace for the Pagure Exporter project to run tests against",
+ CloneURL: cloneAddr,
+ OriginalURL: strings.ReplaceAll(cloneAddr, ".git", ""),
+ }, repo)
+
+ topics, err := downloader.GetTopics()
+ require.NoError(t, err)
+
+ // Testing repository topics migration
+ assert.Equal(t, []string{"srce", "test", "gridhead", "protop2g"}, topics)
+
+ // Testing labels migration
+ labels, err := downloader.GetLabels()
+ require.NoError(t, err)
+ assert.Len(t, labels, 15)
+
+ // Testing issue tickets probing
+ issues, isEnd, err := downloader.GetIssues(1, 20)
+ require.NoError(t, err)
+ assert.True(t, isEnd)
+
+ // Testing issue tickets migration
+ assertIssuesEqual(t, []*base.Issue{
+ {
+ Number: 2,
+ Title: "This is the title of the second test issue",
+ Content: "This is the body of the second test issue",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Milestone: "Milestone BBBB",
+ Created: time.Date(2023, time.October, 13, 4, 1, 16, 0, time.UTC),
+ Updated: time.Date(2025, time.June, 25, 6, 25, 57, 0, time.UTC),
+ Closed: timePtr(time.Date(2025, time.June, 25, 6, 22, 59, 0, time.UTC)),
+ Labels: []*base.Label{
+ {
+ Name: "cccc",
+ },
+ {
+ Name: "dddd",
+ },
+ {
+ Name: "Closed As/Complete",
+ },
+ {
+ Name: "Priority/Rare",
+ },
+ },
+ },
+ {
+ Number: 1,
+ Title: "This is the title of the first test issue",
+ Content: "This is the body of the first test issue",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "open",
+ Milestone: "Milestone AAAA",
+ Created: time.Date(2023, time.October, 13, 3, 57, 42, 0, time.UTC),
+ Updated: time.Date(2025, time.June, 25, 6, 25, 45, 0, time.UTC),
+ Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ Labels: []*base.Label{
+ {
+ Name: "aaaa",
+ },
+ {
+ Name: "bbbb",
+ },
+ {
+ Name: "Priority/Epic",
+ },
+ },
+ },
+ }, issues)
+
+ // Testing comments under issue tickets
+ comments, _, err := downloader.GetComments(issues[0])
+ require.NoError(t, err)
+ assertCommentsEqual(t, []*base.Comment{
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.October, 13, 4, 3, 30, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue tagged with: cccc, dddd",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.October, 13, 4, 6, 4, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "The is the first comment under the second test issue",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.October, 13, 4, 6, 16, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "The is the second comment under the second test issue",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.October, 13, 4, 7, 12, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.May, 8, 4, 50, 21, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 22, 52, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone BBBB)\n- Issue status updated to: Open (was: Closed)",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 23, 0o2, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Complete\n- Issue status updated to: Closed (was: Open)",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 24, 34, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB",
+ },
+ {
+ IssueIndex: 2,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 25, 57, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Rare",
+ },
+ }, comments)
+
+ prs, isEnd, err := downloader.GetPullRequests(1, 20)
+ require.NoError(t, err)
+ assert.True(t, isEnd)
+
+ // Testing pull requests migrated
+ assertPullRequestsEqual(t, []*base.PullRequest{
+ {
+ Number: 10,
+ Title: "Change the branch identity to `test-ffff` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Created: time.Date(2025, time.May, 19, 6, 12, 45, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC),
+ Closed: timePtr(time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC)),
+ MergedTime: timePtr(time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC)),
+ Merged: true,
+ Labels: []*base.Label{
+ {
+ Name: "ffff",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-ffff",
+ SHA: "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ {
+ Number: 9,
+ Title: "Change the branch identity to `test-eeee` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Created: time.Date(2025, time.May, 19, 6, 12, 41, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC),
+ Closed: timePtr(time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC)),
+ MergedTime: timePtr(time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC)),
+ Merged: true,
+ Labels: []*base.Label{
+ {
+ Name: "eeee",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-eeee",
+ SHA: "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ {
+ Number: 8,
+ Title: "Change the branch identity to `test-dddd` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Created: time.Date(2025, time.May, 5, 6, 45, 32, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC),
+ Closed: timePtr(time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC)),
+ MergedTime: timePtr(time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC)), // THIS IS WRONG
+ Merged: false,
+ Labels: []*base.Label{
+ {
+ Name: "dddd",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-dddd",
+ SHA: "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ {
+ Number: 7,
+ Title: "Change the branch identity to `test-cccc` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Created: time.Date(2025, time.May, 5, 6, 45, 6, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC), // IT SHOULD BE NIL
+ Closed: timePtr(time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC)), // IT is CLOSED, Not MERGED so SHOULD NOT BE NIL
+ MergedTime: timePtr(time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC)), // THIS IS WRONG
+ Merged: false,
+ Labels: []*base.Label{
+ {
+ Name: "cccc",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-cccc",
+ SHA: "f1246e331cade9341b9e4f311b7a134f99893d21",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ {
+ Number: 6,
+ Title: "Change the branch identity to `test-bbbb` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "open",
+ Created: time.Date(2025, time.May, 5, 6, 44, 30, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 19, 8, 30, 50, 0, time.UTC),
+ Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ MergedTime: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ Merged: false,
+ Labels: []*base.Label{
+ {
+ Name: "bbbb",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-bbbb",
+ SHA: "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ {
+ Number: 5,
+ Title: "Change the branch identity to `test-aaaa` in the README.md file",
+ Content: "Signed-off-by: Akashdeep Dhar ",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "open",
+ Created: time.Date(2025, time.May, 5, 6, 43, 57, 0, time.UTC),
+ Updated: time.Date(2025, time.May, 19, 6, 29, 45, 0, time.UTC),
+ Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ MergedTime: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ Merged: false,
+ Labels: []*base.Label{
+ {
+ Name: "aaaa",
+ },
+ },
+ Head: base.PullRequestBranch{
+ Ref: "test-aaaa",
+ SHA: "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ RepoName: "protop2g-test-srce",
+ },
+ Base: base.PullRequestBranch{
+ Ref: "main",
+ SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7",
+ RepoName: "protop2g-test-srce",
+ },
+ },
+ }, prs)
+
+ // Testing comments under pull requests
+ comments, _, err = downloader.GetComments(prs[5])
+ require.NoError(t, err)
+ assertCommentsEqual(t, []*base.Comment{
+ {
+ IssueIndex: 5,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.May, 5, 6, 44, 13, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa",
+ },
+ {
+ IssueIndex: 5,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.May, 7, 5, 25, 21, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "This is the first comment under this pull request.",
+ },
+ {
+ IssueIndex: 5,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.May, 7, 5, 25, 29, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "This is the second comment under this pull request.",
+ },
+ }, comments)
+
+ // Testing milestones migration
+ milestones, err := downloader.GetMilestones()
+ require.NoError(t, err)
+ dict := map[string]*base.Milestone{
+ "Milestone AAAA": {
+ Title: "Milestone AAAA",
+ Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)),
+ State: "open",
+ },
+ "Milestone BBBB": {
+ Title: "Milestone BBBB",
+ Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)),
+ State: "closed",
+ },
+ "Milestone CCCC": {
+ Title: "Milestone CCCC",
+ Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)),
+ State: "open",
+ },
+ "Milestone DDDD": {
+ Title: "Milestone DDDD",
+ Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)),
+ State: "closed",
+ },
+ }
+
+ // We do not like when tests fail just because of dissimilar ordering
+ for _, item := range milestones {
+ assertMilestoneEqual(t, item, dict[item.Title])
+ }
+}
+
+func TestPagureDownloadRepoWithPrivateIssues(t *testing.T) {
+ // Skip tests if Pagure token is not found
+ cloneUser := os.Getenv("PAGURE_CLONE_USER")
+ clonePassword := os.Getenv("PAGURE_CLONE_PASSWORD")
+ apiUser := os.Getenv("PAGURE_API_USER")
+ apiPassword := os.Getenv("PAGURE_API_TOKEN")
+ if apiUser == "" || apiPassword == "" {
+ t.Skip("skipped test because a PAGURE_ variable was not in the environment")
+ }
+
+ cloneAddr := fmt.Sprintf("https://%s:%s@pagure.io/protop2g-test-srce.git", cloneUser, clonePassword)
+ u, _ := url.Parse(cloneAddr)
+
+ fixtPath := "./testdata/pagure/full_download/authorized"
+ server := unittest.NewMockWebServer(t, "https://pagure.io", fixtPath, false)
+ defer server.Close()
+
+ factory := &PagureDownloaderFactory{}
+ downloader, err := factory.New(t.Context(), base.MigrateOptions{
+ CloneAddr: u.String(),
+ AuthUsername: apiUser,
+ AuthPassword: apiPassword,
+ AuthToken: apiPassword,
+ })
+ require.NoError(t, err)
+
+ repo, err := downloader.GetRepoInfo()
+ require.NoError(t, err)
+
+ // Testing repository contents migration
+ assertRepositoryEqual(t, &base.Repository{
+ Name: "protop2g-test-srce",
+ Owner: "",
+ Description: "The source namespace for the Pagure Exporter project to run tests against",
+ CloneURL: cloneAddr,
+ OriginalURL: strings.ReplaceAll(cloneAddr, ".git", ""),
+ }, repo)
+
+ topics, err := downloader.GetTopics()
+ require.NoError(t, err)
+
+ // Testing repository topics migration
+ assert.Equal(t, []string{"srce", "test", "gridhead", "protop2g"}, topics)
+
+ // Testing labels migration
+ labels, err := downloader.GetLabels()
+ require.NoError(t, err)
+ assert.Len(t, labels, 15)
+
+ // Testing issue tickets probing
+ issues, isEnd, err := downloader.GetIssues(1, 20)
+ require.NoError(t, err)
+ assert.True(t, isEnd)
+
+ // Testing issue tickets migration
+ assertIssuesEqual(t, []*base.Issue{
+ {
+ Number: 4,
+ Title: "This is the title of the fourth test issue",
+ Content: "This is the body of the fourth test issue",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "closed",
+ Milestone: "Milestone DDDD",
+ Created: time.Date(2023, time.November, 21, 8, 6, 56, 0, time.UTC),
+ Updated: time.Date(2025, time.June, 25, 6, 26, 26, 0, time.UTC),
+ Closed: timePtr(time.Date(2025, time.June, 25, 6, 23, 51, 0, time.UTC)),
+ Labels: []*base.Label{
+ {
+ Name: "gggg",
+ },
+ {
+ Name: "hhhh",
+ },
+ {
+ Name: "Closed As/Baseless",
+ },
+ {
+ Name: "Priority/Common",
+ },
+ },
+ },
+ {
+ Number: 3,
+ Title: "This is the title of the third test issue",
+ Content: "This is the body of the third test issue",
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ State: "open",
+ Milestone: "Milestone CCCC",
+ Created: time.Date(2023, time.November, 21, 8, 3, 57, 0, time.UTC),
+ Updated: time.Date(2025, time.June, 25, 6, 26, 7, 0, time.UTC),
+ Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)),
+ Labels: []*base.Label{
+ {
+ Name: "eeee",
+ },
+ {
+ Name: "ffff",
+ },
+ {
+ Name: "Priority/Uncommon",
+ },
+ },
+ },
+ }, issues)
+
+ // Testing comments under issue tickets
+ comments, _, err := downloader.GetComments(issues[0])
+ require.NoError(t, err)
+ assertCommentsEqual(t, []*base.Comment{
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.November, 21, 8, 7, 25, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "This is the first comment under the fourth test issue",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.November, 21, 8, 7, 34, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "This is the second comment under the fourth test issue",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2023, time.November, 21, 8, 8, 1, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.May, 8, 4, 50, 46, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 23, 46, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone DDDD)\n- Issue status updated to: Open (was: Closed)",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 23, 52, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Baseless\n- Issue status updated to: Closed (was: Open)",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 24, 55, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD",
+ },
+ {
+ IssueIndex: 4,
+ PosterName: "t0xic0der",
+ PosterID: -1,
+ Created: time.Date(2025, time.June, 25, 6, 26, 26, 0, time.UTC),
+ Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
+ Content: "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Common",
+ },
+ }, comments)
+}
diff --git a/services/migrations/restore.go b/services/migrations/restore.go
index e8725bc647..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"
)
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/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce
new file mode 100644
index 0000000000..5f569b145c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce
@@ -0,0 +1,85 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:51:21 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 1464
+content-security-policy: default-src 'self';script-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; style-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmI4NGRmYWMyY2Q2MzVlOWRkOTQwMGYwNzk2NzdlOGQxN2UxYTJlMCJ9.G2IX6Q.ETAG_sYgE1V9xYQNy_CD4HOP32Q; Expires=Sat, 23-Aug-2025 06:51:21 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo
new file mode 100644
index 0000000000..e31c4193e0
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:44:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 452
+content-security-policy: default-src 'self';script-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; style-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2EzN2Y5MTI1ZjkxNTkzMDViMWQ5ZGVhYjkyNzVlZTkwNmVjYzgzYiJ9.G2IWRw.1ZmhWySKFk3Lw_bA-EwmcNCgcz0; Expires=Sat, 23-Aug-2025 06:44:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1747635011,
+ "commit_time_offset": 0,
+ "committer": "Akashdeep Dhar",
+ "hash": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "message": "Change the branch identity to `test-eeee` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "1e8fa9a17b4b4ddde50f334626b0a5497070cff7"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo
new file mode 100644
index 0000000000..722b59a6dd
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:44:57 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; style-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjM5ZTYyYWM5NDViYTYwMjg1MjFlMTAzNjU4OWQ3Zjc1NjA5ZmUzMSJ9.G2IWaQ._b9edyW_4DSX-umHlyVib496s00; Expires=Sat, 23-Aug-2025 06:44:57 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169509,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "message": "Change the branch identity to `test-dddd` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "586a1e8a79e572691dc086ef7bf4e2f6d34c5254"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo
new file mode 100644
index 0000000000..8d3fb6f5f2
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:43:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 452
+content-security-policy: default-src 'self';script-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; style-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNDRkYmIwMjQwODI1ODc4MGUzYWM4ZDZhZTkxYWQ2NTkyMDFhNTg0ZSJ9.G2IWIw.U2Rb6xUm4Wk9ODweB3hH1cggWkM; Expires=Sat, 23-Aug-2025 06:43:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1747635352,
+ "commit_time_offset": 0,
+ "committer": "Akashdeep Dhar",
+ "hash": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "message": "Change the branch identity to `test-ffff` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "01b420e2964928a15f790f9b7c1a0053e7b5f0a5"
+ ],
+ "tree_id": "0c5e64a6b912cb0c3d66e66896fa98a98da69fe4"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo
new file mode 100644
index 0000000000..852a4b724d
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:46:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; style-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYTE0Y2EzMTZiMWRkZGZjOTJjMjkwZTkyNjM5OWEzYWZiNjA0OTZiMCJ9.G2IWvw.SNBn8NelY5GBQ-8SmLY-Uwy-uq0; Expires=Sat, 23-Aug-2025 06:46:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169040,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "message": "Change the branch identity to `test-bbbb` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "7a23fc15f5a1463f2c425f0146def7c19ecf6c88"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo
new file mode 100644
index 0000000000..a35fb1f416
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:46:59 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; style-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmY2MjEyYzNmMDkzMmZkZDMxYjYwYzVmMDdjYzEzY2JhZTJkMjVmZCJ9.G2IW4w.JCI6ih36NlW5IPwNX1LaCbb6v5U; Expires=Sat, 23-Aug-2025 06:46:59 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169185,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "message": "Change the branch identity to `test-aaaa` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "e7911825d29e73f260de95d5070898ccbe6340a6"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo
new file mode 100644
index 0000000000..8e161ce97c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:45:31 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; style-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDNmYWUzNTBlOWZkNDY0MTJlOWNkMjQ0M2FjZmYyY2VkZjBlY2ZiYSJ9.G2IWjA.co1poP1T_e07frtYRBAkxcp2RTM; Expires=Sat, 23-Aug-2025 06:45:32 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169354,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "message": "Change the branch identity to `test-cccc` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "f1e37736d409bb136451dfc8e3a2017d3af2ce7d"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3
new file mode 100644
index 0000000000..d9b5d17073
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3
@@ -0,0 +1,17 @@
+HTTP/2 404
+date: Wed, 23 Jul 2025 06:17:46 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 233
+content-security-policy: default-src 'self';script-src 'self' 'nonce-AyzMgRLEDXIM7Oiv1cbBRx6WC'; style-src 'self' 'nonce-AyzMgRLEDXIM7Oiv1cbBRx6WC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiMGMxMmJkYmFmNWY1ODBiNzQxY2Q2Y2RiODk1OTRmOWMwOGY1OWM2ZSJ9.G2IQCg.BSeYpRe9-FITZnjNS1EKHjwOFmM; Expires=Sat, 23-Aug-2025 06:17:46 GMT; Secure; HttpOnly; Path=/
+content-type: text/html; charset=UTF-8
+
+
+404 Not Found
+Not Found
+The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4
new file mode 100644
index 0000000000..6aa95bba6a
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4
@@ -0,0 +1,17 @@
+HTTP/2 404
+date: Wed, 23 Jul 2025 06:17:58 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 233
+content-security-policy: default-src 'self';script-src 'self' 'nonce-F7TmwHL5oVhCcN5Mddi8OGms7'; style-src 'self' 'nonce-F7TmwHL5oVhCcN5Mddi8OGms7'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTYwOWIyN2EzYjA4NmE5OTZhNjk3NWU5ZWM4OTI1MWNkN2U5OWYyMiJ9.G2IQFg.qReOtqa1f6EAhBqYOj_aQRCvgac; Expires=Sat, 23-Aug-2025 06:17:58 GMT; Secure; HttpOnly; Path=/
+content-type: text/html; charset=UTF-8
+
+
+404 Not Found
+Not Found
+The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues
new file mode 100644
index 0000000000..84a9e44c35
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues
@@ -0,0 +1,299 @@
+HTTP/2 200
+date: Wed, 30 Jul 2025 05:15:59 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 18702
+content-security-policy: default-src 'self';script-src 'self' 'nonce-SA5GWMgMPH5tNiDy97SIr6jxM'; style-src 'self' 'nonce-SA5GWMgMPH5tNiDy97SIr6jxM'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTA1NjdkYzFjN2QyMjFhMGU3NTVmN2ZlMDYzZTgyMTQ0ZTM3NzFkNiJ9.G2s8Dw.36fFhcCc63_kPkxcJp8tz-ufC8Y; Expires=Sat, 30-Aug-2025 05:15:59 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "args": {
+ "assignee": null,
+ "author": null,
+ "milestones": [],
+ "no_stones": null,
+ "order": null,
+ "priority": null,
+ "since": null,
+ "status": "all",
+ "tags": []
+ },
+ "issues": [
+ {
+ "assignee": null,
+ "blocks": [],
+ "close_status": "Baseless",
+ "closed_at": "1750832631",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "This is the first comment under the fourth test issue",
+ "date_created": "1700554045",
+ "edited_on": null,
+ "editor": null,
+ "id": 885231,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under the fourth test issue",
+ "date_created": "1700554054",
+ "edited_on": null,
+ "editor": null,
+ "id": 885232,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)",
+ "date_created": "1700554081",
+ "edited_on": null,
+ "editor": null,
+ "id": 885233,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD",
+ "date_created": "1746679846",
+ "edited_on": null,
+ "editor": null,
+ "id": 971680,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone DDDD)\n- Issue status updated to: Open (was: Closed)",
+ "date_created": "1750832626",
+ "edited_on": null,
+ "editor": null,
+ "id": 976715,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Baseless\n- Issue status updated to: Closed (was: Open)",
+ "date_created": "1750832632",
+ "edited_on": null,
+ "editor": null,
+ "id": 976716,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD",
+ "date_created": "1750832695",
+ "edited_on": null,
+ "editor": null,
+ "id": 976718,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Common",
+ "date_created": "1750832786",
+ "edited_on": null,
+ "editor": null,
+ "id": 976722,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "content": "This is the body of the fourth test issue",
+ "custom_fields": [],
+ "date_created": "1700554016",
+ "depends": [],
+ "full_url": "https://pagure.io/protop2g-test-srce/issue/4",
+ "id": 4,
+ "last_updated": "1750832786",
+ "milestone": "Milestone DDDD",
+ "priority": 1,
+ "private": true,
+ "related_prs": [],
+ "status": "Closed",
+ "tags": [
+ "gggg",
+ "hhhh"
+ ],
+ "title": "This is the title of the fourth test issue",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "assignee": null,
+ "blocks": [],
+ "close_status": null,
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "This is the first comment under the third test issue",
+ "date_created": "1700553880",
+ "edited_on": null,
+ "editor": null,
+ "id": 885229,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under the third test issue",
+ "date_created": "1700553892",
+ "edited_on": null,
+ "editor": null,
+ "id": 885230,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone CCCC",
+ "date_created": "1746679833",
+ "edited_on": null,
+ "editor": null,
+ "id": 971679,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Uncommon",
+ "date_created": "1750832767",
+ "edited_on": null,
+ "editor": null,
+ "id": 976721,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "content": "This is the body of the third test issue",
+ "custom_fields": [],
+ "date_created": "1700553837",
+ "depends": [],
+ "full_url": "https://pagure.io/protop2g-test-srce/issue/3",
+ "id": 3,
+ "last_updated": "1750832767",
+ "milestone": "Milestone CCCC",
+ "priority": 2,
+ "private": true,
+ "related_prs": [],
+ "status": "Open",
+ "tags": [
+ "eeee",
+ "ffff"
+ ],
+ "title": "This is the title of the third test issue",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ ],
+ "pagination": {
+ "first": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1",
+ "last": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1",
+ "next": null,
+ "page": 1,
+ "pages": 1,
+ "per_page": 20,
+ "prev": null
+ },
+ "total_issues": 2
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10
new file mode 100644
index 0000000000..e43f97a590
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10
@@ -0,0 +1,259 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:21:04 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 6179
+content-security-policy: default-src 'self';script-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; style-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTQ3NGFiNjdkNjdlZjNiNWNmOWYwZGFmODg1MDMxMTYzNTc5OTkxMyJ9.G2IQ0A.97bsQ3k_CAN3UOID130UESba38M; Expires=Sat, 23-Aug-2025 06:21:04 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "branch": "main",
+ "branch_from": "test-ffff",
+ "cached_merge_status": "unknown",
+ "closed_at": "1747635431",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: ffff\n- Request assigned",
+ "commit": null,
+ "date_created": "1747635211",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219623,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "rebased onto 01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "commit": null,
+ "date_created": "1747635389",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219625,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been merged by t0xic0der",
+ "commit": null,
+ "date_created": "1747635431",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219626,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "commit_stop": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "date_created": "1747635165",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/10",
+ "id": 10,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747635431",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Merged",
+ "tags": [
+ "ffff"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-ffff` in the README.md file",
+ "uid": "d3b7100abf8b4b02aa220d899e063295",
+ "updated_on": "1747635431",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5
new file mode 100644
index 0000000000..be1c0e3a25
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5
@@ -0,0 +1,249 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:19:26 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5859
+content-security-policy: default-src 'self';script-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; style-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODFiMTQzZWQwZGEzMWIxNmE3NTk1ODYyNWE0MGZjNjcwNThmMDc3ZSJ9.G2IQbg.9qdYqcrGIqnCKtjsWYjcfMCTzok; Expires=Sat, 23-Aug-2025 06:19:26 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-aaaa",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa",
+ "commit": null,
+ "date_created": "1746427453",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219086,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595521",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219190,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595529",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219191,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "date_created": "1746427437",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5",
+ "id": 5,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747636185",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "aaaa"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-aaaa` in the README.md file",
+ "uid": "f9e737c5ccc1434e9798cfd49d192538",
+ "updated_on": "1746595529",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6
new file mode 100644
index 0000000000..878989a6cd
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6
@@ -0,0 +1,249 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:19:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5859
+content-security-policy: default-src 'self';script-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; style-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNWY5OWMwYjViOTkzZjM0YTY3OTJiYTZmNDk4YzY3MzUwMzY3MzY2OCJ9.G2IQgw.dxyk0aB89uDJGN15BTWrZEvCFWg; Expires=Sat, 23-Aug-2025 06:19:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-bbbb",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb",
+ "commit": null,
+ "date_created": "1746427480",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219087,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595539",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219192,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595552",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219193,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "date_created": "1746427470",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6",
+ "id": 6,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747643450",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "bbbb"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-bbbb` in the README.md file",
+ "uid": "9cab89d6bb8c499e8fcb47926f1f5806",
+ "updated_on": "1746595552",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7
new file mode 100644
index 0000000000..51c269273c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7
@@ -0,0 +1,234 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:00 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5458
+content-security-policy: default-src 'self';script-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; style-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjUwNDcwNWQ4Y2UxZTRjODRlNjQxYTA0NzVlNDA5NGQwNzI4YTY1NyJ9.G2IQkA.KtxZfzau2wOwMTyfG_xNBHTMHSI; Expires=Sat, 23-Aug-2025 06:20:00 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-cccc",
+ "cached_merge_status": "FFORWARD",
+ "closed_at": "1746428043",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: cccc",
+ "commit": null,
+ "date_created": "1746427513",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219088,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been closed by t0xic0der",
+ "commit": null,
+ "date_created": "1746428043",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219090,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "commit_stop": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "date_created": "1746427506",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/7",
+ "id": 7,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1746428043",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Closed",
+ "tags": [
+ "cccc"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-cccc` in the README.md file",
+ "uid": "f696feab56b84557b4d4a8a4462420ee",
+ "updated_on": "1746428043",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8
new file mode 100644
index 0000000000..45ae72d6e8
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8
@@ -0,0 +1,234 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:17 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5458
+content-security-policy: default-src 'self';script-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; style-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2I3NWQwYzBhY2YxMjhmYTU0YmU5NzEzYzcyNjNjZTQ4NjFlNDE5ZCJ9.G2IQoQ.Ds29JFMXbO3q2kBPxmfx2kQ7YjA; Expires=Sat, 23-Aug-2025 06:20:17 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-dddd",
+ "cached_merge_status": "FFORWARD",
+ "closed_at": "1746428053",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: dddd",
+ "commit": null,
+ "date_created": "1746427540",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219089,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been closed by t0xic0der",
+ "commit": null,
+ "date_created": "1746428053",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219091,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "commit_stop": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "date_created": "1746427532",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/8",
+ "id": 8,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1746428053",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Closed",
+ "tags": [
+ "dddd"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-dddd` in the README.md file",
+ "uid": "493b294044fd48e18f424210c919d8de",
+ "updated_on": "1746428053",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9
new file mode 100644
index 0000000000..2e19157948
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9
@@ -0,0 +1,239 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:51 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5628
+content-security-policy: default-src 'self';script-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; style-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2MyYzdlY2E0MDYzNWRmZjRhMDc2MGE0OTg2MjNhMTU4MWNhZDg4OSJ9.G2IQww.VZpiXbZftgigHkiS15g8DF6iQSY; Expires=Sat, 23-Aug-2025 06:20:51 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "branch": "main",
+ "branch_from": "test-eeee",
+ "cached_merge_status": "unknown",
+ "closed_at": "1747635243",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: eeee\n- Request assigned",
+ "commit": null,
+ "date_created": "1747635200",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219622,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been merged by t0xic0der",
+ "commit": null,
+ "date_created": "1747635243",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219624,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "commit_stop": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "date_created": "1747635161",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/9",
+ "id": 9,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747635243",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Merged",
+ "tags": [
+ "eeee"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-eeee` in the README.md file",
+ "uid": "f2ad806e430a40bd8ee5894484338df4",
+ "updated_on": "1747635243",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests
new file mode 100644
index 0000000000..15b69e8f03
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests
@@ -0,0 +1,507 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:18:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 14090
+content-security-policy: default-src 'self';script-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; style-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODAwYWYyZGI3MjZlMzA2ZTdmNTdlMmIwNGVkNmU3YTBmNDYwZDUyMyJ9.G2IQRw.EDXBH36zsKcHKDETH_g7miO_r_w; Expires=Sat, 23-Aug-2025 06:18:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "args": {
+ "assignee": null,
+ "author": null,
+ "page": 1,
+ "per_page": 20,
+ "status": true,
+ "tags": []
+ },
+ "pagination": {
+ "first": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1",
+ "last": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1",
+ "next": null,
+ "page": 1,
+ "pages": 1,
+ "per_page": 20,
+ "prev": null
+ },
+ "requests": [
+ {
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-bbbb",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb",
+ "commit": null,
+ "date_created": "1746427480",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219087,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595539",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219192,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595552",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219193,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "date_created": "1746427470",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6",
+ "id": 6,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747643450",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "bbbb"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-bbbb` in the README.md file",
+ "uid": "9cab89d6bb8c499e8fcb47926f1f5806",
+ "updated_on": "1746595552",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-aaaa",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa",
+ "commit": null,
+ "date_created": "1746427453",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219086,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595521",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219190,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595529",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219191,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "date_created": "1746427437",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5",
+ "id": 5,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747636185",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "aaaa"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-aaaa` in the README.md file",
+ "uid": "f9e737c5ccc1434e9798cfd49d192538",
+ "updated_on": "1746595529",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "total_requests": 2
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa
new file mode 100644
index 0000000000..d0f24e646e
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:34:39 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; style-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZmJhYTlmMTAzYzYxMWE5NmZiZDNkZjE1ZmRiZGQzYTA5YTQ1YTM2YyJ9.G2IF7w.CLP1edDfZSoJcXjoUNt40swcBV8; Expires=Sat, 23-Aug-2025 05:34:39 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "aaaa",
+ "tag_color": "#ff0000",
+ "tag_description": "aaaa"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb
new file mode 100644
index 0000000000..a859096f25
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:34:50 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; style-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZWU4ZDUxYzY4NmUzMzM4ODg4YmRmMjQwYmU3ODVhOTA4MzQ4N2Q2NiJ9.G2IF-g.nG1k1zU4b9Eo9WFGCas8R9a5-Vg; Expires=Sat, 23-Aug-2025 05:34:50 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "bbbb",
+ "tag_color": "#ff0000",
+ "tag_description": "bbbb"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc
new file mode 100644
index 0000000000..c4fdc9aa46
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:35:02 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; style-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYjhhM2YxNjJhZDYwYWU5NzE4NmQ4NDBkMzJlOWNkYmYxN2Y2NjBmMyJ9.G2IGBg.oA7d9DJHW4_ilpbiVkbveF5dM3Q; Expires=Sat, 23-Aug-2025 05:35:02 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "cccc",
+ "tag_color": "#ffff00",
+ "tag_description": "cccc"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd
new file mode 100644
index 0000000000..db9ad09a74
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; style-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2VhZjRhOTlkZWUxZDcxODg2NWIyN2JhZGY4ZjUyMjcxZTdkZGU0MyJ9.G2IGVw.JCK3tXfD0aOgDdIAMv5MlFkl-SY; Expires=Sat, 23-Aug-2025 05:36:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "dddd",
+ "tag_color": "#ffff00",
+ "tag_description": "dddd"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee
new file mode 100644
index 0000000000..eac0faf464
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:34 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; style-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGEyNDUwYzk5MDY2ZjJhZGQyNDhlZmZmM2QxN2UxZTM0ODI2NWFhZiJ9.G2IGYg.hM6ZKEPDXtOvTWlSPeQBLiZjCO4; Expires=Sat, 23-Aug-2025 05:36:34 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "eeee",
+ "tag_color": "#00ff00",
+ "tag_description": "eeee"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff
new file mode 100644
index 0000000000..af5935aa46
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:48 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; style-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYWMyNjBhNjE5MjI0OTQ0YTU2Yzc5YjNmNzU3ZTU2MTYzZGQwMGMwNSJ9.G2IGcQ.22KbaZBjPxJkpIoTBIn1UtVzEjI; Expires=Sat, 23-Aug-2025 05:36:49 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "ffff",
+ "tag_color": "#00ff00",
+ "tag_description": "ffff"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg
new file mode 100644
index 0000000000..28fd0ef15d
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:37:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; style-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGExMmI2MGZhYTQyYTY5MmQ0MzNkNWFlZTU0YjE4M2NjY2NmYjI3MCJ9.G2IGkw.9-wL70lPAOlpIv8cusGLLA0Np_U; Expires=Sat, 23-Aug-2025 05:37:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "gggg",
+ "tag_color": "#0000ff",
+ "tag_description": "gggg"
+}
diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags
new file mode 100644
index 0000000000..1176b532a4
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags
@@ -0,0 +1,26 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:16:48 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 142
+content-security-policy: default-src 'self';script-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; style-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmQ2NTQ1MmFhZWJjZjA0OWIxNTI5MjBjODQyYzUzOGRkYmYyYTkwOSJ9.G2IBwA.pKqOBVDJrtKfyrbJPyuaRfFjnR4; Expires=Sat, 23-Aug-2025 05:16:48 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tags": [
+ "aaaa",
+ "bbbb",
+ "cccc",
+ "dddd",
+ "eeee",
+ "ffff",
+ "gggg",
+ "hhhh"
+ ],
+ "total_tags": 8
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce
new file mode 100644
index 0000000000..5f569b145c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce
@@ -0,0 +1,85 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:51:21 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 1464
+content-security-policy: default-src 'self';script-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; style-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmI4NGRmYWMyY2Q2MzVlOWRkOTQwMGYwNzk2NzdlOGQxN2UxYTJlMCJ9.G2IX6Q.ETAG_sYgE1V9xYQNy_CD4HOP32Q; Expires=Sat, 23-Aug-2025 06:51:21 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo
new file mode 100644
index 0000000000..e31c4193e0
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:44:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 452
+content-security-policy: default-src 'self';script-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; style-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2EzN2Y5MTI1ZjkxNTkzMDViMWQ5ZGVhYjkyNzVlZTkwNmVjYzgzYiJ9.G2IWRw.1ZmhWySKFk3Lw_bA-EwmcNCgcz0; Expires=Sat, 23-Aug-2025 06:44:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1747635011,
+ "commit_time_offset": 0,
+ "committer": "Akashdeep Dhar",
+ "hash": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "message": "Change the branch identity to `test-eeee` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "1e8fa9a17b4b4ddde50f334626b0a5497070cff7"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo
new file mode 100644
index 0000000000..722b59a6dd
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:44:57 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; style-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjM5ZTYyYWM5NDViYTYwMjg1MjFlMTAzNjU4OWQ3Zjc1NjA5ZmUzMSJ9.G2IWaQ._b9edyW_4DSX-umHlyVib496s00; Expires=Sat, 23-Aug-2025 06:44:57 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169509,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "message": "Change the branch identity to `test-dddd` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "586a1e8a79e572691dc086ef7bf4e2f6d34c5254"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo
new file mode 100644
index 0000000000..8d3fb6f5f2
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:43:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 452
+content-security-policy: default-src 'self';script-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; style-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNDRkYmIwMjQwODI1ODc4MGUzYWM4ZDZhZTkxYWQ2NTkyMDFhNTg0ZSJ9.G2IWIw.U2Rb6xUm4Wk9ODweB3hH1cggWkM; Expires=Sat, 23-Aug-2025 06:43:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1747635352,
+ "commit_time_offset": 0,
+ "committer": "Akashdeep Dhar",
+ "hash": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "message": "Change the branch identity to `test-ffff` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "01b420e2964928a15f790f9b7c1a0053e7b5f0a5"
+ ],
+ "tree_id": "0c5e64a6b912cb0c3d66e66896fa98a98da69fe4"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo
new file mode 100644
index 0000000000..852a4b724d
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:46:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; style-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYTE0Y2EzMTZiMWRkZGZjOTJjMjkwZTkyNjM5OWEzYWZiNjA0OTZiMCJ9.G2IWvw.SNBn8NelY5GBQ-8SmLY-Uwy-uq0; Expires=Sat, 23-Aug-2025 06:46:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169040,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "message": "Change the branch identity to `test-bbbb` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "7a23fc15f5a1463f2c425f0146def7c19ecf6c88"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo
new file mode 100644
index 0000000000..a35fb1f416
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:46:59 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; style-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmY2MjEyYzNmMDkzMmZkZDMxYjYwYzVmMDdjYzEzY2JhZTJkMjVmZCJ9.G2IW4w.JCI6ih36NlW5IPwNX1LaCbb6v5U; Expires=Sat, 23-Aug-2025 06:46:59 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169185,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "message": "Change the branch identity to `test-aaaa` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "e7911825d29e73f260de95d5070898ccbe6340a6"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo
new file mode 100644
index 0000000000..8e161ce97c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo
@@ -0,0 +1,25 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:45:31 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 454
+content-security-policy: default-src 'self';script-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; style-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDNmYWUzNTBlOWZkNDY0MTJlOWNkMjQ0M2FjZmYyY2VkZjBlY2ZiYSJ9.G2IWjA.co1poP1T_e07frtYRBAkxcp2RTM; Expires=Sat, 23-Aug-2025 06:45:32 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "author": "Akashdeep Dhar",
+ "commit_time": 1697169354,
+ "commit_time_offset": 330,
+ "committer": "Akashdeep Dhar",
+ "hash": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "message": "Change the branch identity to `test-cccc` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n",
+ "parent_ids": [
+ "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7"
+ ],
+ "tree_id": "f1e37736d409bb136451dfc8e3a2017d3af2ce7d"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1
new file mode 100644
index 0000000000..ab96eec4a3
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1
@@ -0,0 +1,125 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:17:07 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 3105
+content-security-policy: default-src 'self';script-src 'self' 'nonce-yBIiXAYKuVAlrSnvYuxYU2MVh'; style-src 'self' 'nonce-yBIiXAYKuVAlrSnvYuxYU2MVh'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDQ4YWUxNzRmZGRjNDE3MGE3Zjg4YTY2OWJmMzI4MTA3OGQ0NDJmNyJ9.G2IP4w.fBfDUgO0_qt8TTh1QGvCxyEoNw8; Expires=Sat, 23-Aug-2025 06:17:07 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "blocks": [],
+ "close_status": null,
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: aaaa, bbbb",
+ "date_created": "1697169699",
+ "edited_on": null,
+ "editor": null,
+ "id": 878471,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under the first test issue",
+ "date_created": "1697169880",
+ "edited_on": null,
+ "editor": null,
+ "id": 878473,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under the first test issue",
+ "date_created": "1697169924",
+ "edited_on": null,
+ "editor": null,
+ "id": 878474,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone AAAA",
+ "date_created": "1746679806",
+ "edited_on": null,
+ "editor": null,
+ "id": 971677,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Epic",
+ "date_created": "1750832745",
+ "edited_on": null,
+ "editor": null,
+ "id": 976719,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "content": "This is the body of the first test issue",
+ "custom_fields": [],
+ "date_created": "1697169462",
+ "depends": [],
+ "full_url": "https://pagure.io/protop2g-test-srce/issue/1",
+ "id": 1,
+ "last_updated": "1750832745",
+ "milestone": "Milestone AAAA",
+ "priority": 4,
+ "private": false,
+ "related_prs": [],
+ "status": "Open",
+ "tags": [
+ "aaaa",
+ "bbbb"
+ ],
+ "title": "This is the title of the first test issue",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2
new file mode 100644
index 0000000000..7488423491
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2
@@ -0,0 +1,17 @@
+HTTP/2 404
+date: Wed, 23 Jul 2025 06:17:34 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 233
+content-security-policy: default-src 'self';script-src 'self' 'nonce-sIVvSKRvcU00FQYaXHjgZGoqv'; style-src 'self' 'nonce-sIVvSKRvcU00FQYaXHjgZGoqv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTE1NmEzNWM2ZWU4ZGI1M2U5MTFlOGIzN2UzYzRkNzcxNDZlMjk1MCJ9.G2IP_g.YZx_ll-7EyLvtel9UcO0uQAoujM; Expires=Sat, 23-Aug-2025 06:17:34 GMT; Secure; HttpOnly; Path=/
+content-type: text/html; charset=UTF-8
+
+
+404 Not Found
+Not Found
+The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues
new file mode 100644
index 0000000000..4f14350af2
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues
@@ -0,0 +1,331 @@
+HTTP/2 200
+date: Wed, 30 Jul 2025 05:17:03 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 10166
+content-security-policy: default-src 'self';script-src 'self' 'nonce-rxZTVpKgLDOgYu1TX1ab1qR2p'; style-src 'self' 'nonce-rxZTVpKgLDOgYu1TX1ab1qR2p'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGRkN2YyNjZiMGQ5YmM4Mjc3YjllYjkzMjUyNjZlMmQ5OTExMWI2NCJ9.G2s8UA.oQa5peejoMGwTzJF3vWHgcS8naQ; Expires=Sat, 30-Aug-2025 05:17:04 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "args": {
+ "assignee": null,
+ "author": null,
+ "milestones": [],
+ "no_stones": null,
+ "order": null,
+ "priority": null,
+ "since": null,
+ "status": "all",
+ "tags": []
+ },
+ "issues": [
+ {
+ "assignee": null,
+ "blocks": [],
+ "close_status": "Complete",
+ "closed_at": "1750832579",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: cccc, dddd",
+ "date_created": "1697169810",
+ "edited_on": null,
+ "editor": null,
+ "id": 878472,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "The is the first comment under the second test issue",
+ "date_created": "1697169964",
+ "edited_on": null,
+ "editor": null,
+ "id": 878475,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "The is the second comment under the second test issue",
+ "date_created": "1697169976",
+ "edited_on": null,
+ "editor": null,
+ "id": 878476,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)",
+ "date_created": "1697170032",
+ "edited_on": null,
+ "editor": null,
+ "id": 878477,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB",
+ "date_created": "1746679821",
+ "edited_on": null,
+ "editor": null,
+ "id": 971678,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone BBBB)\n- Issue status updated to: Open (was: Closed)",
+ "date_created": "1750832572",
+ "edited_on": null,
+ "editor": null,
+ "id": 976713,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Complete\n- Issue status updated to: Closed (was: Open)",
+ "date_created": "1750832582",
+ "edited_on": null,
+ "editor": null,
+ "id": 976714,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB",
+ "date_created": "1750832674",
+ "edited_on": null,
+ "editor": null,
+ "id": 976717,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Rare",
+ "date_created": "1750832757",
+ "edited_on": null,
+ "editor": null,
+ "id": 976720,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "content": "This is the body of the second test issue",
+ "custom_fields": [],
+ "date_created": "1697169676",
+ "depends": [],
+ "full_url": "https://pagure.io/protop2g-test-srce/issue/2",
+ "id": 2,
+ "last_updated": "1750832757",
+ "milestone": "Milestone BBBB",
+ "priority": 3,
+ "private": false,
+ "related_prs": [],
+ "status": "Closed",
+ "tags": [
+ "cccc",
+ "dddd"
+ ],
+ "title": "This is the title of the second test issue",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "assignee": null,
+ "blocks": [],
+ "close_status": null,
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: aaaa, bbbb",
+ "date_created": "1697169699",
+ "edited_on": null,
+ "editor": null,
+ "id": 878471,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under the first test issue",
+ "date_created": "1697169880",
+ "edited_on": null,
+ "editor": null,
+ "id": 878473,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under the first test issue",
+ "date_created": "1697169924",
+ "edited_on": null,
+ "editor": null,
+ "id": 878474,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone AAAA",
+ "date_created": "1746679806",
+ "edited_on": null,
+ "editor": null,
+ "id": 971677,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Epic",
+ "date_created": "1750832745",
+ "edited_on": null,
+ "editor": null,
+ "id": 976719,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "content": "This is the body of the first test issue",
+ "custom_fields": [],
+ "date_created": "1697169462",
+ "depends": [],
+ "full_url": "https://pagure.io/protop2g-test-srce/issue/1",
+ "id": 1,
+ "last_updated": "1750832745",
+ "milestone": "Milestone AAAA",
+ "priority": 4,
+ "private": false,
+ "related_prs": [],
+ "status": "Open",
+ "tags": [
+ "aaaa",
+ "bbbb"
+ ],
+ "title": "This is the title of the first test issue",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "pagination": {
+ "first": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1",
+ "last": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1",
+ "next": null,
+ "page": 1,
+ "pages": 1,
+ "per_page": 20,
+ "prev": null
+ },
+ "total_issues": 2
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10
new file mode 100644
index 0000000000..e43f97a590
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10
@@ -0,0 +1,259 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:21:04 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 6179
+content-security-policy: default-src 'self';script-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; style-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTQ3NGFiNjdkNjdlZjNiNWNmOWYwZGFmODg1MDMxMTYzNTc5OTkxMyJ9.G2IQ0A.97bsQ3k_CAN3UOID130UESba38M; Expires=Sat, 23-Aug-2025 06:21:04 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "branch": "main",
+ "branch_from": "test-ffff",
+ "cached_merge_status": "unknown",
+ "closed_at": "1747635431",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: ffff\n- Request assigned",
+ "commit": null,
+ "date_created": "1747635211",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219623,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "rebased onto 01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "commit": null,
+ "date_created": "1747635389",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219625,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been merged by t0xic0der",
+ "commit": null,
+ "date_created": "1747635431",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219626,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "commit_stop": "1a6ccc212aa958a0fe76155c2907c889969a7224",
+ "date_created": "1747635165",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/10",
+ "id": 10,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747635431",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Merged",
+ "tags": [
+ "ffff"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-ffff` in the README.md file",
+ "uid": "d3b7100abf8b4b02aa220d899e063295",
+ "updated_on": "1747635431",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5
new file mode 100644
index 0000000000..be1c0e3a25
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5
@@ -0,0 +1,249 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:19:26 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5859
+content-security-policy: default-src 'self';script-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; style-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODFiMTQzZWQwZGEzMWIxNmE3NTk1ODYyNWE0MGZjNjcwNThmMDc3ZSJ9.G2IQbg.9qdYqcrGIqnCKtjsWYjcfMCTzok; Expires=Sat, 23-Aug-2025 06:19:26 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-aaaa",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa",
+ "commit": null,
+ "date_created": "1746427453",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219086,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595521",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219190,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595529",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219191,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "date_created": "1746427437",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5",
+ "id": 5,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747636185",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "aaaa"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-aaaa` in the README.md file",
+ "uid": "f9e737c5ccc1434e9798cfd49d192538",
+ "updated_on": "1746595529",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6
new file mode 100644
index 0000000000..878989a6cd
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6
@@ -0,0 +1,249 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:19:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5859
+content-security-policy: default-src 'self';script-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; style-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNWY5OWMwYjViOTkzZjM0YTY3OTJiYTZmNDk4YzY3MzUwMzY3MzY2OCJ9.G2IQgw.dxyk0aB89uDJGN15BTWrZEvCFWg; Expires=Sat, 23-Aug-2025 06:19:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-bbbb",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb",
+ "commit": null,
+ "date_created": "1746427480",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219087,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595539",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219192,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595552",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219193,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "date_created": "1746427470",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6",
+ "id": 6,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747643450",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "bbbb"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-bbbb` in the README.md file",
+ "uid": "9cab89d6bb8c499e8fcb47926f1f5806",
+ "updated_on": "1746595552",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7
new file mode 100644
index 0000000000..51c269273c
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7
@@ -0,0 +1,234 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:00 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5458
+content-security-policy: default-src 'self';script-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; style-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjUwNDcwNWQ4Y2UxZTRjODRlNjQxYTA0NzVlNDA5NGQwNzI4YTY1NyJ9.G2IQkA.KtxZfzau2wOwMTyfG_xNBHTMHSI; Expires=Sat, 23-Aug-2025 06:20:00 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-cccc",
+ "cached_merge_status": "FFORWARD",
+ "closed_at": "1746428043",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: cccc",
+ "commit": null,
+ "date_created": "1746427513",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219088,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been closed by t0xic0der",
+ "commit": null,
+ "date_created": "1746428043",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219090,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "commit_stop": "f1246e331cade9341b9e4f311b7a134f99893d21",
+ "date_created": "1746427506",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/7",
+ "id": 7,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1746428043",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Closed",
+ "tags": [
+ "cccc"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-cccc` in the README.md file",
+ "uid": "f696feab56b84557b4d4a8a4462420ee",
+ "updated_on": "1746428043",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8
new file mode 100644
index 0000000000..45ae72d6e8
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8
@@ -0,0 +1,234 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:17 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5458
+content-security-policy: default-src 'self';script-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; style-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2I3NWQwYzBhY2YxMjhmYTU0YmU5NzEzYzcyNjNjZTQ4NjFlNDE5ZCJ9.G2IQoQ.Ds29JFMXbO3q2kBPxmfx2kQ7YjA; Expires=Sat, 23-Aug-2025 06:20:17 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-dddd",
+ "cached_merge_status": "FFORWARD",
+ "closed_at": "1746428053",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: dddd",
+ "commit": null,
+ "date_created": "1746427540",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219089,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been closed by t0xic0der",
+ "commit": null,
+ "date_created": "1746428053",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219091,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "commit_stop": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160",
+ "date_created": "1746427532",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/8",
+ "id": 8,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1746428053",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Closed",
+ "tags": [
+ "dddd"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-dddd` in the README.md file",
+ "uid": "493b294044fd48e18f424210c919d8de",
+ "updated_on": "1746428053",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9
new file mode 100644
index 0000000000..2e19157948
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9
@@ -0,0 +1,239 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:20:51 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 5628
+content-security-policy: default-src 'self';script-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; style-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2MyYzdlY2E0MDYzNWRmZjRhMDc2MGE0OTg2MjNhMTU4MWNhZDg4OSJ9.G2IQww.VZpiXbZftgigHkiS15g8DF6iQSY; Expires=Sat, 23-Aug-2025 06:20:51 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "assignee": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "branch": "main",
+ "branch_from": "test-eeee",
+ "cached_merge_status": "unknown",
+ "closed_at": "1747635243",
+ "closed_by": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ },
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: eeee\n- Request assigned",
+ "commit": null,
+ "date_created": "1747635200",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219622,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "Pull-Request has been merged by t0xic0der",
+ "commit": null,
+ "date_created": "1747635243",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219624,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "commit_stop": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5",
+ "date_created": "1747635161",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/9",
+ "id": 9,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747635243",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Merged",
+ "tags": [
+ "eeee"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-eeee` in the README.md file",
+ "uid": "f2ad806e430a40bd8ee5894484338df4",
+ "updated_on": "1747635243",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests
new file mode 100644
index 0000000000..15b69e8f03
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests
@@ -0,0 +1,507 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 06:18:47 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 14090
+content-security-policy: default-src 'self';script-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; style-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODAwYWYyZGI3MjZlMzA2ZTdmNTdlMmIwNGVkNmU3YTBmNDYwZDUyMyJ9.G2IQRw.EDXBH36zsKcHKDETH_g7miO_r_w; Expires=Sat, 23-Aug-2025 06:18:47 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "args": {
+ "assignee": null,
+ "author": null,
+ "page": 1,
+ "per_page": 20,
+ "status": true,
+ "tags": []
+ },
+ "pagination": {
+ "first": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1",
+ "last": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1",
+ "next": null,
+ "page": 1,
+ "pages": 1,
+ "per_page": 20,
+ "prev": null
+ },
+ "requests": [
+ {
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-bbbb",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb",
+ "commit": null,
+ "date_created": "1746427480",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219087,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595539",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219192,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595552",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219193,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c",
+ "date_created": "1746427470",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6",
+ "id": 6,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747643450",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "bbbb"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-bbbb` in the README.md file",
+ "uid": "9cab89d6bb8c499e8fcb47926f1f5806",
+ "updated_on": "1746595552",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "assignee": null,
+ "branch": "main",
+ "branch_from": "test-aaaa",
+ "cached_merge_status": "CONFLICTS",
+ "closed_at": null,
+ "closed_by": null,
+ "comments": [
+ {
+ "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa",
+ "commit": null,
+ "date_created": "1746427453",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219086,
+ "line": null,
+ "notification": true,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the first comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595521",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219190,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ {
+ "comment": "This is the second comment under this pull request.",
+ "commit": null,
+ "date_created": "1746595529",
+ "edited_on": null,
+ "editor": null,
+ "filename": null,
+ "id": 219191,
+ "line": null,
+ "notification": false,
+ "parent": null,
+ "reactions": {},
+ "tree": null,
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4",
+ "date_created": "1746427437",
+ "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5",
+ "id": 5,
+ "initial_comment": "Signed-off-by: Akashdeep Dhar ",
+ "last_updated": "1747636185",
+ "project": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "remote_git": null,
+ "repo_from": {
+ "access_groups": {
+ "admin": [],
+ "collaborator": [],
+ "commit": [],
+ "ticket": []
+ },
+ "access_users": {
+ "admin": [
+ "ryanlerch"
+ ],
+ "collaborator": [],
+ "commit": [],
+ "owner": [
+ "t0xic0der"
+ ],
+ "ticket": []
+ },
+ "close_status": [
+ "Complete",
+ "Baseless"
+ ],
+ "custom_keys": [],
+ "date_created": "1697168063",
+ "date_modified": "1744795940",
+ "description": "The source namespace for the Pagure Exporter project to run tests against",
+ "full_url": "https://pagure.io/protop2g-test-srce",
+ "fullname": "protop2g-test-srce",
+ "id": 17042,
+ "milestones": {
+ "Milestone AAAA": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone BBBB": {
+ "active": false,
+ "date": "1765497600"
+ },
+ "Milestone CCCC": {
+ "active": true,
+ "date": "1765497600"
+ },
+ "Milestone DDDD": {
+ "active": false,
+ "date": "1765497600"
+ }
+ },
+ "name": "protop2g-test-srce",
+ "namespace": null,
+ "parent": null,
+ "priorities": {
+ "": "",
+ "1": "Common",
+ "2": "Uncommon",
+ "3": "Rare",
+ "4": "Epic",
+ "5": "Mythic"
+ },
+ "tags": [
+ "srce",
+ "test",
+ "gridhead",
+ "protop2g"
+ ],
+ "url_path": "protop2g-test-srce",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ },
+ "status": "Open",
+ "tags": [
+ "aaaa"
+ ],
+ "threshold_reached": null,
+ "title": "Change the branch identity to `test-aaaa` in the README.md file",
+ "uid": "f9e737c5ccc1434e9798cfd49d192538",
+ "updated_on": "1746595529",
+ "user": {
+ "full_url": "https://pagure.io/user/t0xic0der",
+ "fullname": "Akashdeep Dhar",
+ "name": "t0xic0der",
+ "url_path": "user/t0xic0der"
+ }
+ }
+ ],
+ "total_requests": 2
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa
new file mode 100644
index 0000000000..d0f24e646e
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:34:39 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; style-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZmJhYTlmMTAzYzYxMWE5NmZiZDNkZjE1ZmRiZGQzYTA5YTQ1YTM2YyJ9.G2IF7w.CLP1edDfZSoJcXjoUNt40swcBV8; Expires=Sat, 23-Aug-2025 05:34:39 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "aaaa",
+ "tag_color": "#ff0000",
+ "tag_description": "aaaa"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb
new file mode 100644
index 0000000000..a859096f25
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:34:50 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; style-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZWU4ZDUxYzY4NmUzMzM4ODg4YmRmMjQwYmU3ODVhOTA4MzQ4N2Q2NiJ9.G2IF-g.nG1k1zU4b9Eo9WFGCas8R9a5-Vg; Expires=Sat, 23-Aug-2025 05:34:50 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "bbbb",
+ "tag_color": "#ff0000",
+ "tag_description": "bbbb"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc
new file mode 100644
index 0000000000..c4fdc9aa46
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:35:02 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; style-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYjhhM2YxNjJhZDYwYWU5NzE4NmQ4NDBkMzJlOWNkYmYxN2Y2NjBmMyJ9.G2IGBg.oA7d9DJHW4_ilpbiVkbveF5dM3Q; Expires=Sat, 23-Aug-2025 05:35:02 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "cccc",
+ "tag_color": "#ffff00",
+ "tag_description": "cccc"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd
new file mode 100644
index 0000000000..db9ad09a74
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; style-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2VhZjRhOTlkZWUxZDcxODg2NWIyN2JhZGY4ZjUyMjcxZTdkZGU0MyJ9.G2IGVw.JCK3tXfD0aOgDdIAMv5MlFkl-SY; Expires=Sat, 23-Aug-2025 05:36:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "dddd",
+ "tag_color": "#ffff00",
+ "tag_description": "dddd"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee
new file mode 100644
index 0000000000..eac0faf464
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:34 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; style-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGEyNDUwYzk5MDY2ZjJhZGQyNDhlZmZmM2QxN2UxZTM0ODI2NWFhZiJ9.G2IGYg.hM6ZKEPDXtOvTWlSPeQBLiZjCO4; Expires=Sat, 23-Aug-2025 05:36:34 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "eeee",
+ "tag_color": "#00ff00",
+ "tag_description": "eeee"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff
new file mode 100644
index 0000000000..af5935aa46
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:36:48 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; style-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYWMyNjBhNjE5MjI0OTQ0YTU2Yzc5YjNmNzU3ZTU2MTYzZGQwMGMwNSJ9.G2IGcQ.22KbaZBjPxJkpIoTBIn1UtVzEjI; Expires=Sat, 23-Aug-2025 05:36:49 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "ffff",
+ "tag_color": "#00ff00",
+ "tag_description": "ffff"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg
new file mode 100644
index 0000000000..28fd0ef15d
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg
@@ -0,0 +1,18 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:37:23 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 77
+content-security-policy: default-src 'self';script-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; style-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGExMmI2MGZhYTQyYTY5MmQ0MzNkNWFlZTU0YjE4M2NjY2NmYjI3MCJ9.G2IGkw.9-wL70lPAOlpIv8cusGLLA0Np_U; Expires=Sat, 23-Aug-2025 05:37:23 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tag": "gggg",
+ "tag_color": "#0000ff",
+ "tag_description": "gggg"
+}
diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags
new file mode 100644
index 0000000000..1176b532a4
--- /dev/null
+++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags
@@ -0,0 +1,26 @@
+HTTP/2 200
+date: Wed, 23 Jul 2025 05:16:48 GMT
+server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6
+x-xss-protection: 1; mode=block
+x-content-type-options: nosniff
+referrer-policy: same-origin
+x-frame-options: ALLOW-FROM https://pagure.io/
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-length: 142
+content-security-policy: default-src 'self';script-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; style-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io;
+set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmQ2NTQ1MmFhZWJjZjA0OWIxNTI5MjBjODQyYzUzOGRkYmYyYTkwOSJ9.G2IBwA.pKqOBVDJrtKfyrbJPyuaRfFjnR4; Expires=Sat, 23-Aug-2025 05:16:48 GMT; Secure; HttpOnly; Path=/
+content-type: application/json
+
+{
+ "tags": [
+ "aaaa",
+ "bbbb",
+ "cccc",
+ "dddd",
+ "eeee",
+ "ffff",
+ "gggg",
+ "hhhh"
+ ],
+ "total_tags": 8
+}
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 085995df4f..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
@@ -244,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()
@@ -286,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
@@ -337,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)
@@ -346,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)
@@ -382,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 4b1d7718b6..fdd02dedea 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+ - `)
@@ -33,19 +33,22 @@ var AddPushMirrorRemote = addPushMirrorRemote
func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
addRemoteAndConfig := func(addr, path string) error {
- cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
- if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
- cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path))
+ var cmd *git.Command
+ if m.BranchFilter == "" {
+ cmd = git.NewCommand(ctx, "remote", "add", "--mirror").AddDynamicArguments(m.RemoteName, addr)
} else {
- cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path))
+ cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(m.RemoteName, addr)
+ }
+ if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
+ cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path))
+ } else {
+ cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, addr, path))
}
if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
return err
}
- if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
- return err
- }
- if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ err := addRemotePushRefSpecs(ctx, path, m)
+ if err != nil {
return err
}
return nil
@@ -67,6 +70,49 @@ func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
return nil
}
+func addRemotePushRefSpecs(ctx context.Context, path string, m *repo_model.PushMirror) error {
+ if m.BranchFilter == "" {
+ // If there is no branch filter, set the push refspecs to mirror all branches and tags.
+ if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ return err
+ }
+ } else {
+ branches := strings.SplitSeq(m.BranchFilter, ",")
+ for branch := range branches {
+ branch = strings.TrimSpace(branch)
+ if branch == "" {
+ continue
+ }
+ refspec := fmt.Sprintf("+refs/heads/%s:refs/heads/%s", branch, branch)
+ if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", refspec).RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ return err
+ }
+ }
+ }
+ if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ return err
+ }
+ return nil
+}
+
+func UpdatePushMirrorBranchFilter(ctx context.Context, m *repo_model.PushMirror) error {
+ path := m.Repo.RepoPath()
+
+ // First, remove all existing push refspecs for this remote
+ cmd := git.NewCommand(ctx, "config", "--unset-all").AddDynamicArguments("remote." + m.RemoteName + ".push")
+ if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ // Ignore error if the key doesn't exist
+ if !strings.Contains(err.Error(), "does not exist") {
+ return err
+ }
+ }
+ err := addRemotePushRefSpecs(ctx, path, m)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
// RemovePushMirrorRemote removes the push mirror remote.
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName)
@@ -212,7 +258,6 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
return util.SanitizeErrorCredentialURLs(err)
}
-
return nil
}
diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go
deleted file mode 100644
index 76632b6872..0000000000
--- a/services/mirror/mirror_test.go
+++ /dev/null
@@ -1,66 +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
- * [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.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)
-
- assert.EqualValues(t, "refs/pull/26595/head", results[6].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[6].oldCommitID)
- assert.EqualValues(t, "", results[6].newCommitID)
-
- assert.EqualValues(t, "refs/pull/26595/merge", results[7].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[7].oldCommitID)
- assert.EqualValues(t, "", results[7].newCommitID)
-
- assert.EqualValues(t, "refs/pull/25873/head", results[8].refName.String())
- assert.EqualValues(t, "e0639e38fb", results[8].oldCommitID)
- assert.EqualValues(t, "6db2410489", results[8].newCommitID)
-
- assert.EqualValues(t, "refs/pull/25873/merge", results[9].refName.String())
- assert.EqualValues(t, "1c97ebc746", results[9].oldCommitID)
- assert.EqualValues(t, "976d27d52f", results[9].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/main_test.go b/services/moderation/main_test.go
new file mode 100644
index 0000000000..3a268260d2
--- /dev/null
+++ b/services/moderation/main_test.go
@@ -0,0 +1,17 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ "testing"
+
+ "forgejo.org/models/unittest"
+
+ _ "forgejo.org/models/forgefed"
+ _ "forgejo.org/models/moderation"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m)
+}
diff --git a/services/moderation/moderating.go b/services/moderation/moderating.go
new file mode 100644
index 0000000000..f329070963
--- /dev/null
+++ b/services/moderation/moderating.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ "forgejo.org/models/issues"
+ "forgejo.org/models/moderation"
+ "forgejo.org/models/repo"
+ "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/log"
+ "forgejo.org/services/context"
+)
+
+// GetShadowCopyMap unmarshals the shadow copy raw value of the given abuse report and returns a list of pairs
+// (to be rendered when the report is reviewed by an admin).
+// If the report does not have a shadow copy ID or the raw value is empty, returns nil.
+// If the unmarshal fails a warning is added in the logs and returns nil.
+func GetShadowCopyMap(ctx *context.Context, ard *moderation.AbuseReportDetailed) []moderation.ShadowCopyField {
+ if ard.ShadowCopyID.Valid && len(ard.ShadowCopyRawValue) > 0 {
+ var data moderation.ShadowCopyData
+
+ switch ard.ContentType {
+ case moderation.ReportedContentTypeUser:
+ data = new(user.UserData)
+ case moderation.ReportedContentTypeRepository:
+ data = new(repo.RepositoryData)
+ case moderation.ReportedContentTypeIssue:
+ data = new(issues.IssueData)
+ case moderation.ReportedContentTypeComment:
+ data = new(issues.CommentData)
+ }
+ if err := json.Unmarshal([]byte(ard.ShadowCopyRawValue), &data); err != nil {
+ log.Warn("Unmarshal failed for shadow copy #%d. %v", ard.ShadowCopyID.Int64, err)
+ return nil
+ }
+ return data.GetFieldsMap()
+ }
+ return nil
+}
diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go
new file mode 100644
index 0000000000..3d1bb5b32c
--- /dev/null
+++ b/services/moderation/reporting.go
@@ -0,0 +1,170 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ stdCtx "context"
+ "errors"
+ "time"
+
+ "forgejo.org/models/db"
+ "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
+}
+
+// RemoveResolvedReports removes resolved reports
+func RemoveResolvedReports(ctx stdCtx.Context, keepReportsFor time.Duration) error {
+ log.Trace("Doing: RemoveResolvedReports")
+
+ if keepReportsFor <= 0 {
+ return nil
+ }
+
+ err := db.WithTx(ctx, func(ctx stdCtx.Context) error {
+ resolvedReports, err := moderation.GetResolvedReports(ctx, keepReportsFor)
+ if err != nil {
+ return err
+ }
+
+ for _, report := range resolvedReports {
+ _, err := db.GetEngine(ctx).ID(report.ID).Delete(&moderation.AbuseReport{})
+ if err != nil {
+ return err
+ }
+
+ if report.ShadowCopyID.Valid {
+ _, err := db.GetEngine(ctx).ID(report.ShadowCopyID).Delete(&moderation.AbuseReportShadowCopy{})
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ log.Trace("Finished: RemoveResolvedReports")
+ return nil
+}
diff --git a/services/moderation/reporting_test.go b/services/moderation/reporting_test.go
new file mode 100644
index 0000000000..70925bf184
--- /dev/null
+++ b/services/moderation/reporting_test.go
@@ -0,0 +1,97 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package moderation
+
+import (
+ "testing"
+ "time"
+
+ "forgejo.org/models/db"
+ report_model "forgejo.org/models/moderation"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/timeutil"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestRemoveResolvedReportsWhenNoTimeSet(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ // reportAge needs to be an int64 to match what timeutil.Day expects so we cast the value
+ reportAge := int64(20)
+ resolvedReport := &report_model.AbuseReport{
+ Status: report_model.ReportStatusTypeHandled,
+ ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository,
+ ContentID: 2, Category: report_model.AbuseCategoryTypeOther,
+ CreatedUnix: timeutil.TimeStampNow(),
+ ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*reportAge),
+ }
+ _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport)
+ require.NoError(t, err)
+
+ // No reports should be deleted when the default time to keep is 0
+ err = RemoveResolvedReports(db.DefaultContext, time.Second*0)
+ require.NoError(t, err)
+ unittest.AssertExistsIf(t, true, resolvedReport)
+}
+
+func TestRemoveResolvedReportsWhenMatchTimeSet(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value
+ keepReportsFor := int64(4)
+ resolvedReport := &report_model.AbuseReport{
+ Status: report_model.ReportStatusTypeHandled,
+ ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository,
+ ContentID: 2, Category: report_model.AbuseCategoryTypeOther,
+ CreatedUnix: timeutil.TimeStampNow(),
+ ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor),
+ }
+
+ _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport)
+ require.NoError(t, err)
+
+ // Report should be deleted when older than the default time to keep
+ err = RemoveResolvedReports(db.DefaultContext, time.Second*4)
+ require.NoError(t, err)
+ unittest.AssertExistsIf(t, false, resolvedReport)
+}
+
+func TestRemoveResolvedReportsWhenTimeSetButReportNew(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ resolvedReport := &report_model.AbuseReport{
+ Status: report_model.ReportStatusTypeHandled,
+ ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository,
+ ContentID: 2, Category: report_model.AbuseCategoryTypeOther,
+ CreatedUnix: timeutil.TimeStampNow(),
+ ResolvedUnix: timeutil.TimeStampNow(),
+ }
+ _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport)
+ require.NoError(t, err)
+
+ // Report should not be deleted when newer than the default time to keep
+ err = RemoveResolvedReports(db.DefaultContext, time.Second*4)
+ require.NoError(t, err)
+ unittest.AssertExistsIf(t, true, resolvedReport)
+}
+
+func TestDoesNotRemoveOpenReports(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value
+ keepReportsFor := int64(4)
+ resolvedReport := &report_model.AbuseReport{
+ Status: report_model.ReportStatusTypeOpen,
+ ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository,
+ ContentID: 2, Category: report_model.AbuseCategoryTypeOther,
+ CreatedUnix: timeutil.TimeStampNow(),
+ ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor),
+ }
+
+ _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport)
+ require.NoError(t, err)
+
+ // Report should not be deleted when open
+ // and older than the default time to keep
+ err = RemoveResolvedReports(db.DefaultContext, time.Second*4)
+ require.NoError(t, err)
+ unittest.AssertExistsIf(t, true, resolvedReport)
+}
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
index f49c435e64..9693f4322e 100644
--- a/services/packages/alt/repository.go
+++ b/services/packages/alt/repository.go
@@ -14,15 +14,15 @@ import (
"path"
"time"
- packages_model "code.gitea.io/gitea/models/packages"
- alt_model "code.gitea.io/gitea/models/packages/alt"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "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"
- packages_service "code.gitea.io/gitea/services/packages"
+ 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"
)
@@ -714,21 +714,23 @@ func buildRelease(ctx context.Context, pv *packages_model.PackageVersion, pfs []
for architecture := range architectures.Seq() {
version := time.Now().Unix()
label := setting.AppName
- data := fmt.Sprintf(`Archive: Alt Linux Team
+ origin := setting.AppName
+ archive := setting.AppName
+
+ data := fmt.Sprintf(`Archive: %s
Component: classic
Version: %d
-Origin: Alt Linux Team
+Origin: %s
Label: %s
Architecture: %s
NotAutomatic: false
`,
- version, label, architecture)
+ archive, version, origin, 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)
@@ -744,7 +746,7 @@ NotAutomatic: false
data = fmt.Sprintf(`Origin: %s
Label: %s
-Suite: Sisyphus
+Suite: Unknown
Codename: %d
Date: %s
Architectures: %s
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 d84bdf1b03..f8b08a0b59 100644
--- a/services/packages/cleanup/cleanup.go
+++ b/services/packages/cleanup/cleanup.go
@@ -8,20 +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"
- alt_service "code.gitea.io/gitea/services/packages/alt"
- 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
@@ -122,23 +122,24 @@ 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)
}
- } else if pcr.Type == packages_model.TypeAlt {
+ 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)
}
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
index 4a22ee7a62..7fa938e260 100644
--- a/services/packages/package_update.go
+++ b/services/packages/package_update.go
@@ -7,13 +7,13 @@ import (
"context"
"fmt"
- org_model "code.gitea.io/gitea/models/organization"
- 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"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/util"
+ 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 {
diff --git a/services/packages/packages.go b/services/packages/packages.go
index bf89b6ad35..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, "")
})
}
@@ -261,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))
@@ -304,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,
}
diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go
index 705876e5c0..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,
diff --git a/services/pull/check.go b/services/pull/check.go
index 2d91ed07f5..c31d107605 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
@@ -392,10 +404,14 @@ func CheckPRsForBaseBranch(ctx context.Context, baseRepo *repo_model.Repository,
// Init runs the task queue to test all the checking status pull requests
func Init() error {
+ if err := LoadMergeMessageTemplates(); err != nil {
+ return err
+ }
+
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 dfb8ff708b..9b7e1660bc 100644
--- a/services/pull/check_test.go
+++ b/services/pull/check_test.go
@@ -9,11 +9,11 @@ import (
"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"
@@ -52,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 2c77d9cf4e..0a95ea1152 100644
--- a/services/pull/commit_status.go
+++ b/services/pull/commit_status.go
@@ -9,13 +9,12 @@ 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/gitrepo"
+ "forgejo.org/modules/log"
+ "forgejo.org/modules/structs"
"github.com/gobwas/glob"
)
@@ -105,7 +104,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
return "", errors.New("head branch does not exist, can not merge")
}
- if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) {
+ if pr.Flow == issues_model.PullRequestFlowAGit && !headGitRepo.IsReferenceExist(pr.GetGitRefName()) {
return "", errors.New("head branch does not exist, can not merge")
}
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..a2542176f0 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,26 +14,51 @@ 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"
)
+var mergeMessageTemplates = make(map[repo_model.MergeStyle]string, len(repo_model.MergeStyles))
+
+func LoadMergeMessageTemplates() error {
+ // Load templates for all known merge styles
+ for _, mergeStyle := range repo_model.MergeStyles {
+ templateFilename := filepath.Join(
+ setting.CustomPath,
+ "default_merge_message",
+ fmt.Sprintf("%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle))),
+ )
+
+ content, err := os.ReadFile(templateFilename)
+ if err == nil {
+ mergeMessageTemplates[mergeStyle] = string(content)
+ } else if os.IsNotExist(err) {
+ // The file no longer exists, so delete any previous content
+ delete(mergeMessageTemplates, mergeStyle)
+ } else {
+ return err
+ }
+ }
+ return nil
+}
+
// getMergeMessage composes the message used when merging a pull request.
func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issues_model.PullRequest, mergeStyle repo_model.MergeStyle, extraVars map[string]string) (message, body string, err error) {
if err := pr.LoadBaseRepo(ctx); err != nil {
@@ -77,6 +103,13 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
if _, ok := err.(git.ErrNotExist); ok {
templateContent, err = commit.GetFileContent(templateFilepathGitea, setting.Repository.PullRequest.DefaultMergeMessageSize)
}
+
+ if _, ok := err.(git.ErrNotExist); ok {
+ if preloadedContent, ok := mergeMessageTemplates[mergeStyle]; ok {
+ templateContent, err = preloadedContent, nil
+ }
+ }
+
if err != nil {
if !git.IsErrNotExist(err) {
return "", "", err
@@ -168,10 +201,68 @@ 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 {
- if err := pr.LoadBaseRepo(ctx); err != nil {
+ pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
+ defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
+
+ pr, err := issues_model.GetPullRequestByID(ctx, pr.ID)
+ if err != nil {
+ log.Error("Unable to load pull request itself: %v", err)
+ return fmt.Errorf("unable to load pull request itself: %w", err)
+ }
+
+ if pr.HasMerged {
+ return models.ErrPullRequestHasMerged{
+ ID: pr.ID,
+ IssueID: pr.IssueID,
+ HeadRepoID: pr.HeadRepoID,
+ BaseRepoID: pr.BaseRepoID,
+ HeadBranch: pr.HeadBranch,
+ BaseBranch: pr.BaseBranch,
+ }
+ }
+
+ if err := pr.LoadIssue(ctx); err != nil {
+ log.Error("Unable to load issue: %v", err)
+ return fmt.Errorf("unable to load issue: %w", err)
+ } else if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("Unable to load base repo: %v", err)
return fmt.Errorf("unable to load base repo: %w", err)
} else if err := pr.LoadHeadRepo(ctx); err != nil {
@@ -179,9 +270,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
return fmt.Errorf("unable to load head repo: %w", err)
}
- pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
- defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
-
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if err != nil {
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
@@ -518,13 +606,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 +623,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 +636,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..4598d57b7a 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,77 @@ 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()
+
+ // If the pull request is zero commits behind, then no rebasing needs to be done.
+ if ctx.pr.CommitsBehind == 0 {
+ return nil
+ }
+
+ // 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 6dda46eaec..f655224c5e 100644
--- a/services/pull/merge_squash.go
+++ b/services/pull/merge_squash.go
@@ -5,15 +5,14 @@ package pull
import (
"fmt"
- "strings"
- 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
@@ -67,10 +66,8 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
if setting.Repository.PullRequest.AddCoCommitterTrailers && ctx.committer.String() != sig.String() {
// add trailer
- if !strings.Contains(message, fmt.Sprintf("Co-authored-by: %s", sig.String())) {
- message += fmt.Sprintf("\nCo-authored-by: %s", sig.String())
- }
- message += fmt.Sprintf("\nCo-committed-by: %s\n", 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..aa033b8cdb 100644
--- a/services/pull/merge_test.go
+++ b/services/pull/merge_test.go
@@ -4,9 +4,22 @@
package pull
import (
+ "os"
+ "path"
+ "strings"
"testing"
+ "forgejo.org/models"
+ 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/gitrepo"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func Test_expandDefaultMergeMessage(t *testing.T) {
@@ -65,3 +78,102 @@ 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"))
+}
+
+func prepareLoadMergeMessageTemplates(targetDir string) error {
+ for _, template := range []string{"MERGE", "REBASE", "REBASE-MERGE", "SQUASH", "MANUALLY-MERGED", "REBASE-UPDATE-ONLY"} {
+ file, err := os.Create(path.Join(targetDir, template+"_TEMPLATE.md"))
+ defer file.Close()
+
+ if err == nil {
+ _, err = file.WriteString("Contents for " + template)
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func TestLoadMergeMessageTemplates(t *testing.T) {
+ defer test.MockVariableValue(&setting.CustomPath, t.TempDir())()
+ templateTemp := path.Join(setting.CustomPath, "default_merge_message")
+
+ require.NoError(t, os.MkdirAll(templateTemp, 0o755))
+ require.NoError(t, prepareLoadMergeMessageTemplates(templateTemp))
+
+ testStyles := []repo_model.MergeStyle{
+ repo_model.MergeStyleMerge,
+ repo_model.MergeStyleRebase,
+ repo_model.MergeStyleRebaseMerge,
+ repo_model.MergeStyleSquash,
+ repo_model.MergeStyleManuallyMerged,
+ repo_model.MergeStyleRebaseUpdate,
+ }
+
+ // Load all templates
+ require.NoError(t, LoadMergeMessageTemplates())
+
+ // Check their correctness
+ assert.Len(t, mergeMessageTemplates, len(testStyles))
+ for _, mergeStyle := range testStyles {
+ assert.Equal(t, "Contents for "+strings.ToUpper(string(mergeStyle)), mergeMessageTemplates[mergeStyle])
+ }
+
+ // Unload all templates
+ require.NoError(t, os.RemoveAll(templateTemp))
+ require.NoError(t, LoadMergeMessageTemplates())
+ assert.Empty(t, mergeMessageTemplates)
+}
+
+func TestMergeMergedPR(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ require.NoError(t, pr.LoadBaseRepo(t.Context()))
+
+ gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo)
+ require.NoError(t, err)
+ defer gitRepo.Close()
+
+ assert.True(t, pr.HasMerged)
+ pr.HasMerged = false
+
+ err = Merge(t.Context(), pr, doer, gitRepo, repo_model.MergeStyleRebase, "", "I should not exist", false)
+ require.Error(t, err)
+ assert.True(t, models.IsErrPullRequestHasMerged(err))
+
+ if mergeErr, ok := err.(models.ErrPullRequestHasMerged); ok {
+ assert.Equal(t, pr.ID, mergeErr.ID)
+ assert.Equal(t, pr.IssueID, mergeErr.IssueID)
+ assert.Equal(t, pr.HeadBranch, mergeErr.HeadBranch)
+ assert.Equal(t, pr.BaseBranch, mergeErr.BaseBranch)
+ }
+}
diff --git a/services/pull/patch.go b/services/pull/patch.go
index e90b4bdbbe..89581c916b 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,162 @@ 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.
+// If `onBare` is true, then the context will use the base repository that does
+// not contain a working tree. Otherwise a temprorary repository is created that
+// contains a working tree.
+func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest, onBare bool) (*testPatchContext, error) {
+ testPatchCtx := &testPatchContext{
+ close: func() {},
+ }
+
+ if onBare {
+ 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, git.SupportGitMergeTree)
+ 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 +210,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 +329,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 +344,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 +367,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 +392,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 +476,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 +496,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 +559,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 +575,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..c1fe6c6b55 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -4,36 +4,33 @@
package pull
import (
- "bytes"
"context"
"fmt"
"io"
- "os"
"regexp"
"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"
+ 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 +43,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
@@ -391,98 +381,51 @@ func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, olderTh
// Update commit divergence.
func ValidatePullRequest(ctx context.Context, pr *issues_model.PullRequest, newCommitID, oldCommitID string, doer *user_model.User) {
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
- if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() {
- changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID)
+ if newCommitID == "" || newCommitID == objectFormat.EmptyObjectID().String() {
+ return
+ }
+
+ testPatchCtx, err := getTestPatchCtx(ctx, pr, true)
+ defer testPatchCtx.close()
+ if err != nil {
+ log.Error("testPatchCtx: %v", err)
+ return
+ }
+
+ changed, err := testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, oldCommitID, newCommitID, testPatchCtx.env)
+ if err != nil {
+ log.Error("CheckIfDiffDiffers: %v", err)
+ }
+ if changed {
+ if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
+ log.Error("MarkReviewsAsStale: %v", err)
+ }
+
+ pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
if err != nil {
- log.Error("checkIfPRContentChanged: %v", err)
+ log.Error("GetFirstMatchProtectedBranchRule: %v", err)
}
- if changed {
- if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
- log.Error("MarkReviewsAsStale: %v", err)
+ if pb != nil && pb.DismissStaleApprovals {
+ if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
+ log.Error("DismissApprovalReviews: %v", err)
}
+ }
+ }
+ if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
+ log.Error("MarkReviewsAsNotStale: %v", err)
+ }
- pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
- if err != nil {
- log.Error("GetFirstMatchProtectedBranchRule: %v", err)
- }
- if pb != nil && pb.DismissStaleApprovals {
- if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
- log.Error("DismissApprovalReviews: %v", err)
- }
- }
- }
- if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
- log.Error("MarkReviewsAsNotStale: %v", err)
- }
- divergence, err := GetDiverging(ctx, pr)
+ divergence, err := git.GetDivergingCommits(ctx, testPatchCtx.gitRepo.Path, testPatchCtx.baseRev, testPatchCtx.headRev, testPatchCtx.env)
+ if err != nil {
+ log.Error("GetDivergingCommits: %v", err)
+ } else {
+ err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind)
if err != nil {
- log.Error("GetDiverging: %v", err)
- } else {
- err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind)
- if err != nil {
- log.Error("UpdateCommitDivergence: %v", err)
- }
+ log.Error("UpdateCommitDivergence: %v", err)
}
}
}
-// checkIfPRContentChanged checks if diff to target branch has changed by push
-// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
-func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
- prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
- if err != nil {
- log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
- return false, err
- }
- defer cancel()
-
- tmpRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
- if err != nil {
- return false, fmt.Errorf("OpenRepository: %w", err)
- }
- defer tmpRepo.Close()
-
- // Find the merge-base
- _, base, err := tmpRepo.GetMergeBase("", "base", "tracking")
- if err != nil {
- return false, fmt.Errorf("GetMergeBase: %w", err)
- }
-
- cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
- stdoutReader, stdoutWriter, err := os.Pipe()
- if err != nil {
- return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
- }
-
- stderr := new(bytes.Buffer)
- if err := cmd.Run(&git.RunOpts{
- Dir: prCtx.tmpBasePath,
- Stdout: stdoutWriter,
- Stderr: stderr,
- PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
- _ = stdoutWriter.Close()
- defer func() {
- _ = stdoutReader.Close()
- }()
- return util.IsEmptyReader(stdoutReader)
- },
- }); err != nil {
- if err == util.ErrNotEmpty {
- return true, nil
- }
- err = git.ConcatenateError(err, stderr.String())
-
- log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v",
- newCommitID, oldCommitID, base,
- pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch,
- err)
-
- return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
- }
-
- return false, nil
-}
-
// PushToBaseRepo pushes commits from branches of head repository to
// corresponding branches of base repository.
// FIXME: Only push branches that are actually updates?
diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go
index c51619e7f6..99607c5b35 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"
@@ -92,3 +92,39 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage)
}
+
+func TestPullRequest_GetDefaultMergeMessage_GlobalTemplate(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
+
+ require.NoError(t, pr.LoadBaseRepo(t.Context()))
+ gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo)
+ require.NoError(t, err)
+ defer gitRepo.Close()
+
+ templateRepo, err := git.OpenRepository(t.Context(), "./../../modules/git/tests/repos/templates_repo")
+ require.NoError(t, err)
+ defer templateRepo.Close()
+
+ mergeMessageTemplates[repo_model.MergeStyleMerge] = "${PullRequestTitle} (${PullRequestReference})\n${PullRequestDescription}"
+
+ // Check template is used for Merge...
+ mergeMessage, body, err := GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleMerge)
+ require.NoError(t, err)
+
+ assert.Equal(t, "issue3 (#3)", mergeMessage)
+ assert.Equal(t, "content for the third issue", body)
+
+ // ...but not for RebaseMerge
+ mergeMessage, _, err = GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleRebaseMerge)
+ require.NoError(t, err)
+
+ assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage)
+
+ // ...and that custom Merge template takes priority
+ mergeMessage, body, err = GetDefaultMergeMessage(t.Context(), templateRepo, pr, repo_model.MergeStyleMerge)
+ require.NoError(t, err)
+
+ assert.Equal(t, "Default merge message template", mergeMessage)
+ assert.Equal(t, "This line was read from .forgejo/default_merge_message/MERGE_TEMPLATE.md", body)
+}
diff --git a/services/pull/review.go b/services/pull/review.go
index 927c43150b..b0ab700fa6 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -6,26 +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?`)
-
// ErrDismissRequestOnClosedPR represents an error when an user tries to dismiss a review associated to a closed or merged PR.
type ErrDismissRequestOnClosedPR struct{}
@@ -47,8 +44,8 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
// If the line got changed the comment is going to be invalidated.
func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *git.Repository, branch string) error {
// FIXME differentiate between previous and proposed line
- commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine()))
- if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
+ commit, err := repo.LineBlame(branch, c.TreePath, c.UnsignedLine())
+ if err != nil && (errors.Is(err, git.ErrBlameFileDoesNotExist) || errors.Is(err, git.ErrBlameFileNotEnoughLines)) {
c.Invalidated = true
return issues_model.UpdateCommentInvalidate(ctx, c)
}
@@ -229,10 +226,10 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
// FIXME validate treePath
// Get latest commit referencing the commented line
// No need for get commit for base branch changes
- commit, err := gitRepo.LineBlame(head, gitRepo.Path, treePath, uint(line))
+ commit, err := gitRepo.LineBlame(head, treePath, uint64(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 !errors.Is(err, git.ErrBlameFileDoesNotExist) && !errors.Is(err, git.ErrBlameFileNotEnoughLines) {
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
}
}
@@ -301,10 +298,16 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
if headCommitID == commitID {
stale = false
} else {
- stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
+ testPatchCtx, err := getTestPatchCtx(ctx, pr, true)
+ defer testPatchCtx.close()
if err != nil {
return nil, nil, err
}
+
+ stale, err = testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, commitID, headCommitID, testPatchCtx.env)
+ if err != nil {
+ return nil, nil, fmt.Errorf("CheckIfDiffDiffers: %w", err)
+ }
}
}
@@ -387,7 +390,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 +400,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 b52e4b124e..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)
}
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 8a1118cc2b..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
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 09213e5c65..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,29 +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"
- 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"
- 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"
- federation_service "code.gitea.io/gitea/services/federation"
+ "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"
)
@@ -89,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 {
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..d701508ff0 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
@@ -107,7 +108,7 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
switch {
case entry.IsDir():
return ContentTypeDir
- case entry.IsSubModule():
+ case entry.IsSubmodule():
return ContentTypeSubmodule
case entry.IsExecutable(), entry.IsRegular():
return ContentTypeRegular
@@ -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,19 +206,19 @@ 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
}
contentsResponse.Target = &targetFromContent
- } else if entry.IsSubModule() {
+ } else if entry.IsSubmodule() {
contentsResponse.Type = string(ContentTypeSubmodule)
- submoduleURL, err := commit.GetSubModule(treePath)
+ submodule, err := commit.GetSubmodule(treePath, entry)
if err != nil {
return nil, err
}
- if submoduleURL != "" {
- contentsResponse.SubmoduleGitURL = &submoduleURL
+ if submodule.URL != "" {
+ contentsResponse.SubmoduleGitURL = &submodule.URL
}
}
// Handle links
@@ -228,7 +230,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
downloadURLString := downloadURL.String()
contentsResponse.DownloadURL = &downloadURLString
}
- if !entry.IsSubModule() {
+ if !entry.IsSubmodule() {
htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath))
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 7884d880e0..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) {
@@ -50,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()))
@@ -104,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 db2f4403f4..169cafba0d 100644
--- a/services/repository/files/file_test.go
+++ b/services/repository/files/file_test.go
@@ -14,13 +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)
})
}
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 30d95ba9ab..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
@@ -368,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)
}
@@ -376,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..5a369b27a5 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.
@@ -87,7 +87,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
if entries[e].IsDir() {
copy(treeURL[copyPos:], entries[e].ID.String())
tree.Entries[i].URL = string(treeURL)
- } else if entries[e].IsSubModule() {
+ } else if entries[e].IsSubmodule() {
// In Github Rest API Version=2022-11-28, if a tree entry is a submodule,
// its url will be returned as an empty string.
// So the URL will be set to "" here.
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/services/repository/gitgraph/graph.go b/services/repository/gitgraph/graph.go
index 4db5598015..bf15baed2a 100644
--- a/services/repository/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/services/repository/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go
index 4e94468205..20107cc646 100644
--- a/services/repository/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
diff --git a/services/repository/gitgraph/graph_test.go b/services/repository/gitgraph/graph_test.go
index e7e437e42d..374341b276 100644
--- a/services/repository/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/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 838386d845..e38c38e29c 100644
--- a/services/repository/lfs_test.go
+++ b/services/repository/lfs_test.go
@@ -8,14 +8,14 @@ import (
"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"
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 c8f65dd63d..80f5d68231 100644
--- a/services/repository/migrate.go
+++ b/services/repository/migrate.go
@@ -10,18 +10,18 @@ import (
"net/http"
"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/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
diff --git a/services/repository/push.go b/services/repository/push.go
index 924f365c05..eaedd80e1f 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -10,23 +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"
- "code.gitea.io/gitea/modules/util"
- 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
@@ -66,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")
}
}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 35bcdfd528..41f3a96dd1 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -6,23 +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"
- 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"
- 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
@@ -72,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..2de83ef5a2 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) {
@@ -15,16 +15,16 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data
return nil, false, err
}
- s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{
+ s, exists, err := db.Get[secret_model.Secret](ctx, secret_model.FindSecretsOptions{
OwnerID: ownerID,
RepoID: repoID,
Name: name,
- })
+ }.ToConds())
if err != nil {
return nil, false, err
}
- if len(s) == 0 {
+ if !exists {
s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data)
if err != nil {
return nil, false, err
@@ -32,11 +32,11 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data
return s, true, nil
}
- if err := secret_model.UpdateSecret(ctx, s[0].ID, data); err != nil {
+ s.SetSecret(data)
+ if _, err := db.GetEngine(ctx).Cols("data").ID(s.ID).Update(s); err != nil {
return nil, false, err
}
-
- return s[0], false, nil
+ return s, false, nil
}
func DeleteSecretByID(ctx context.Context, ownerID, repoID, secretID int64) 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 13959e56b4..a2ba5d71a7 100644
--- a/services/user/block_test.go
+++ b/services/user/block_test.go
@@ -6,12 +6,12 @@ package user
import (
"testing"
- model "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"
- "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"
diff --git a/services/user/delete.go b/services/user/delete.go
index 587e3c2a8f..bed7abde07 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.ID, 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..36a1145aec 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.ID, 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 62c30ac05f..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 {
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 62fe44ca27..9cb6858f0c 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -11,24 +11,26 @@ 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/auth"
+ "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"
+ "forgejo.org/services/auth/source/oauth2"
+ 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
@@ -47,10 +49,28 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe
}
// Non-local users are not allowed to change their username.
- if !u.IsOrganization() && !u.IsLocal() {
- return user_model.ErrUserIsNotLocal{
- UID: u.ID,
- Name: u.Name,
+ // If the doer is an admin, then allow the rename - they know better.
+ if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() {
+ // If the user's authentication source is OAuth2 and that source allows for
+ // username changes then don't make a fuzz about it.
+
+ if !u.IsOAuth2() {
+ return user_model.ErrUserIsNotLocal{
+ UID: u.ID,
+ Name: u.Name,
+ }
+ }
+
+ source, err := auth.GetSourceByID(ctx, u.LoginSource)
+ if err != nil {
+ return err
+ }
+ sourceCfg := source.Cfg.(*oauth2.Source)
+ if !sourceCfg.AllowUsernameChange {
+ return user_model.ErrUserIsNotLocal{
+ UID: u.ID,
+ Name: u.Name,
+ }
}
}
diff --git a/services/user/user_test.go b/services/user/user_test.go
index 058ff7b6ed..0747833557 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -11,17 +11,21 @@ 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/moderation"
+ "forgejo.org/models/organization"
+ repo_model "forgejo.org/models/repo"
+ "forgejo.org/models/unittest"
+ user_model "forgejo.org/models/user"
+ "forgejo.org/modules/json"
+ "forgejo.org/modules/optional"
+ "forgejo.org/modules/setting"
+ "forgejo.org/modules/test"
+ "forgejo.org/modules/timeutil"
+ "forgejo.org/services/auth/source/oauth2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -67,13 +71,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)()
@@ -143,17 +141,10 @@ func TestCreateUser(t *testing.T) {
}
func TestRenameUser(t *testing.T) {
+ defer unittest.OverrideFixtures("models/user/fixtures/")()
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21})
- t.Run("Non-Local", func(t *testing.T) {
- u := &user_model.User{
- Type: user_model.UserTypeIndividual,
- LoginType: auth.OAuth2,
- }
- require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename"), user_model.ErrUserIsNotLocal{})
- })
-
t.Run("Same username", func(t *testing.T) {
require.NoError(t, RenameUser(db.DefaultContext, user, user.Name))
})
@@ -195,7 +186,7 @@ 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})
})
@@ -211,17 +202,41 @@ func TestRenameUser(t *testing.T) {
unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "user_rename"})
// The granularity of created_unix is a second.
- time.Sleep(time.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
- time.Sleep(time.Second)
+ 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"})
})
+
+ t.Run("Non-local", func(t *testing.T) {
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1041, LoginSource: 1001})
+ authSource := unittest.AssertExistsAndLoadBean(t, &auth.Source{ID: user.LoginSource})
+ assert.False(t, user.IsLocal())
+ assert.True(t, user.IsOAuth2())
+
+ t.Run("Allowed", func(t *testing.T) {
+ require.NoError(t, RenameUser(t.Context(), user, "I-am-a-local-username"))
+ })
+
+ t.Run("Not allowed", func(t *testing.T) {
+ authSourceCfg := authSource.Cfg.(*oauth2.Source)
+ authSourceCfg.AllowUsernameChange = false
+ authSource.Cfg = authSourceCfg
+ _, err := db.GetEngine(t.Context()).Cols("cfg").ID(authSource.ID).Update(authSource)
+ require.NoError(t, err)
+
+ require.ErrorIs(t, RenameUser(t.Context(), user, "Another-username-change"), user_model.ErrUserIsNotLocal{UID: user.ID, Name: user.Name})
+ t.Run("Admin", func(t *testing.T) {
+ require.NoError(t, AdminRenameUser(t.Context(), user, "Another-username-change"))
+ })
+ })
+ })
}
func TestCreateUser_Issue5882(t *testing.T) {
@@ -283,3 +298,56 @@ func TestDeleteInactiveUsers(t *testing.T) {
unittest.AssertExistsIf(t, true, newUser)
unittest.AssertExistsIf(t, true, newEmail)
}
+
+func TestCreateShadowCopyOnUserUpdate(t *testing.T) {
+ defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")()
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ userAlexSmithID := int64(1002)
+ abuseReportID := int64(2) // submitted for @alexsmith
+ newDummyValue := "[REDACTED]" // used for updating profile text fields
+
+ // Retrieve the abusive user (@alexsmith) and the abuse report already created for this user.
+ abuser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID})
+ report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{
+ ID: abuseReportID,
+ ContentType: moderation.ReportedContentTypeUser,
+ ContentID: abuser.ID,
+ })
+ // The report should not already have a shadow copy linked.
+ assert.False(t, report.ShadowCopyID.Valid)
+
+ // Keep a copy of old field values before updating them.
+ oldUserData := user_model.UserData{
+ FullName: abuser.FullName,
+ Location: abuser.Location,
+ Website: abuser.Website,
+ Pronouns: abuser.Pronouns,
+ Description: abuser.Description,
+ }
+
+ // The abusive user is updating their profile.
+ opts := &UpdateOptions{
+ FullName: optional.Some(newDummyValue),
+ Location: optional.Some(newDummyValue),
+ Website: optional.Some(newDummyValue),
+ Pronouns: optional.Some(newDummyValue),
+ Description: optional.Some(newDummyValue),
+ }
+ require.NoError(t, UpdateUser(t.Context(), abuser, opts))
+
+ // Reload the report.
+ report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID})
+ // A shadow copy should have been created and linked to our report.
+ assert.True(t, report.ShadowCopyID.Valid)
+ // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values.
+ shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64})
+ shadowCopyUserData := new(user_model.UserData)
+ require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyUserData))
+ // Check to see if the initial field values of the user were stored within the shadow copy.
+ assert.Equal(t, oldUserData.FullName, shadowCopyUserData.FullName)
+ assert.Equal(t, oldUserData.Location, shadowCopyUserData.Location)
+ assert.Equal(t, oldUserData.Website, shadowCopyUserData.Website)
+ assert.Equal(t, oldUserData.Pronouns, shadowCopyUserData.Pronouns)
+ assert.Equal(t, oldUserData.Description, shadowCopyUserData.Description)
+}
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 7056e77b47..fcef4612e1 100644
--- a/services/webhook/default_test.go
+++ b/services/webhook/default_test.go
@@ -6,9 +6,9 @@ package webhook
import (
"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"
@@ -237,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 c6d1cb60dc..1a9ce05de4 100644
--- a/services/webhook/deliver_test.go
+++ b/services/webhook/deliver_test.go
@@ -12,13 +12,13 @@ import (
"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"
- "code.gitea.io/gitea/modules/test"
- 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"
@@ -137,7 +137,7 @@ 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))
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 762d29dddc..5d2a240660 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -7,10 +7,10 @@ import (
"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"
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 e0bb2225f7..b04be30bc6 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -6,11 +6,11 @@ package webhook
import (
"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"
@@ -175,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) {
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 614e0f1ef4..7cf24b84ed 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -6,10 +6,10 @@ package webhook
import (
"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"
diff --git a/services/webhook/general.go b/services/webhook/general.go
index ef68f2885b..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
@@ -302,6 +304,25 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, w
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
+}
+
// ToHook convert models.Webhook to api.Hook
// This function is not part of the convert package to prevent an import cycle
func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
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 4b33bfb1d3..bdb0c292ab 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -16,16 +16,16 @@ 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"
- 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{}
@@ -273,6 +273,12 @@ func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) {
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 46e0041a34..1644def0e1 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -6,10 +6,10 @@ package webhook
import (
"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"
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 d9a9724e5b..da6439f198 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -6,10 +6,10 @@ package webhook
import (
"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"
@@ -335,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:" {
@@ -356,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:" {
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index ddd7002890..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() {
@@ -887,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
index 36ec3b8bf1..a810de91c1 100644
--- a/services/webhook/notifier_test.go
+++ b/services/webhook/notifier_test.go
@@ -4,20 +4,22 @@
package webhook
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"
- 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/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/test"
+ 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"
@@ -56,13 +58,7 @@ func pushCommits() *repository.PushCommits {
}
func TestSyncPushCommits(t *testing.T) {
- defer unittest.OverrideFixtures(
- unittest.FixturesOptions{
- Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
- Base: setting.AppWorkPath,
- Dirs: []string{"services/webhook/TestPushCommits"},
- },
- )()
+ defer unittest.OverrideFixtures("services/webhook/TestPushCommits")()
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
@@ -90,18 +86,12 @@ func TestSyncPushCommits(t *testing.T) {
var payloadContent structs.PushPayload
require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
assert.Len(t, payloadContent.Commits, 1)
- assert.EqualValues(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID)
+ assert.Equal(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID)
})
}
func TestPushCommits(t *testing.T) {
- defer unittest.OverrideFixtures(
- unittest.FixturesOptions{
- Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
- Base: setting.AppWorkPath,
- Dirs: []string{"services/webhook/TestPushCommits"},
- },
- )()
+ defer unittest.OverrideFixtures("services/webhook/TestPushCommits")()
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
@@ -129,6 +119,193 @@ func TestPushCommits(t *testing.T) {
var payloadContent structs.PushPayload
require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent))
assert.Len(t, payloadContent.Commits, 1)
- assert.EqualValues(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID)
+ 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 0f696f1b99..e5bf4ec8d1 100644
--- a/services/webhook/packagist_test.go
+++ b/services/webhook/packagist_test.go
@@ -7,10 +7,10 @@ import (
"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"
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 5ef3e4e06f..8c61e7ba25 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -11,15 +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"
- 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"
)
@@ -142,6 +142,7 @@ 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) {
refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
@@ -311,6 +312,12 @@ func (s slackConvertor) Repository(p *api.RepositoryPayload) (SlackPayload, erro
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 ecc11d541f..62090fd310 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -6,10 +6,10 @@ package webhook
import (
"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"
diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go
index 346ccd3c0b..2593afb0b2 100644
--- a/services/webhook/sourcehut/builds.go
+++ b/services/webhook/sourcehut/builds.go
@@ -13,17 +13,17 @@ import (
"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"
@@ -190,6 +190,10 @@ func (pc sourcehutConvertor) Package(_ *api.PackagePayload) (graphqlPayload[buil
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
}
+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
diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go
index 689c369a7f..ac4172f5ff 100644
--- a/services/webhook/sourcehut/builds_test.go
+++ b/services/webhook/sourcehut/builds_test.go
@@ -7,14 +7,14 @@ import (
"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"
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index a02a7691e9..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{}
@@ -205,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 85a62f7615..5066e55b8c 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -6,10 +6,10 @@ package webhook
import (
"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"
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 2ebbbe4a51..15cb8f620c 100644
--- a/services/webhook/webhook_test.go
+++ b/services/webhook/webhook_test.go
@@ -7,15 +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"
- user_model "code.gitea.io/gitea/models/user"
- webhook_model "code.gitea.io/gitea/models/webhook"
- "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"
+ "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"
@@ -104,7 +105,8 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
func TestWebhookUserMail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
- setting.Service.NoReplyAddress = "no-reply.com"
+ 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 1ca5573cae..d4eaa29117 100644
--- a/templates/admin/auth/edit.tmpl
+++ b/templates/admin/auth/edit.tmpl
@@ -299,6 +299,13 @@
{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}
+
+
+
{{ctx.Locale.Tr "admin.auths.allow_username_change"}}
+
+
{{ctx.Locale.Tr "admin.auths.allow_username_change.description"}}
+
+
{{ctx.Locale.Tr "admin.auths.oauth2_use_custom_url"}}
@@ -397,7 +404,7 @@
{{ctx.Locale.Tr "admin.auths.update"}}
- {{ctx.Locale.Tr "admin.auths.delete"}}
+ {{ctx.Locale.Tr "admin.auths.delete"}}
@@ -414,7 +421,7 @@
-
+
+
+
+
{{ctx.Locale.Tr "admin.auths.allow_username_change"}}
+
+
{{ctx.Locale.Tr "admin.auths.allow_username_change.description"}}
+
+
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 8f2b1c12e3..36d44f21f3 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -51,6 +51,16 @@
+
+
+
+ {{ctx.Locale.Tr "admin.config.global_2fa_requirement.title"}}
+ {{ctx.Locale.Tr (print "admin.config.global_2fa_requirement." .GlobalTwoFactorRequirement)}}
+
+
+
@@ -247,6 +257,16 @@
+
+
+
+ {{ctx.Locale.Tr "enabled"}}
+ {{if .Moderation.Enabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
+
+
+
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl
index 8796794aee..0a9a28fa2d 100644
--- a/templates/admin/emails/list.tmpl
+++ b/templates/admin/emails/list.tmpl
@@ -62,12 +62,12 @@
{{else}}
-
{{ctx.Locale.Tr "no_results_found"}}
+
{{ctx.Locale.Tr "repo.pulls.no_results"}}
{{end}}
@@ -104,7 +104,7 @@
-
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 4f8783dd42..08f0a4f204 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -25,7 +25,7 @@
{{svg "octicon-note" 16}}
{{else}}
- {{ctx.Locale.Tr "no_results_found"}}
+ {{ctx.Locale.Tr "repo.pulls.no_results"}}
{{end}}
{{if .Notices}}
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index b719d259e0..8c9c198897 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -67,7 +67,7 @@
{{svg "octicon-pencil"}}
{{else}}
- {{ctx.Locale.Tr "no_results_found"}}
+ {{ctx.Locale.Tr "repo.pulls.no_results"}}
{{end}}
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index f22600a449..6b6b463a6b 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -72,10 +72,10 @@
{{ctx.Locale.TrSize .CalculateBlobSize}}
{{DateUtils.AbsoluteShort .Version.CreatedUnix}}
- {{svg "octicon-trash"}}
+ {{svg "octicon-trash"}}
{{else}}
- {{ctx.Locale.Tr "no_results_found"}}
+ {{ctx.Locale.Tr "repo.pulls.no_results"}}
{{end}}
@@ -84,7 +84,7 @@
{{template "base/paginate" .}}
-
+
-
+
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index afe8e6942a..57c0c210cc 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -35,7 +35,7 @@
{{end}}
-