diff --git a/.deadcode-out b/.deadcode-out
index f9bee43043..61c5bcb055 100644
--- a/.deadcode-out
+++ b/.deadcode-out
@@ -27,13 +27,15 @@ forgejo.org/models/db
TruncateBeans
InTransaction
DumpTables
- GetTableNames
forgejo.org/models/dbfs
file.renameTo
Create
Rename
+forgejo.org/models/forgefed
+ GetFederationHost
+
forgejo.org/models/forgejo/semver
GetVersion
SetVersionString
@@ -65,6 +67,7 @@ forgejo.org/models/user
DeleteUserSetting
GetFederatedUser
GetFederatedUserByUserID
+ UpdateFederatedUser
GetFollowersForUser
AddFollower
RemoveFollower
@@ -245,9 +248,6 @@ forgejo.org/routers/web/org
forgejo.org/services/context
GetPrivateContext
-forgejo.org/services/federation
- Init
-
forgejo.org/services/repository
IsErrForkAlreadyExist
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 3f250e5682..28fa9e4555 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -6,7 +6,7 @@
"ghcr.io/devcontainers/features/node:1": {
"version": "22"
},
- "ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
+ "ghcr.io/devcontainers/features/git-lfs:1.2.4": {},
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
},
"customizations": {
diff --git a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml
deleted file mode 100644
index d4e6e3f2a7..0000000000
--- a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Install the minimal version of Git supported by Forgejo
-#
-runs:
- using: "composite"
- steps:
- - name: install git and git-lfs
- run: |
- set -x
-
- export DEBIAN_FRONTEND=noninteractive
-
- apt-get update -qq
- apt-get -q install -y -qq curl ca-certificates
-
- curl -sS -o /tmp/git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb
- curl -sS -o /tmp/git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb
- curl -sS -o /tmp/git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb
-
- apt-get -q install --allow-downgrades -y -qq /tmp/git-man.deb
- apt-get -q install --allow-downgrades -y -qq /tmp/git.deb
- apt-get -q install --allow-downgrades -y -qq /tmp/git-lfs.deb
diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml
index 729cba3723..5aa6c8cd98 100644
--- a/.forgejo/workflows/renovate.yml
+++ b/.forgejo/workflows/renovate.yml
@@ -28,7 +28,7 @@ jobs:
runs-on: docker
container:
- image: data.forgejo.org/renovate/renovate:41.17.2
+ image: data.forgejo.org/renovate/renovate:41.1.4
steps:
- name: Load renovate repo cache
diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml
index 102a2d9774..9e5cfb92ed 100644
--- a/.forgejo/workflows/testing-integration.yml
+++ b/.forgejo/workflows/testing-integration.yml
@@ -33,8 +33,11 @@ jobs:
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env
- - name: install git 2.34.1 and git-lfs 3.0.2
- uses: ./.forgejo/workflows-composite/install-minimum-git-version
+ - name: install git 2.30
+ uses: ./.forgejo/workflows-composite/apt-install-from
+ with:
+ packages: git/bullseye git-lfs/bullseye
+ release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-backend test-check'
@@ -52,8 +55,11 @@ jobs:
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env
- - name: install git 2.34.1 and git-lfs 3.0.2
- uses: ./.forgejo/workflows-composite/install-minimum-git-version
+ - name: install git 2.30
+ uses: ./.forgejo/workflows-composite/apt-install-from
+ with:
+ packages: git/bullseye git-lfs/bullseye
+ release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-sqlite-migration test-sqlite'
diff --git a/Makefile b/Makefile
index db4ec2fbd5..e770f2a989 100644
--- a/Makefile
+++ b/Makefile
@@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
-RENOVATE_NPM_PACKAGE ?= renovate@41.17.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
+RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index c3b261320c..fb6c201a5e 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -595,8 +595,8 @@
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
},
{
- "name": "github.com/inbucket/html2text",
- "path": "github.com/inbucket/html2text/LICENSE",
+ "name": "github.com/jaytaylor/html2text",
+ "path": "github.com/jaytaylor/html2text/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
},
{
@@ -779,16 +779,6 @@
"path": "github.com/nwaples/rardecode/LICENSE",
"licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
- {
- "name": "github.com/olekukonko/errors",
- "path": "github.com/olekukonko/errors/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
- },
- {
- "name": "github.com/olekukonko/ll",
- "path": "github.com/olekukonko/ll/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
- },
{
"name": "github.com/olekukonko/tablewriter",
"path": "github.com/olekukonko/tablewriter/LICENSE.md",
diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go
index 7159d55e99..eb89273e7f 100644
--- a/cmd/dump_repo.go
+++ b/cmd/dump_repo.go
@@ -82,11 +82,6 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}
func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error {
- setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
-
- // setting.DisableLoggerInit()
- setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
-
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
diff --git a/cmd/hook.go b/cmd/hook.go
index 7378dc21ad..909cdfdf84 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -231,6 +231,8 @@ Forgejo or set your environment appropriately.`, "")
}
}
+ supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil
+
for scanner.Scan() {
// TODO: support news feeds for wiki
if isWiki {
@@ -248,25 +250,31 @@ Forgejo or set your environment appropriately.`, "")
total++
lastline++
- // All references should be checked because permission check was delayed.
- oldCommitIDs[count] = oldCommitID
- newCommitIDs[count] = newCommitID
- refFullNames[count] = refFullName
- count++
- fmt.Fprint(out, "*")
+ // If the ref is a branch or tag, check if it's protected
+ // if supportProcReceive all ref should be checked because
+ // permission check was delayed
+ if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() {
+ oldCommitIDs[count] = oldCommitID
+ newCommitIDs[count] = newCommitID
+ refFullNames[count] = refFullName
+ count++
+ fmt.Fprint(out, "*")
- if count >= hookBatchSize {
- fmt.Fprintf(out, " Checking %d references\n", count)
+ if count >= hookBatchSize {
+ fmt.Fprintf(out, " Checking %d references\n", count)
- hookOptions.OldCommitIDs = oldCommitIDs
- hookOptions.NewCommitIDs = newCommitIDs
- hookOptions.RefFullNames = refFullNames
- extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
- if extra.HasError() {
- return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
+ hookOptions.OldCommitIDs = oldCommitIDs
+ hookOptions.NewCommitIDs = newCommitIDs
+ hookOptions.RefFullNames = refFullNames
+ extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
+ if extra.HasError() {
+ return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
+ }
+ count = 0
+ lastline = 0
}
- count = 0
- lastline = 0
+ } else {
+ fmt.Fprint(out, ".")
}
if lastline >= hookBatchSize {
fmt.Fprint(out, "\n")
@@ -505,6 +513,10 @@ Forgejo or set your environment appropriately.`, "")
return nil
}
+ if git.CheckGitVersionAtLeast("2.29") != nil {
+ return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
+ }
+
reader := bufio.NewReader(os.Stdin)
repoUser := os.Getenv(repo_module.EnvRepoUsername)
repoName := os.Getenv(repo_module.EnvRepoName)
diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go
index c18bfa919b..c543afe872 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -44,11 +44,6 @@ func defaultLoggingFlags() []cli.Flag {
Aliases: []string{"e"},
Usage: "Matching expression for the logger",
},
- &cli.StringFlag{
- Name: "exclusion",
- Aliases: []string{"x"},
- Usage: "Exclusion for the logger",
- },
&cli.StringFlag{
Name: "prefix",
Aliases: []string{"p"},
@@ -291,9 +286,6 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[
if len(c.String("expression")) > 0 {
vals["expression"] = c.String("expression")
}
- if len(c.String("exclusion")) > 0 {
- vals["exclusion"] = c.String("exclusion")
- }
if len(c.String("prefix")) > 0 {
vals["prefix"] = c.String("prefix")
}
diff --git a/cmd/serv.go b/cmd/serv.go
index b0571a276c..1fac2d13f5 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -193,10 +193,12 @@ func runServ(ctx context.Context, c *cli.Command) error {
}
if len(words) < 2 {
- // for AGit Flow
- if cmd == "ssh_info" {
- fmt.Print(`{"type":"agit","version":1}`)
- return nil
+ if git.CheckGitVersionAtLeast("2.29") == nil {
+ // for AGit Flow
+ if cmd == "ssh_info" {
+ fmt.Print(`{"type":"agit","version":1}`)
+ return nil
+ }
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 37d67df5f0..1b8d4c6697 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -631,7 +631,6 @@ LEVEL = Info
;LEVEL=
;FLAGS = stdflags or journald
;EXPRESSION =
-;EXCLUSION =
;PREFIX =
;COLORIZE = false
;;
diff --git a/go.mod b/go.mod
index de6331722b..bb2be827eb 100644
--- a/go.mod
+++ b/go.mod
@@ -24,7 +24,7 @@ require (
github.com/ProtonMail/go-crypto v1.3.0
github.com/PuerkitoBio/goquery v1.10.3
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
- github.com/alecthomas/chroma/v2 v2.19.0
+ github.com/alecthomas/chroma/v2 v2.18.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.2
github.com/buildkite/terminal-to-html/v3 v3.16.8
@@ -42,7 +42,7 @@ require (
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.2
- github.com/go-chi/cors v1.2.2
+ github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
@@ -56,15 +56,15 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-github/v64 v64.0.0
- github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5
+ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
- github.com/inbucket/html2text v0.9.0
- github.com/jhillyerd/enmime/v2 v2.2.0
+ github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
+ github.com/jhillyerd/enmime/v2 v2.1.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.0
@@ -79,7 +79,7 @@ require (
github.com/minio/minio-go/v7 v7.0.94
github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52
- github.com/niklasfasching/go-org v1.9.0
+ github.com/niklasfasching/go-org v1.8.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
@@ -158,7 +158,7 @@ require (
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
- github.com/fatih/color v1.18.0 // indirect
+ github.com/fatih/color v1.16.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
@@ -192,7 +192,7 @@ require (
github.com/libdns/libdns v1.0.0-beta.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/miekg/dns v1.1.63 // indirect
@@ -205,9 +205,7 @@ require (
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
- github.com/olekukonko/errors v1.1.0 // indirect
- github.com/olekukonko/ll v0.0.9 // indirect
- github.com/olekukonko/tablewriter v1.0.7 // indirect
+ github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
diff --git a/go.sum b/go.sum
index 38a9dd5708..639880e2ce 100644
--- a/go.sum
+++ b/go.sum
@@ -62,8 +62,8 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4=
-github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
+github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
+github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -192,8 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@@ -215,8 +215,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
-github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
-github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
+github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
+github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
@@ -307,8 +307,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
-github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
-github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -341,12 +341,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
-github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks=
-github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE=
+github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
+github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8=
-github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI=
+github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0=
+github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -389,10 +389,12 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
@@ -426,20 +428,16 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG
github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/niklasfasching/go-org v1.9.0 h1:4/Sr68Qx06hjC9MVDB/4etGP67JionLHGscLMOClpnk=
-github.com/niklasfasching/go-org v1.9.0/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
+github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
+github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
-github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
-github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
-github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
-github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
-github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -647,6 +645,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/models/actions/run.go b/models/actions/run.go
index 69592120e9..55def805ed 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -284,10 +284,16 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) {
return &run, nil
}
-func GetRunBefore(ctx context.Context, _ *ActionRun) (*ActionRun, error) {
- // TODO return the most recent run related to the run given in argument
- // see https://codeberg.org/forgejo/user-research/issues/63 for context
- return nil, nil
+// GetRunBefore returns the last run that completed a given timestamp (not inclusive).
+func GetRunBefore(ctx context.Context, repoID int64, timestamp timeutil.TimeStamp) (*ActionRun, error) {
+ var run ActionRun
+ has, err := db.GetEngine(ctx).Where("repo_id=? AND stopped IS NOT NULL AND stopped", repoID, timestamp).OrderBy("stopped DESC").Limit(1).Get(&run)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, fmt.Errorf("run before: %w", util.ErrNotExist)
+ }
+ return &run, nil
}
func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, workflowFile, event string) (*ActionRun, error) {
diff --git a/models/actions/run_test.go b/models/actions/run_test.go
index c9a552a2b2..11b03022ff 100644
--- a/models/actions/run_test.go
+++ b/models/actions/run_test.go
@@ -5,7 +5,92 @@ package actions
import (
"testing"
+ "time"
+
+ "forgejo.org/models/db"
+ "forgejo.org/models/unittest"
+ "forgejo.org/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGetRunBefore(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+
+ // this repo is part of the test database requiring loading "repository.yml" in main_test.go
+ var repoID int64 = 1
+
+ workflowID := "test_workflow"
+
+ // third completed run
+ time1, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
+ require.NoError(t, err)
+ timeutil.MockSet(time1)
+ run1 := ActionRun{
+ ID: 1,
+ Index: 1,
+ RepoID: repoID,
+ Stopped: timeutil.TimeStampNow(),
+ WorkflowID: workflowID,
+ }
+
+ // fourth completed run
+ time2, err := time.Parse(time.RFC3339, "2024-08-31T15:47:55+08:00")
+ require.NoError(t, err)
+ timeutil.MockSet(time2)
+ run2 := ActionRun{
+ ID: 2,
+ Index: 2,
+ RepoID: repoID,
+ Stopped: timeutil.TimeStampNow(),
+ WorkflowID: workflowID,
+ }
+
+ // second completed run
+ time3, err := time.Parse(time.RFC3339, "2024-07-31T15:47:54+08:00")
+ require.NoError(t, err)
+ timeutil.MockSet(time3)
+ run3 := ActionRun{
+ ID: 3,
+ Index: 3,
+ RepoID: repoID,
+ Stopped: timeutil.TimeStampNow(),
+ WorkflowID: workflowID,
+ }
+
+ // first completed run
+ time4, err := time.Parse(time.RFC3339, "2024-06-30T15:47:54+08:00")
+ require.NoError(t, err)
+ timeutil.MockSet(time4)
+ run4 := ActionRun{
+ ID: 4,
+ Index: 4,
+ RepoID: repoID,
+ Stopped: timeutil.TimeStampNow(),
+ WorkflowID: workflowID,
+ }
+ require.NoError(t, db.Insert(db.DefaultContext, &run1))
+ runBefore, err := GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
+ // there is no run before run1
+ require.Error(t, err)
+ require.Nil(t, runBefore)
+
+ // now there is only run3 before run1
+ require.NoError(t, db.Insert(db.DefaultContext, &run3))
+ runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
+ require.NoError(t, err)
+ assert.Equal(t, run3.ID, runBefore.ID)
+
+ // there still is only run3 before run1
+ require.NoError(t, db.Insert(db.DefaultContext, &run2))
+ runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
+ require.NoError(t, err)
+ assert.Equal(t, run3.ID, runBefore.ID)
+
+ // run4 is further away from run1
+ require.NoError(t, db.Insert(db.DefaultContext, &run4))
+ runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
+ require.NoError(t, err)
+ assert.Equal(t, run3.ID, runBefore.ID)
}
diff --git a/models/asymkey/main_test.go b/models/asymkey/main_test.go
index aa13a93f0a..316e8f1d54 100644
--- a/models/asymkey/main_test.go
+++ b/models/asymkey/main_test.go
@@ -15,6 +15,8 @@ func TestMain(m *testing.M) {
"gpg_key.yml",
"public_key.yml",
"TestParseCommitWithSSHSignature/public_key.yml",
+ "deploy_key.yml",
+ "gpg_key_import.yml",
"user.yml",
"email_address.yml",
},
diff --git a/models/db/engine.go b/models/db/engine.go
index 76025f7d67..05a119b08d 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -15,7 +15,6 @@ import (
"strings"
"time"
- "forgejo.org/modules/container"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
@@ -439,12 +438,3 @@ func GetMasterEngine(x Engine) (*xorm.Engine, error) {
return engine, nil
}
-
-// GetTableNames returns the table name of all registered models.
-func GetTableNames() container.Set[string] {
- names := make(container.Set[string])
- for _, table := range tables {
- names.Add(x.TableName(table))
- }
- return names
-}
diff --git a/models/db/table_names_test.go b/models/db/table_names_test.go
deleted file mode 100644
index 176ce9905d..0000000000
--- a/models/db/table_names_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2025 The Forgejo Authors. All rights reserved.
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-package db
-
-import (
- "slices"
- "testing"
-
- "forgejo.org/modules/test"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetTableNames(t *testing.T) {
- t.Run("Simple", func(t *testing.T) {
- defer test.MockVariableValue(&tables, []any{new(GPGKey)})()
-
- assert.Equal(t, []string{"gpg_key"}, GetTableNames().Values())
- })
-
- t.Run("Multiple tables", func(t *testing.T) {
- defer test.MockVariableValue(&tables, []any{new(GPGKey), new(User), new(BlockedUser)})()
-
- tableNames := GetTableNames().Values()
- slices.Sort(tableNames)
-
- assert.Equal(t, []string{"forgejo_blocked_user", "gpg_key", "user"}, tableNames)
- })
-}
-
-type GPGKey struct{}
-
-type User struct{}
-
-type BlockedUser struct{}
-
-func (*BlockedUser) TableName() string {
- return "forgejo_blocked_user"
-}
diff --git a/models/fixtures/TestActivateUserEmail/email_address.yml b/models/fixtures/TestActivateUserEmail/email_address.yml
deleted file mode 100644
index cf41ff8241..0000000000
--- a/models/fixtures/TestActivateUserEmail/email_address.yml
+++ /dev/null
@@ -1,7 +0,0 @@
--
- id: 1001
- uid: 1001
- email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net
- lower_email: anothertestuserwithuppercaseemail@otto.splvs.net
- is_activated: false
- is_primary: true
diff --git a/models/fixtures/TestActivateUserEmail/user.yml b/models/fixtures/TestActivateUserEmail/user.yml
deleted file mode 100644
index 0a68e70a4a..0000000000
--- a/models/fixtures/TestActivateUserEmail/user.yml
+++ /dev/null
@@ -1,12 +0,0 @@
--
- id: 1001
- lower_name: user1001
- name: user1001
- full_name: User That loves Upper Cases
- email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net
- passwd: ZogKvWdyEx:password
- passwd_hash_algo: dummy
- avatar: ''
- avatar_email: anothertestuserwithuppercaseemail@otto.splvs.net
- login_name: user1
- created_unix: 1672578000
diff --git a/models/fixtures/action_variable.yml b/models/fixtures/action_variable.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/action_variable.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml
index 6908d85dda..34407d6f81 100644
--- a/models/fixtures/comment.yml
+++ b/models/fixtures/comment.yml
@@ -186,46 +186,10 @@
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
- milestone_id: 10 # not existing milestone
+ milestone_id: 10 # not exsting milestone
old_milestone_id: 0
created_unix: 946685080
--
- id: 2004
- type: 8 # milestone
- poster_id: 1
- issue_id: 1 # in repo_id 1
- milestone_id: 1
- old_milestone_id: 10 # not existing (ghost) milestone
- created_unix: 946685085
-
--
- id: 2005
- type: 8 # milestone
- poster_id: 1
- issue_id: 1 # in repo_id 1
- milestone_id: 10 # not existing (ghost) milestone
- old_milestone_id: 1
- created_unix: 946685090
-
--
- id: 2006
- type: 8 # milestone
- poster_id: 1
- issue_id: 1 # in repo_id 1
- milestone_id: 11 # not existing (ghost) milestone
- old_milestone_id: 10 # not existing (ghost) milestone
- created_unix: 946685095
-
--
- id: 2007
- type: 8 # milestone
- poster_id: 1
- issue_id: 1 # in repo_id 1
- milestone_id: 0
- old_milestone_id: 11 # not existing (ghost) milestone
- created_unix: 946685100
-
-
id: 2010
type: 30 # project
diff --git a/models/fixtures/deploy_key.yml b/models/fixtures/deploy_key.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/deploy_key.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/external_login_user.yml b/models/fixtures/external_login_user.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/external_login_user.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/federated_user.yml b/models/fixtures/federated_user.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/federated_user.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/federation_host.yml b/models/fixtures/federation_host.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/federation_host.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/gpg_key_import.yml b/models/fixtures/gpg_key_import.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/gpg_key_import.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/login_source.yml b/models/fixtures/login_source.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/login_source.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/protected_branch.yml b/models/fixtures/protected_branch.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/protected_branch.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/pull_auto_merge.yml b/models/fixtures/pull_auto_merge.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/pull_auto_merge.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/push_mirror.yml b/models/fixtures/push_mirror.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/push_mirror.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/repo_archiver.yml b/models/fixtures/repo_archiver.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/repo_archiver.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/repo_indexer_status.yml b/models/fixtures/repo_indexer_status.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/repo_indexer_status.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index 2f104eed65..c383fa43ac 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -32,7 +32,7 @@
created_unix: 1731254961
updated_unix: 1731254961
topics: '[]'
-
+
-
id: 2
owner_id: 2
diff --git a/models/fixtures/secret.yml b/models/fixtures/secret.yml
new file mode 100644
index 0000000000..ca780a73aa
--- /dev/null
+++ b/models/fixtures/secret.yml
@@ -0,0 +1 @@
+[] # empty
diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go
index bc64f07013..737350b019 100644
--- a/models/forgejo_migrations/migrate.go
+++ b/models/forgejo_migrations/migrate.go
@@ -108,7 +108,7 @@ var migrations = []*Migration{
// v33 -> v34
NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun),
// v34 -> v35
- NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped),
+ NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped),
}
// GetCurrentDBVersion returns the current Forgejo database version.
diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go
index ca412d7951..0fb3b43e2c 100644
--- a/models/forgejo_migrations/v35.go
+++ b/models/forgejo_migrations/v35.go
@@ -4,10 +4,16 @@
package forgejo_migrations //nolint:revive
import (
+ "forgejo.org/modules/timeutil"
+
"xorm.io/xorm"
)
-// see https://codeberg.org/forgejo/forgejo/issues/8373
-func NoopAddIndexToActionRunStopped(x *xorm.Engine) error {
- return nil
+func AddIndexToActionRunStopped(x *xorm.Engine) error {
+ type ActionRun struct {
+ ID int64
+ Stopped timeutil.TimeStamp `xorm:"index"`
+ }
+
+ return x.Sync(&ActionRun{})
}
diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go
index 9b502d1c91..7285e347b4 100644
--- a/models/issues/comment_list.go
+++ b/models/issues/comment_list.go
@@ -101,7 +101,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
return nil
}
- milestones := make(map[int64]*Milestone, len(milestoneIDs))
+ milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
limit := db.DefaultMaxInSize
@@ -110,7 +110,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
}
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
- Find(&milestones)
+ Find(&milestoneMaps)
if err != nil {
return err
}
@@ -118,8 +118,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:]
}
- for _, comment := range comments {
- comment.Milestone = milestones[comment.MilestoneID]
+ for _, issue := range comments {
+ issue.Milestone = milestoneMaps[issue.MilestoneID]
}
return nil
}
@@ -140,7 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
return nil
}
- milestones := make(map[int64]*Milestone, len(milestoneIDs))
+ milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
limit := db.DefaultMaxInSize
@@ -149,7 +149,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
}
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
- Find(&milestones)
+ Find(&milestoneMaps)
if err != nil {
return err
}
@@ -157,8 +157,8 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:]
}
- for _, comment := range comments {
- comment.OldMilestone = milestones[comment.OldMilestoneID]
+ for _, issue := range comments {
+ issue.OldMilestone = milestoneMaps[issue.MilestoneID]
}
return nil
}
diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go
index 529f0c15d4..91a69c26a7 100644
--- a/models/issues/issue_search.go
+++ b/models/issues/issue_search.go
@@ -48,9 +48,7 @@ type IssuesOptions struct { //nolint
UpdatedBeforeUnix int64
// prioritize issues from this repo
PriorityRepoID int64
- // if this issue index (not ID) exists and matches the filters, *and* priorityrepo sort is used, show it first
- PriorityIssueIndex int64
- IsArchived optional.Option[bool]
+ IsArchived optional.Option[bool]
// If combined with AllPublic, then private as well as public issues
// that matches the criteria will be returned, if AllPublic is false
@@ -62,7 +60,7 @@ type IssuesOptions struct { //nolint
// applySorts sort an issues-related session based on the provided
// sortType string
-func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIssueIndex int64) {
+func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
switch sortType {
case "oldest":
sess.Asc("issue.created_unix").Asc("issue.id")
@@ -99,11 +97,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIss
case "priorityrepo":
sess.OrderBy("CASE "+
"WHEN issue.repo_id = ? THEN 1 "+
- "ELSE 2 END ASC", priorityRepoID)
- if priorityIssueIndex != 0 {
- sess.OrderBy("issue.index = ? DESC", priorityIssueIndex)
- }
- sess.Desc("issue.created_unix").
+ "ELSE 2 END ASC", priorityRepoID).
+ Desc("issue.created_unix").
Desc("issue.id")
case "project-column-sorting":
sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id")
@@ -475,7 +470,7 @@ func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) {
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
applyLimit(sess, opts)
applyConditions(sess, opts)
- applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex)
+ applySorts(sess, opts.SortType, opts.PriorityRepoID)
issues := IssueList{}
if err := sess.Find(&issues); err != nil {
@@ -499,7 +494,7 @@ func IssueIDs(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Co
}
applyLimit(sess, opts)
- applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex)
+ applySorts(sess, opts.SortType, opts.PriorityRepoID)
var res []int64
total, err := sess.Select("`issue`.id").Table(&Issue{}).FindAndCount(&res)
diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index ddb813cf44..8fc0491026 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -149,7 +149,7 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
}
findSession := listPullRequestStatement(ctx, baseRepoID, opts)
- applySorts(findSession, opts.SortType, 0, 0)
+ applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
found := findSession.Find(&prs)
diff --git a/models/migrations/test/tests.go b/models/migrations/test/tests.go
index 6be3b3c2fc..c1f0caf19b 100644
--- a/models/migrations/test/tests.go
+++ b/models/migrations/test/tests.go
@@ -95,8 +95,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
t.Logf("initializing fixtures from: %s", fixturesDir)
if err := unittest.InitFixtures(
unittest.FixturesOptions{
- Dir: fixturesDir,
- SkipCleanRegistedModels: true,
+ Dir: fixturesDir,
}, x); err != nil {
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
return x, deferFn
diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go
index 3a6244ef4c..dadd61a95e 100644
--- a/models/moderation/abuse_report.go
+++ b/models/moderation/abuse_report.go
@@ -100,7 +100,7 @@ type AbuseReport struct {
// The abuse category selected by the reporter.
Category AbuseCategoryType `xorm:"INDEX NOT NULL"`
// Remarks provided by the reporter.
- Remarks string `xorm:"VARCHAR(500)"`
+ Remarks string
// The ID of the corresponding shadow-copied content when exists; otherwise null.
ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go
index d363610a48..cdd8f69c52 100644
--- a/models/moderation/shadow_copy.go
+++ b/models/moderation/shadow_copy.go
@@ -17,7 +17,7 @@ import (
type AbuseReportShadowCopy struct {
ID int64 `xorm:"pk autoincr"`
- RawValue string `xorm:"LONGTEXT NOT NULL"` // A JSON with relevant fields from user, repository, issue or comment table.
+ RawValue string `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
diff --git a/models/unittest/fixture_loader.go b/models/unittest/fixture_loader.go
index dda5c48d35..67ef1b28df 100644
--- a/models/unittest/fixture_loader.go
+++ b/models/unittest/fixture_loader.go
@@ -12,8 +12,6 @@ import (
"path/filepath"
"strings"
- "forgejo.org/modules/container"
-
"gopkg.in/yaml.v3"
)
@@ -34,15 +32,13 @@ type loader struct {
fixtureFiles []*fixtureFile
}
-func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTableNames container.Set[string]) (*loader, error) {
+func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loader, error) {
l := &loader{
db: db,
dialect: dialect,
fixtureFiles: []*fixtureFile{},
}
- tablesWithoutFixture := allTableNames
-
// Load fixtures
for _, fixturePath := range fixturePaths {
stat, err := os.Stat(fixturePath)
@@ -64,7 +60,6 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl
return nil, err
}
l.fixtureFiles = append(l.fixtureFiles, fixtureFile)
- tablesWithoutFixture.Remove(fixtureFile.name)
}
}
} else {
@@ -76,14 +71,6 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl
}
}
- // Even though these tables have no fixtures, they can still be used and ensure
- // they are cleaned.
- for table := range tablesWithoutFixture.Seq() {
- l.fixtureFiles = append(l.fixtureFiles, &fixtureFile{
- name: table,
- })
- }
-
return l, nil
}
@@ -191,13 +178,13 @@ func (l *loader) Load() error {
}()
// Clean the table and re-insert the fixtures.
- tableDeleted := make(container.Set[string])
+ tableDeleted := map[string]struct{}{}
for _, fixture := range l.fixtureFiles {
- if !tableDeleted.Contains(fixture.name) {
+ if _, ok := tableDeleted[fixture.name]; !ok {
if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", l.quoteKeyword(fixture.name))); err != nil {
return fmt.Errorf("cannot delete table %s: %w", fixture.name, err)
}
- tableDeleted.Add(fixture.name)
+ tableDeleted[fixture.name] = struct{}{}
}
for _, insertSQL := range fixture.insertSQLs {
diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go
index 829cc16466..6dc5c8412d 100644
--- a/models/unittest/fixtures.go
+++ b/models/unittest/fixtures.go
@@ -7,12 +7,10 @@ package unittest
import (
"fmt"
"path/filepath"
- "sync"
"time"
"forgejo.org/models/db"
"forgejo.org/modules/auth/password/hash"
- "forgejo.org/modules/container"
"forgejo.org/modules/setting"
"xorm.io/xorm"
@@ -46,8 +44,6 @@ func OverrideFixtures(dir string) func() {
}
}
-var allTableNames = sync.OnceValue(db.GetTableNames)
-
// InitFixtures initialize test fixtures for a test database
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
e, err := GetXORMEngine(engine...)
@@ -79,12 +75,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
panic("Unsupported RDBMS for test")
}
- var allTables container.Set[string]
- if !opts.SkipCleanRegistedModels {
- allTables = allTableNames().Clone()
- }
-
- fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths, allTables)
+ fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths)
if err != nil {
return err
}
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 29ec82c55f..d34c9e9a0a 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -217,10 +217,6 @@ type FixturesOptions struct {
Files []string
Dirs []string
Base string
- // By default all registered models are cleaned, even if they do not have
- // fixture. Enabling this will skip that and only models with fixtures are
- // considered.
- SkipCleanRegistedModels bool
}
// CreateTestEngine creates a memory database and loads the fixture data from fixturesDir
diff --git a/models/user/activitypub.go b/models/user/activitypub.go
index aabf2336fc..816fd8a098 100644
--- a/models/user/activitypub.go
+++ b/models/user/activitypub.go
@@ -19,7 +19,7 @@ func (u *User) APActorID() string {
return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID)))
}
-// KeyID returns the ID of the user's public key
-func (u *User) KeyID() string {
+// APActorKeyID returns the ID of the user's public key
+func (u *User) APActorKeyID() string {
return u.APActorID() + "#main-key"
}
diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go
index 85f5b16c65..1801f57a23 100644
--- a/models/user/email_address_test.go
+++ b/models/user/email_address_test.go
@@ -181,20 +181,3 @@ func TestDeletePrimaryEmailAddressOfUser(t *testing.T) {
assert.True(t, user_model.IsErrEmailAddressNotExist(err))
assert.Nil(t, email)
}
-
-func TestActivateUserEmail(t *testing.T) {
- defer unittest.OverrideFixtures("models/fixtures/TestActivateUserEmail")()
- require.NoError(t, unittest.PrepareTestDatabase())
-
- t.Run("Activate email", func(t *testing.T) {
- require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", true))
-
- unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = true")
- })
-
- t.Run("Deactivate email", func(t *testing.T) {
- require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", false))
-
- unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = false")
- })
-}
diff --git a/models/user/user.go b/models/user/user.go
index b124572bb6..eedd1db80e 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -182,11 +182,11 @@ func (u *User) BeforeUpdate() {
u.MaxRepoCreation = -1
}
- // Ensure AvatarEmail is set for non-organization users, because organization
- // are not required to have a email set.
+ // Organization does not need email
+ u.Email = strings.ToLower(u.Email)
if !u.IsOrganization() {
if len(u.AvatarEmail) == 0 {
- u.AvatarEmail = strings.ToLower(u.Email)
+ u.AvatarEmail = u.Email
}
}
diff --git a/models/user/user_repository.go b/models/user/user_repository.go
index 85f44f1598..3f24efb1fb 100644
--- a/models/user/user_repository.go
+++ b/models/user/user_repository.go
@@ -57,6 +57,14 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat
return committer.Commit()
}
+func (federatedUser *FederatedUser) UpdateFederatedUser(ctx context.Context) error {
+ if _, err := validation.IsValid(federatedUser); err != nil {
+ return err
+ }
+ _, err := db.GetEngine(ctx).ID(federatedUser.ID).Cols("inbox_path").Update(federatedUser)
+ return err
+}
+
func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) {
federatedUser := new(FederatedUser)
user := new(User)
@@ -211,6 +219,7 @@ func RemoveFollower(ctx context.Context, followedUser *User, followingUser *Fede
return err
}
+// TODO: We should unify Activity-pub-following and classical following (see models/user/follow.go)
func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) {
if res, err := validation.IsValid(followedUser); !res {
return false, err
diff --git a/models/user/user_test.go b/models/user/user_test.go
index f9a3aa6075..fd9d05653f 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -150,7 +150,7 @@ func TestAPActorID_APActorID(t *testing.T) {
func TestKeyID(t *testing.T) {
user := user_model.User{ID: 1}
- url := user.KeyID()
+ url := user.APActorKeyID()
expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key"
assert.Equal(t, expected, url)
}
diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go
index fb6fa8b543..d015fb7bec 100644
--- a/modules/activitypub/client.go
+++ b/modules/activitypub/client.go
@@ -89,7 +89,6 @@ func NewClientFactory() (c *ClientFactory, err error) {
type APClientFactory interface {
WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error)
- WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error)
}
// Client struct
@@ -104,8 +103,12 @@ type Client struct {
}
// NewRequest function
-func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error) {
- privPem, _ := pem.Decode([]byte(privateKey))
+func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) {
+ priv, err := GetPrivateKey(ctx, user)
+ if err != nil {
+ return nil, err
+ }
+ privPem, _ := pem.Decode([]byte(priv))
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
if err != nil {
return nil, err
@@ -123,14 +126,6 @@ func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID s
return &c, nil
}
-func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) {
- priv, err := GetPrivateKey(ctx, user)
- if err != nil {
- return nil, err
- }
- return cf.WithKeysDirect(ctx, priv, pubID)
-}
-
// NewRequest function
func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) {
buf := bytes.NewBuffer(b)
@@ -154,14 +149,12 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
return nil, err
}
- if c.pubID != "" {
- signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
- if err != nil {
- return nil, err
- }
- if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil {
- return nil, err
- }
+ signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
+ if err != nil {
+ return nil, err
+ }
+ if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil {
+ return nil, err
}
resp, err = c.client.Do(req)
@@ -174,15 +167,12 @@ func (c *Client) Get(to string) (resp *http.Response, err error) {
if req, err = c.newRequest(http.MethodGet, nil, to); err != nil {
return nil, err
}
-
- if c.pubID != "" {
- signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime)
- if err != nil {
- return nil, err
- }
- if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil {
- return nil, err
- }
+ signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime)
+ if err != nil {
+ return nil, err
+ }
+ if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil {
+ return nil, err
}
resp, err = c.client.Do(req)
diff --git a/modules/container/set.go b/modules/container/set.go
index d3719dc552..70f837bc66 100644
--- a/modules/container/set.go
+++ b/modules/container/set.go
@@ -74,8 +74,3 @@ func (s Set[T]) Values() []T {
func (s Set[T]) Seq() iter.Seq[T] {
return maps.Keys(s)
}
-
-// Clone returns a identical shallow copy of this set.
-func (s Set[T]) Clone() Set[T] {
- return maps.Clone(s)
-}
diff --git a/modules/container/set_test.go b/modules/container/set_test.go
index 44e4847f6b..af5e9126ab 100644
--- a/modules/container/set_test.go
+++ b/modules/container/set_test.go
@@ -47,11 +47,4 @@ func TestSet(t *testing.T) {
assert.False(t, s.IsSubset([]string{"key1"}))
assert.True(t, s.IsSubset([]string{}))
-
- t.Run("Clone", func(t *testing.T) {
- clonedSet := s.Clone()
- clonedSet.Remove("key6")
- assert.False(t, clonedSet.Contains("key6"))
- assert.True(t, s.Contains("key6"))
- })
}
diff --git a/modules/git/blame.go b/modules/git/blame.go
index 868edab2b8..4ff347e31b 100644
--- a/modules/git/blame.go
+++ b/modules/git/blame.go
@@ -132,7 +132,7 @@ func (r *BlameReader) Close() error {
// CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
var ignoreRevsFile *string
- if !bypassBlameIgnore {
+ if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore {
ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
}
diff --git a/modules/git/blob.go b/modules/git/blob.go
index 4eef5f0e2a..8c5c275146 100644
--- a/modules/git/blob.go
+++ b/modules/git/blob.go
@@ -8,7 +8,6 @@ import (
"bufio"
"bytes"
"encoding/base64"
- "fmt"
"io"
"forgejo.org/modules/log"
@@ -173,43 +172,60 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) {
return string(buf), err
}
-type BlobTooLargeError struct {
- Size, Limit int64
-}
-
-func (b BlobTooLargeError) Error() string {
- return fmt.Sprintf("blob: content larger than limit (%d > %d)", b.Size, b.Limit)
-}
-
-// GetContentBase64 Reads the content of the blob and returns it as base64 encoded string.
-// Returns [BlobTooLargeError] if the (unencoded) content is larger than the limit.
-func (b *Blob) GetContentBase64(limit int64) (string, error) {
- if b.Size() > limit {
- return "", BlobTooLargeError{
- Size: b.Size(),
- Limit: limit,
- }
+// GetBlobLineCount gets line count of the blob
+func (b *Blob) GetBlobLineCount() (int, error) {
+ reader, err := b.DataAsync()
+ if err != nil {
+ return 0, err
}
+ defer reader.Close()
+ buf := make([]byte, 32*1024)
+ count := 1
+ lineSep := []byte{'\n'}
- rc, size, err := b.NewTruncatedReader(limit)
+ c, err := reader.Read(buf)
+ if c == 0 && err == io.EOF {
+ return 0, nil
+ }
+ for {
+ count += bytes.Count(buf[:c], lineSep)
+ switch {
+ case err == io.EOF:
+ return count, nil
+ case err != nil:
+ return count, err
+ }
+ c, err = reader.Read(buf)
+ }
+}
+
+// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string
+func (b *Blob) GetBlobContentBase64() (string, error) {
+ dataRc, err := b.DataAsync()
if err != nil {
return "", err
}
- defer rc.Close()
+ defer dataRc.Close()
- encoding := base64.StdEncoding
- buf := bytes.NewBuffer(make([]byte, 0, encoding.EncodedLen(int(size))))
+ pr, pw := io.Pipe()
+ encoder := base64.NewEncoder(base64.StdEncoding, pw)
- encoder := base64.NewEncoder(encoding, buf)
+ go func() {
+ _, err := io.Copy(encoder, dataRc)
+ _ = encoder.Close()
- if _, err := io.Copy(encoder, rc); err != nil {
+ if err != nil {
+ _ = pw.CloseWithError(err)
+ } else {
+ _ = pw.Close()
+ }
+ }()
+
+ out, err := io.ReadAll(pr)
+ if err != nil {
return "", err
}
- if err := encoder.Close(); err != nil {
- return "", err
- }
-
- return buf.String(), nil
+ return string(out), nil
}
// GuessContentType guesses the content type of the blob.
@@ -220,7 +236,7 @@ func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) {
}
defer r.Close()
- return typesniffer.DetectContentTypeFromReader(r, b.Name())
+ return typesniffer.DetectContentTypeFromReader(r)
}
// GetBlob finds the blob object in the repository.
diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go
index a4b8033941..54115013d3 100644
--- a/modules/git/blob_test.go
+++ b/modules/git/blob_test.go
@@ -63,24 +63,6 @@ func TestBlob(t *testing.T) {
require.Equal(t, "file2\n", r)
})
- t.Run("GetContentBase64", func(t *testing.T) {
- r, err := testBlob.GetContentBase64(100)
- require.NoError(t, err)
- require.Equal(t, "ZmlsZTIK", r)
-
- r, err = testBlob.GetContentBase64(-1)
- require.ErrorAs(t, err, &BlobTooLargeError{})
- require.Empty(t, r)
-
- r, err = testBlob.GetContentBase64(4)
- require.ErrorAs(t, err, &BlobTooLargeError{})
- require.Empty(t, r)
-
- r, err = testBlob.GetContentBase64(6)
- require.NoError(t, err)
- require.Equal(t, "ZmlsZTIK", r)
- })
-
t.Run("NewTruncatedReader", func(t *testing.T) {
// read fewer than available
rc, size, err := testBlob.NewTruncatedReader(100)
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 1228b4523b..96831e3ae4 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -412,7 +412,11 @@ func (c *Commit) GetSubModule(entryname string) (string, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
- cmd := NewCommand(c.repo.Ctx, "name-rev", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
+ cmd := NewCommand(c.repo.Ctx, "name-rev")
+ if CheckGitVersionAtLeast("2.13.0") == nil {
+ cmd.AddArguments("--exclude", "refs/tags/*")
+ }
+ cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil {
// handle special case where git can not describe commit
diff --git a/modules/git/git.go b/modules/git/git.go
index 851b090b53..1dfd0b5134 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -23,7 +23,7 @@ import (
)
// RequiredVersion is the minimum Git version required
-const RequiredVersion = "2.34.1"
+const RequiredVersion = "2.0.0"
var (
// GitExecutable is the command name of git
@@ -33,6 +33,7 @@ var (
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
DefaultContext context.Context
+ SupportProcReceive bool // >= 2.29
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
InvertedGitFlushEnv bool // 2.43.1
SupportCheckAttrOnBare bool // >= 2.40
@@ -112,7 +113,7 @@ func VersionInfo() string {
format := "%s"
args := []any{GitVersion.Original()}
// Since git wire protocol has been released from git v2.18
- if setting.Git.EnableAutoGitWireProtocol {
+ if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
format += ", Wire Protocol %s Enabled"
args = append(args, "Version 2") // for focus color
}
@@ -171,13 +172,16 @@ func InitFull(ctx context.Context) (err error) {
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
}
- if setting.Git.EnableAutoGitWireProtocol {
+ // Since git wire protocol has been released from git v2.18
+ if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
}
// Explicitly disable credential helper, otherwise Git credentials might leak
- globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
-
+ if CheckGitVersionAtLeast("2.9") == nil {
+ globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
+ }
+ SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil
SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil
if SupportHashSha256 {
@@ -191,6 +195,9 @@ func InitFull(ctx context.Context) (err error) {
SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil
if setting.LFS.StartServer {
+ if CheckGitVersionAtLeast("2.1.2") != nil {
+ return errors.New("LFS server support requires Git >= 2.1.2")
+ }
globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
}
@@ -227,28 +234,38 @@ func syncGitConfig() (err error) {
}
}
- // Set git some configurations - these must be set to these values for forgejo to work correctly
+ // Set git some configurations - these must be set to these values for gitea to work correctly
if err := configSet("core.quotePath", "false"); err != nil {
return err
}
- if err := configSet("receive.advertisePushOptions", "true"); err != nil {
- return err
+ if CheckGitVersionAtLeast("2.10") == nil {
+ if err := configSet("receive.advertisePushOptions", "true"); err != nil {
+ return err
+ }
}
- if err := configSet("core.commitGraph", "true"); err != nil {
- return err
- }
- if err := configSet("gc.writeCommitGraph", "true"); err != nil {
- return err
- }
- if err := configSet("fetch.writeCommitGraph", "true"); err != nil {
- return err
+ if CheckGitVersionAtLeast("2.18") == nil {
+ if err := configSet("core.commitGraph", "true"); err != nil {
+ return err
+ }
+ if err := configSet("gc.writeCommitGraph", "true"); err != nil {
+ return err
+ }
+ if err := configSet("fetch.writeCommitGraph", "true"); err != nil {
+ return err
+ }
}
- // set support for AGit flow
- if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
- return err
+ if SupportProcReceive {
+ // set support for AGit flow
+ if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
+ return err
+ }
+ } else {
+ if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
+ return err
+ }
}
// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user
@@ -267,6 +284,11 @@ func syncGitConfig() (err error) {
switch setting.Repository.Signing.Format {
case "ssh":
+ // First do a git version check.
+ if CheckGitVersionAtLeast("2.34.0") != nil {
+ return errors.New("ssh signing requires Git >= 2.34.0")
+ }
+
// Get the ssh-keygen binary that Git will use.
// This can be overridden in app.ini in [git.config] section, so we must
// query this information.
@@ -303,7 +325,8 @@ func syncGitConfig() (err error) {
}
}
- if !setting.Git.DisablePartialClone {
+ // By default partial clones are disabled, enable them from git v2.22
+ if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
if err = configSet("uploadpack.allowfilter", "true"); err != nil {
return err
}
diff --git a/modules/git/git_test.go b/modules/git/git_test.go
index 38d4db169c..01200dba68 100644
--- a/modules/git/git_test.go
+++ b/modules/git/git_test.go
@@ -14,6 +14,7 @@ import (
"forgejo.org/modules/test"
"forgejo.org/modules/util"
+ "github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -104,6 +105,10 @@ func TestSyncConfigGPGFormat(t *testing.T) {
})
t.Run("SSH format", func(t *testing.T) {
+ if CheckGitVersionAtLeast("2.34.0") != nil {
+ t.SkipNow()
+ }
+
r, err := os.OpenRoot(t.TempDir())
require.NoError(t, err)
f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700)
@@ -116,6 +121,13 @@ func TestSyncConfigGPGFormat(t *testing.T) {
assert.True(t, gitConfigContains("[gpg]"))
assert.True(t, gitConfigContains("format = ssh"))
+ t.Run("Old version", func(t *testing.T) {
+ oldVersion, err := version.NewVersion("2.33.0")
+ require.NoError(t, err)
+ defer test.MockVariableValue(&GitVersion, oldVersion)()
+ require.ErrorContains(t, syncGitConfig(), "ssh signing requires Git >= 2.34.0")
+ })
+
t.Run("No ssh-keygen binary", func(t *testing.T) {
require.NoError(t, r.Remove("ssh-keygen"))
require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary")
diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go
index 1ee8921854..f39b7113bb 100644
--- a/modules/git/pipeline/revlist.go
+++ b/modules/git/pipeline/revlist.go
@@ -16,6 +16,26 @@ import (
"forgejo.org/modules/log"
)
+// RevListAllObjects runs rev-list --objects --all and writes to a pipewriter
+func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, basePath string, errChan chan<- error) {
+ defer wg.Done()
+ defer revListWriter.Close()
+
+ stderr := new(bytes.Buffer)
+ var errbuf strings.Builder
+ cmd := git.NewCommand(ctx, "rev-list", "--objects", "--all")
+ if err := cmd.Run(&git.RunOpts{
+ Dir: basePath,
+ Stdout: revListWriter,
+ Stderr: stderr,
+ }); err != nil {
+ log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String())
+ err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String())
+ _ = revListWriter.CloseWithError(err)
+ errChan <- err
+ }
+}
+
// RevListObjects run rev-list --objects from headSHA to baseSHA
func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) {
defer wg.Done()
diff --git a/modules/git/remote.go b/modules/git/remote.go
index 83a02fe2be..fb66d76ff0 100644
--- a/modules/git/remote.go
+++ b/modules/git/remote.go
@@ -12,7 +12,14 @@ import (
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
- result, _, err := NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName).RunStdString(&RunOpts{Dir: repoPath})
+ var cmd *Command
+ if CheckGitVersionAtLeast("2.7") == nil {
+ cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
+ } else {
+ cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
+ }
+
+ result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil {
return "", err
}
diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go
index 3d2c845fa0..c69382e245 100644
--- a/modules/git/repo_attribute_test.go
+++ b/modules/git/repo_attribute_test.go
@@ -5,6 +5,7 @@ package git
import (
"context"
+ "fmt"
"io"
"io/fs"
"os"
@@ -196,7 +197,7 @@ func TestGitAttributeCheckerError(t *testing.T) {
path := t.TempDir()
// we can't use unittest.CopyDir because of an import cycle (git.Init in unittest)
- require.NoError(t, os.CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo"))))
+ require.NoError(t, CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo"))))
gitRepo, err := openRepositoryWithDefaultContext(path)
require.NoError(t, err)
@@ -323,3 +324,32 @@ func TestGitAttributeCheckerError(t *testing.T) {
require.ErrorIs(t, err, fs.ErrClosed)
})
}
+
+// CopyFS is adapted from https://github.com/golang/go/issues/62484
+// which should be available with go1.23
+func CopyFS(dir string, fsys fs.FS) error {
+ return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error {
+ targ := filepath.Join(dir, filepath.FromSlash(path))
+ if d.IsDir() {
+ return os.MkdirAll(targ, 0o777)
+ }
+ r, err := fsys.Open(path)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+ info, err := r.Stat()
+ if err != nil {
+ return err
+ }
+ w, err := os.OpenFile(targ, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o666|info.Mode()&0o777)
+ if err != nil {
+ return err
+ }
+ if _, err := io.Copy(w, r); err != nil {
+ w.Close()
+ return fmt.Errorf("copying %s: %v", path, err)
+ }
+ return w.Close()
+ })
+}
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index 41ca0e39b1..4c8516f828 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -443,18 +443,42 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
}
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
- command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix)
+ if CheckGitVersionAtLeast("2.7.0") == nil {
+ command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix)
- if limit != -1 {
- command = command.AddOptionFormat("--count=%d", limit)
+ if limit != -1 {
+ command = command.AddOptionFormat("--count=%d", limit)
+ }
+
+ stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path})
+ if err != nil {
+ return nil, err
+ }
+
+ branches := strings.Fields(stdout)
+ return branches, nil
}
- stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
- branches := strings.Fields(stdout)
+ refs := strings.Split(stdout, "\n")
+
+ var max int
+ if len(refs) > limit {
+ max = limit
+ } else {
+ max = len(refs) - 1
+ }
+
+ branches := make([]string, max)
+ for i, ref := range refs[:max] {
+ parts := strings.Fields(ref)
+
+ branches[i] = parts[len(parts)-1]
+ }
return branches, nil
}
diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go
index c3647bd894..492438be37 100644
--- a/modules/git/repo_commitgraph.go
+++ b/modules/git/repo_commitgraph.go
@@ -11,8 +11,10 @@ import (
// WriteCommitGraph write commit graph to speed up repo access
// this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error {
- if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil {
- return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
+ if CheckGitVersionAtLeast("2.18") == nil {
+ if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil {
+ return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
+ }
}
return nil
}
diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go
index ec5c632ca0..d51b7992fe 100644
--- a/modules/git/tree_entry.go
+++ b/modules/git/tree_entry.go
@@ -116,37 +116,32 @@ func (te *TreeEntry) Type() string {
}
}
-// LinkTarget returns the target of the symlink as string.
-func (te *TreeEntry) LinkTarget() (string, error) {
- if !te.IsLink() {
- return "", ErrBadLink{te.Name(), "not a symlink"}
- }
-
- const symlinkLimit = 4096 // according to git config core.longpaths https://stackoverflow.com/a/22575737
- blob := te.Blob()
- if blob.Size() > symlinkLimit {
- return "", ErrBadLink{te.Name(), "symlink too large"}
- }
-
- rc, size, err := blob.NewTruncatedReader(symlinkLimit)
- if err != nil {
- return "", err
- }
- defer rc.Close()
-
- buf := make([]byte, int(size))
- _, err = io.ReadFull(rc, buf)
- return string(buf), err
-}
-
// FollowLink returns the entry pointed to by a symlink
func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) {
+ if !te.IsLink() {
+ return nil, "", ErrBadLink{te.Name(), "not a symlink"}
+ }
+
// read the link
- lnk, err := te.LinkTarget()
+ r, err := te.Blob().DataAsync()
if err != nil {
return nil, "", err
}
+ closed := false
+ defer func() {
+ if !closed {
+ _ = r.Close()
+ }
+ }()
+ buf := make([]byte, te.Size())
+ _, err = io.ReadFull(r, buf)
+ if err != nil {
+ return nil, "", err
+ }
+ _ = r.Close()
+ closed = true
+ lnk := string(buf)
t := te.ptree
// traverse up directories
diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go
index d385ac21c9..c5f0658d4e 100644
--- a/modules/httplib/serve.go
+++ b/modules/httplib/serve.go
@@ -99,7 +99,7 @@ func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath stri
Filename: path.Base(filePath),
}
- sniffedType := typesniffer.DetectContentType(mineBuf, opts.Filename)
+ sniffedType := typesniffer.DetectContentType(mineBuf)
// the "render" parameter came from year 2016: 638dd24c, it doesn't have clear meaning, so I think it could be removed later
isPlain := sniffedType.IsText() || r.FormValue("render") != ""
diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go
index 4c8b5f2a86..c53b7a2e6d 100644
--- a/modules/indexer/code/bleve/bleve.go
+++ b/modules/indexer/code/bleve/bleve.go
@@ -177,7 +177,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
fileContents, err := io.ReadAll(io.LimitReader(batchReader, size))
if err != nil {
return err
- } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() {
+ } else if !typesniffer.DetectContentType(fileContents).IsText() {
// FIXME: UTF-16 files will probably fail here
// Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty.
fileContents = nil
diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go
index 9b11f56fb7..3903d77fe0 100644
--- a/modules/indexer/code/elasticsearch/elasticsearch.go
+++ b/modules/indexer/code/elasticsearch/elasticsearch.go
@@ -144,7 +144,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
fileContents, err := io.ReadAll(io.LimitReader(batchReader, size))
if err != nil {
return nil, err
- } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() {
+ } else if !typesniffer.DetectContentType(fileContents).IsText() {
// FIXME: UTF-16 files will probably fail here
return nil, nil
}
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index cb98f722c5..573d63a446 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -156,12 +156,11 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error {
func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) {
var queries []query.Query
- tokens, err := options.Tokens()
- if err != nil {
- return nil, err
- }
-
- if len(tokens) > 0 {
+ if options.Keyword != "" {
+ tokens, err := options.Tokens()
+ if err != nil {
+ return nil, err
+ }
q := bleve.NewBooleanQuery()
for _, token := range tokens {
innerQ := bleve.NewDisjunctionQuery(
@@ -171,7 +170,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
if issueID, err := token.ParseIssueReference(); err == nil {
idQuery := inner_bleve.NumericEqualityQuery(issueID, "index")
- idQuery.SetBoost(20.0)
+ idQuery.SetBoost(5.0)
innerQ.AddQuery(idQuery)
}
@@ -198,15 +197,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...))
}
- if options.PriorityRepoID.Has() {
- eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id")
- eq.SetBoost(10.0)
- meh := bleve.NewMatchAllQuery()
- meh.SetBoost(0)
- should := bleve.NewDisjunctionQuery(eq, meh)
- queries = append(queries, should)
- }
-
if options.IsPull.Has() {
queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull"))
}
diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go
index 5f42bce9a1..397daa3265 100644
--- a/modules/indexer/issues/db/db.go
+++ b/modules/indexer/issues/db/db.go
@@ -53,7 +53,6 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
cond := builder.NewCond()
- var priorityIssueIndex int64
if options.Keyword != "" {
repoCond := builder.In("repo_id", options.RepoIDs)
if len(options.RepoIDs) == 1 {
@@ -83,7 +82,6 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
builder.Eq{"`index`": issueID},
cond,
)
- priorityIssueIndex = issueID
}
}
@@ -91,7 +89,6 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
if err != nil {
return nil, err
}
- opt.PriorityIssueIndex = priorityIssueIndex
// If pagesize == 0, return total count only. It's a special case for search count.
if options.Paginator != nil && options.Paginator.PageSize == 0 {
diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go
index 55a471fc8e..4411cc1c37 100644
--- a/modules/indexer/issues/db/options.go
+++ b/modules/indexer/issues/db/options.go
@@ -78,11 +78,6 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
User: nil,
}
- if options.PriorityRepoID.Has() {
- opts.SortType = "priorityrepo"
- opts.PriorityRepoID = options.PriorityRepoID.Value()
- }
-
if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 {
opts.MilestoneIDs = []int64{db.NoConditionID}
} else {
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go
index 311e92730e..9d2786e101 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch.go
@@ -149,13 +149,12 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error {
func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) {
query := elastic.NewBoolQuery()
- tokens, err := options.Tokens()
- if err != nil {
- return nil, err
- }
-
- if len(tokens) > 0 {
+ if options.Keyword != "" {
q := elastic.NewBoolQuery()
+ tokens, err := options.Tokens()
+ if err != nil {
+ return nil, err
+ }
for _, token := range tokens {
innerQ := elastic.NewMultiMatchQuery(token.Term, "content", "comments").FieldWithBoost("title", 2.0).TieBreaker(0.5)
if token.Fuzzy {
@@ -166,7 +165,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
}
var eitherQ elastic.Query = innerQ
if issueID, err := token.ParseIssueReference(); err == nil {
- indexQ := elastic.NewTermQuery("index", issueID).Boost(20)
+ indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0)
eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5)
}
switch token.Kind {
@@ -189,10 +188,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
}
query.Must(q)
}
- if options.PriorityRepoID.Has() {
- q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10)
- query.Should(q)
- }
if options.IsPull.Has() {
query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value()))
diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go
index cdd113212d..6c55405179 100644
--- a/modules/indexer/issues/internal/model.go
+++ b/modules/indexer/issues/internal/model.go
@@ -75,9 +75,8 @@ type SearchResult struct {
type SearchOptions struct {
Keyword string // keyword to search
- RepoIDs []int64 // repository IDs which the issues belong to
- AllPublic bool // if include all public repositories
- PriorityRepoID optional.Option[int64] // issues from this repository will be prioritized when SortByScore
+ RepoIDs []int64 // repository IDs which the issues belong to
+ AllPublic bool // if include all public repositories
IsPull optional.Option[bool] // if the issues is a pull request
IsClosed optional.Option[bool] // if the issues is closed
diff --git a/modules/indexer/issues/internal/qstring.go b/modules/indexer/issues/internal/qstring.go
index 348f7a564b..6b60b4c5f6 100644
--- a/modules/indexer/issues/internal/qstring.go
+++ b/modules/indexer/issues/internal/qstring.go
@@ -45,9 +45,12 @@ func (t *Tokenizer) next() (tk Token, err error) {
// skip all leading white space
for {
- if r, _, err = t.in.ReadRune(); err != nil || r != ' ' {
- break
+ if r, _, err = t.in.ReadRune(); err == nil && r == ' ' {
+ //nolint:staticcheck,wastedassign // SA4006 the variable is used after the loop
+ r, _, err = t.in.ReadRune()
+ continue
}
+ break
}
if err != nil {
return tk, err
@@ -104,17 +107,11 @@ nextEnd:
// Tokenize the keyword
func (o *SearchOptions) Tokens() (tokens []Token, err error) {
- if o.Keyword == "" {
- return nil, nil
- }
-
in := strings.NewReader(o.Keyword)
it := Tokenizer{in: in}
for token, err := it.next(); err == nil; token, err = it.next() {
- if token.Term != "" {
- tokens = append(tokens, token)
- }
+ tokens = append(tokens, token)
}
if err != nil && err != io.EOF {
return nil, err
diff --git a/modules/indexer/issues/internal/qstring_test.go b/modules/indexer/issues/internal/qstring_test.go
index eb4bdb306f..835491707c 100644
--- a/modules/indexer/issues/internal/qstring_test.go
+++ b/modules/indexer/issues/internal/qstring_test.go
@@ -41,36 +41,6 @@ var testOpts = []testIssueQueryStringOpt{
},
},
},
- {
- Keyword: "Hello World",
- Results: []Token{
- {
- Term: "Hello",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- {
- Term: "World",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- },
- },
- {
- Keyword: " Hello World ",
- Results: []Token{
- {
- Term: "Hello",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- {
- Term: "World",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- },
- },
{
Keyword: "+Hello +World",
Results: []Token{
@@ -186,68 +156,6 @@ var testOpts = []testIssueQueryStringOpt{
},
},
},
- {
- Keyword: "\\",
- Results: nil,
- },
- {
- Keyword: "\"",
- Results: nil,
- },
- {
- Keyword: "Hello \\",
- Results: []Token{
- {
- Term: "Hello",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- },
- },
- {
- Keyword: "\"\"",
- Results: nil,
- },
- {
- Keyword: "\" World \"",
- Results: []Token{
- {
- Term: " World ",
- Fuzzy: false,
- Kind: BoolOptShould,
- },
- },
- },
- {
- Keyword: "\"\" World \"\"",
- Results: []Token{
- {
- Term: "World",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- },
- },
- {
- Keyword: "Best \"Hello World\" Ever",
- Results: []Token{
- {
- Term: "Best",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- {
- Term: "Hello World",
- Fuzzy: false,
- Kind: BoolOptShould,
- },
- {
- Term: "Ever",
- Fuzzy: true,
- Kind: BoolOptShould,
- },
- },
- },
}
func TestIssueQueryString(t *testing.T) {
diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go
index 46014994a0..ef75955a14 100644
--- a/modules/indexer/issues/internal/tests/tests.go
+++ b/modules/indexer/issues/internal/tests/tests.go
@@ -87,44 +87,14 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
}
}
-func allResults(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Len(t, result.Hits, len(data))
- assert.Equal(t, len(data), int(result.Total))
-}
-
var cases = []*testIndexerCase{
{
Name: "default",
SearchOptions: &internal.SearchOptions{},
- Expected: allResults,
- },
- {
- Name: "empty keyword",
- SearchOptions: &internal.SearchOptions{
- Keyword: "",
+ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
+ assert.Len(t, result.Hits, len(data))
+ assert.Equal(t, len(data), int(result.Total))
},
- Expected: allResults,
- },
- {
- Name: "whitespace keyword",
- SearchOptions: &internal.SearchOptions{
- Keyword: " ",
- },
- Expected: allResults,
- },
- {
- Name: "dangling slash in keyword",
- SearchOptions: &internal.SearchOptions{
- Keyword: "\\",
- },
- Expected: allResults,
- },
- {
- Name: "dangling quote in keyword",
- SearchOptions: &internal.SearchOptions{
- Keyword: "\"",
- },
- Expected: allResults,
},
{
Name: "empty",
@@ -772,25 +742,6 @@ var cases = []*testIndexerCase{
}
},
},
- {
- Name: "PriorityRepoID",
- SearchOptions: &internal.SearchOptions{
- IsPull: optional.Some(false),
- IsClosed: optional.Some(false),
- PriorityRepoID: optional.Some(int64(3)),
- Paginator: &db.ListOptionsAll,
- SortBy: internal.SortByScore,
- },
- Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- for i, v := range result.Hits {
- if i < 7 {
- assert.Equal(t, int64(3), data[v.ID].RepoID)
- } else {
- assert.NotEqual(t, int64(3), data[v.ID].RepoID)
- }
- }
- },
- },
}
type testIndexerCase struct {
diff --git a/modules/lfs/pointer_scanner.go b/modules/lfs/pointer_scanner.go
index 80da8e5222..632ecd19ae 100644
--- a/modules/lfs/pointer_scanner.go
+++ b/modules/lfs/pointer_scanner.go
@@ -39,7 +39,16 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c
go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg)
// 1. Run batch-check on all objects in the repository
- go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan)
+ if git.CheckGitVersionAtLeast("2.6.0") != nil {
+ revListReader, revListWriter := io.Pipe()
+ shasToCheckReader, shasToCheckWriter := io.Pipe()
+ wg.Add(2)
+ go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, basePath)
+ go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg)
+ go pipeline.RevListAllObjects(ctx, revListWriter, &wg, basePath, errChan)
+ } else {
+ go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan)
+ }
wg.Wait()
close(pointerChan)
diff --git a/modules/log/event_writer.go b/modules/log/event_writer.go
index 32b5b582c5..4b77e488de 100644
--- a/modules/log/event_writer.go
+++ b/modules/log/event_writer.go
@@ -26,7 +26,6 @@ type WriterMode struct {
Flags Flags
Expression string
- Exclusion string
StacktraceLevel Level
diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go
index 4de2b953c7..9189ca4e90 100644
--- a/modules/log/event_writer_base.go
+++ b/modules/log/event_writer_base.go
@@ -68,14 +68,6 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) {
}
}
- var exclusionRegexp *regexp.Regexp
- if b.Mode.Exclusion != "" {
- var err error
- if exclusionRegexp, err = regexp.Compile(b.Mode.Exclusion); err != nil {
- FallbackErrorf("unable to compile exclusion %q for writer %q: %v", b.Mode.Exclusion, b.Name, err)
- }
- }
-
handlePaused := func() {
if pause := b.GetPauseChan(); pause != nil {
select {
@@ -103,13 +95,6 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) {
continue
}
}
- if exclusionRegexp != nil {
- fileLineCaller := fmt.Sprintf("%s:%d:%s", event.Origin.Filename, event.Origin.Line, event.Origin.Caller)
- matched := exclusionRegexp.MatchString(fileLineCaller) || exclusionRegexp.MatchString(event.Origin.MsgSimpleText)
- if matched {
- continue
- }
- }
var err error
switch msg := event.Msg.(type) {
diff --git a/modules/log/event_writer_buffer_test.go b/modules/log/event_writer_buffer_test.go
index d1e37c3673..ba9455ba69 100644
--- a/modules/log/event_writer_buffer_test.go
+++ b/modules/log/event_writer_buffer_test.go
@@ -31,49 +31,3 @@ func TestBufferLogger(t *testing.T) {
logger.Close()
assert.Contains(t, bufferWriter.Buffer.String(), expected)
}
-
-func TestBufferLoggerWithExclusion(t *testing.T) {
- prefix := "ExclusionPrefix "
- level := log.INFO
- message := "something"
-
- bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{
- Level: level,
- Prefix: prefix,
- Exclusion: message,
- })
-
- logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter)
-
- logger.SendLogEvent(&log.Event{
- Level: log.INFO,
- MsgSimpleText: message,
- })
- logger.Close()
- assert.NotContains(t, bufferWriter.Buffer.String(), message)
-}
-
-func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) {
- prefix := "BothPrefix "
- level := log.INFO
- expression := ".*foo.*"
- exclusion := ".*bar.*"
-
- bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{
- Level: level,
- Prefix: prefix,
- Expression: expression,
- Exclusion: exclusion,
- })
-
- logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter)
-
- logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo expression"})
- logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "bar exclusion"})
- logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo bar both"})
- logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "none"})
- logger.Close()
-
- assert.Contains(t, bufferWriter.Buffer.String(), "foo expression")
- assert.NotContains(t, bufferWriter.Buffer.String(), "bar")
-}
diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go
index 99045b0f4f..6d6ceb69d7 100644
--- a/modules/log/logger_test.go
+++ b/modules/log/logger_test.go
@@ -143,19 +143,3 @@ func TestLoggerExpressionFilter(t *testing.T) {
assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.GetLogs())
}
-
-func TestLoggerExclusionFilter(t *testing.T) {
- logger := NewLoggerWithWriters(t.Context(), "test")
-
- w1 := newDummyWriter("dummy-1", DEBUG, 0)
- w1.Mode.Exclusion = "foo.*"
- logger.AddWriters(w1)
-
- logger.Info("foo")
- logger.Info("bar")
- logger.Info("foo bar")
- logger.SendLogEvent(&Event{Level: INFO, Filename: "foo.go", MsgSimpleText: "by filename"})
- logger.Close()
-
- assert.Equal(t, []string{"bar\n"}, w1.GetLogs())
-}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index f7955115e0..e229ee4c65 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) {
test(
"",
- `

`)
+ `
`)
test(
"[["+title+"|"+url+"]]",
@@ -115,7 +115,7 @@ func TestRender_Images(t *testing.T) {
test(
"",
- `
`)
+ `
`)
test(
"[["+title+"|"+url+"]]",
@@ -412,8 +412,8 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
testcase := `

`
- expected := `
-
+ expected := `
+
`
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
require.NoError(t, err)
@@ -845,10 +845,10 @@ mail@domain.com
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -872,10 +872,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -901,10 +901,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -930,10 +930,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -959,10 +959,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -988,10 +988,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1018,10 +1018,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1048,10 +1048,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1078,10 +1078,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1108,10 +1108,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1139,10 +1139,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1170,10 +1170,10 @@ space
remote link
local link
remote link
-
-
-
-
+
+
+
+


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go
index b86c9e3d41..0f9c69cae6 100644
--- a/modules/markup/markdown/transform_image.go
+++ b/modules/markup/markdown/transform_image.go
@@ -44,7 +44,6 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
for _, attr := range v.Attributes() {
image.SetAttribute(attr.Name, attr.Value)
}
- image.SetAttributeString("loading", []byte("lazy"))
for child := v.FirstChild(); child != nil; {
next := child.NextSibling()
image.AppendChild(image, child)
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index aacc2536bf..384dd1fe94 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -108,9 +108,6 @@ func createDefaultPolicy() *bluemonday.Policy {
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img")
- // Allow attributes for images
- policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img")
-
// Allow icons, emojis, chroma syntax and keyword markup on span
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span")
diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go
index a0faff0494..9805a34910 100644
--- a/modules/markup/sanitizer_test.go
+++ b/modules/markup/sanitizer_test.go
@@ -75,10 +75,6 @@ func Test_Sanitizer(t *testing.T) {
// Emoji
`THUMBS UP`, `THUMBS UP`,
`THUMBS UP`, `THUMBS UP`,
-
- // Images lazy loading
- `
`, `
`,
- `
`, `
`,
}
for i := 0; i < len(testCases); i += 2 {
diff --git a/modules/setting/log.go b/modules/setting/log.go
index 6d069d0e9c..0747ac4dac 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -133,7 +133,6 @@ func loadLogModeByName(rootCfg ConfigProvider, loggerName, modeName string) (wri
writerMode.StacktraceLevel = log.LevelFromString(ConfigInheritedKeyString(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel.String()))
writerMode.Prefix = ConfigInheritedKeyString(sec, "PREFIX")
writerMode.Expression = ConfigInheritedKeyString(sec, "EXPRESSION")
- writerMode.Exclusion = ConfigInheritedKeyString(sec, "EXCLUSION")
// flags are updated and set below
switch writerType {
diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go
index 223bd68285..eda6dc36af 100644
--- a/modules/setting/log_test.go
+++ b/modules/setting/log_test.go
@@ -44,7 +44,6 @@ func TestLogConfigDefault(t *testing.T) {
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "info",
"Prefix": "",
@@ -84,7 +83,6 @@ logger.xorm.MODE =
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "info",
"Prefix": "",
@@ -123,7 +121,6 @@ MODE = console
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "info",
"Prefix": "",
@@ -171,7 +168,6 @@ ACCESS = file
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "info",
"Prefix": "",
@@ -195,7 +191,6 @@ ACCESS = file
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "none",
"Level": "info",
"Prefix": "",
@@ -262,7 +257,6 @@ STDERR = true
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "warn",
"Prefix": "",
@@ -276,7 +270,6 @@ STDERR = true
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "error",
"Prefix": "",
@@ -294,7 +287,6 @@ STDERR = true
"BufferLen": 10000,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "none",
"Level": "warn",
"Prefix": "",
@@ -331,7 +323,6 @@ MODE = file
LEVEL = error
STACKTRACE_LEVEL = fatal
EXPRESSION = filter
-EXCLUSION = not
FLAGS = medfile
PREFIX = "[Prefix] "
FILE_NAME = file-xxx.log
@@ -350,7 +341,6 @@ COMPRESSION_LEVEL = 4
"BufferLen": 10,
"Colorize": false,
"Expression": "",
- "Exclusion": "",
"Flags": "stdflags",
"Level": "info",
"Prefix": "",
@@ -370,7 +360,6 @@ COMPRESSION_LEVEL = 4
"BufferLen": 10,
"Colorize": false,
"Expression": "filter",
- "Exclusion": "not",
"Flags": "medfile",
"Level": "error",
"Prefix": "[Prefix] ",
diff --git a/modules/structs/activitypub.go b/modules/structs/activitypub.go
index 0cc257ff95..117eb0bed2 100644
--- a/modules/structs/activitypub.go
+++ b/modules/structs/activitypub.go
@@ -1,5 +1,4 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
-// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
@@ -8,15 +7,3 @@ package structs
type ActivityPub struct {
Context string `json:"@context"`
}
-
-type APRemoteFollowOption struct {
- Target string `json:"target"`
-}
-
-type APPersonFollowItem struct {
- ActorID string `json:"actor_id"`
- Note string `json:"note"`
-
- OriginalURL string `json:"original_url"`
- OriginalItem string `json:"original_item"`
-}
diff --git a/modules/structs/attachment.go b/modules/structs/attachment.go
index 746f618cf0..0a3d4140c2 100644
--- a/modules/structs/attachment.go
+++ b/modules/structs/attachment.go
@@ -22,12 +22,6 @@ type Attachment struct {
Type string `json:"type"`
}
-// WebAttachment the generic attachment with mime type
-type WebAttachment struct {
- *Attachment
- MimeType string `json:"mime_type"`
-}
-
// EditAttachmentOptions options for editing attachments
// swagger:model
type EditAttachmentOptions struct {
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index 11372ca6e1..5adcad0881 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -53,7 +53,8 @@ type CreateHookOption struct {
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
AuthorizationHeader string `json:"authorization_header"`
// default: false
- Active bool `json:"active"`
+ Active bool `json:"active"`
+ IsSystemWebhook bool `json:"is_system_webhook"`
}
// EditHookOption options when modify one hook
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 62e063213c..b75b061218 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) {
remote link
local link
remote link
-
-
+
+
88fc37a3c0...12fc37a3c0 (hash)
diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go
index ea8a69e9b4..9bd908e2b9 100644
--- a/modules/test/distant_federation_server_mock.go
+++ b/modules/test/distant_federation_server_mock.go
@@ -10,79 +10,56 @@ import (
"net/http/httptest"
"strings"
"testing"
-
- "forgejo.org/modules/util"
)
type FederationServerMockPerson struct {
- ID int64
- Name string
- PubKey string
- PrivKey string
+ ID int64
+ Name string
+ PubKey string
}
type FederationServerMockRepository struct {
ID int64
}
-type ApActorMock struct {
- PrivKey string
- PubKey string
-}
type FederationServerMock struct {
- ApActor ApActorMock
Persons []FederationServerMockPerson
Repositories []FederationServerMockRepository
LastPost string
}
func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson {
- priv, pub, _ := util.GenerateKeyPair(3072)
return FederationServerMockPerson{
- ID: id,
- Name: name,
- PubKey: pub,
- PrivKey: priv,
+ ID: id,
+ Name: name,
+ PubKey: `"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA18H5s7N6ItZUAh9tneII\nIuZdTTa3cZlLa/9ejWAHTkcp3WLW+/zbsumlMrWYfBy2/yTm56qasWt38iY4D6ul\n` +
+ `CPiwhAqX3REvVq8tM79a2CEqZn9ka6vuXoDgBg/sBf/BUWqf7orkjUXwk/U0Egjf\nk5jcurF4vqf1u+rlAHH37dvSBaDjNj6Qnj4OP12bjfaY/yvs7+jue/eNXFHjzN4E\n` +
+ `T2H4B/yeKTJ4UuAwTlLaNbZJul2baLlHelJPAsxiYaziVuV5P+IGWckY6RSerRaZ\nAkc4mmGGtjAyfN9aewe+lNVfwS7ElFx546PlLgdQgjmeSwLX8FWxbPE5A/PmaXCs\n` +
+ `nx+nou+3dD7NluULLtdd7K+2x02trObKXCAzmi5/Dc+yKTzpFqEz+hLNCz7TImP/\ncK//NV9Q+X67J9O27baH9R9ZF4zMw8rv2Pg0WLSw1z7lLXwlgIsDapeMCsrxkVO4\n` +
+ `LXX5AQ1xQNtlssnVoUBqBrvZsX2jUUKUocvZqMGuE4hfAgMBAAE=\n-----END PUBLIC KEY-----\n"`,
}
}
-func (p *FederationServerMockPerson) KeyID(host string) string {
- return fmt.Sprintf("%[1]v/api/v1/activitypub/user-id/%[2]v#main-key", host, p.ID)
-}
-
func NewFederationServerMockRepository(id int64) FederationServerMockRepository {
return FederationServerMockRepository{
ID: id,
}
}
-func NewApActorMock() ApActorMock {
- priv, pub, _ := util.GenerateKeyPair(1024)
- return ApActorMock{
- PrivKey: priv,
- PubKey: pub,
- }
-}
-
-func (u *ApActorMock) KeyID(host string) string {
- return fmt.Sprintf("%[1]v/api/v1/activitypub/actor#main-key", host)
-}
-
func (p FederationServerMockPerson) marshal(host string) string {
return fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+
- `"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+
+ `"id":"http://%[1]v/api/activitypub/user-id/%[2]v",`+
`"type":"Person",`+
`"icon":{"type":"Image","mediaType":"image/png","url":"http://%[1]v/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+
`"url":"http://%[1]v/%[2]v",`+
- `"inbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/inbox",`+
- `"outbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/outbox",`+
+ `"inbox":"http://%[1]v/api/activitypub/user-id/%[2]v/inbox",`+
+ `"outbox":"http://%[1]v/api/activitypub/user-id/%[2]v/outbox",`+
`"preferredUsername":"%[3]v",`+
- `"publicKey":{"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v#main-key",`+
- `"owner":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+
- `"publicKeyPem":%[4]q}}`, host, p.ID, p.Name, p.PubKey)
+ `"publicKey":{"id":"http://%[1]v/api/activitypub/user-id/%[2]v#main-key",`+
+ `"owner":"http://%[1]v/api/activitypub/user-id/%[2]v",`+
+ `"publicKeyPem":%[4]v}}`, host, p.ID, p.Name, p.PubKey)
}
func NewFederationServerMock() *FederationServerMock {
return &FederationServerMock{
- ApActor: NewApActorMock(),
Persons: []FederationServerMockPerson{
NewFederationServerMockPerson(15, "stargoose1"),
NewFederationServerMockPerson(30, "stargoose2"),
@@ -94,18 +71,8 @@ func NewFederationServerMock() *FederationServerMock {
}
}
-func (mock *FederationServerMock) recordLastPost(t *testing.T, req *http.Request) {
- buf := new(strings.Builder)
- _, err := io.Copy(buf, req.Body)
- if err != nil {
- t.Errorf("Error reading body: %q", err)
- }
- mock.LastPost = strings.ReplaceAll(buf.String(), req.Host, "DISTANT_FEDERATION_HOST")
-}
-
func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server {
federatedRoutes := http.NewServeMux()
-
federatedRoutes.HandleFunc("/.well-known/nodeinfo",
func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo
@@ -120,28 +87,30 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server {
`"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+
`"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`)
})
-
for _, person := range mock.Persons {
federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/%v", person.ID),
func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2
fmt.Fprint(res, person.marshal(req.Host))
})
- federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/user-id/%v/inbox", person.ID),
- func(res http.ResponseWriter, req *http.Request) {
- mock.recordLastPost(t, req)
- })
}
-
for _, repository := range mock.Repositories {
- federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/repository-id/%v/inbox", repository.ID),
+ federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID),
func(res http.ResponseWriter, req *http.Request) {
- mock.recordLastPost(t, req)
+ if req.Method != "POST" {
+ t.Errorf("POST expected at: %q", req.URL.EscapedPath())
+ }
+ buf := new(strings.Builder)
+ _, err := io.Copy(buf, req.Body)
+ if err != nil {
+ t.Errorf("Error reading body: %q", err)
+ }
+ mock.LastPost = buf.String()
})
}
federatedRoutes.HandleFunc("/",
func(res http.ResponseWriter, req *http.Request) {
- t.Errorf("Unhandled %v request: %q", req.Method, req.URL.EscapedPath())
+ t.Errorf("Unhandled request: %q", req.URL.EscapedPath())
})
federatedSrv := httptest.NewServer(federatedRoutes)
return federatedSrv
diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go
index 8cb1513a88..262feb2b05 100644
--- a/modules/typesniffer/typesniffer.go
+++ b/modules/typesniffer/typesniffer.go
@@ -124,7 +124,7 @@ func (ct SniffedType) GetMimeType() string {
}
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
-func DetectContentType(data []byte, filename string) SniffedType {
+func DetectContentType(data []byte) SniffedType {
if len(data) == 0 {
return SniffedType{"text/unknown"}
}
@@ -176,13 +176,6 @@ func DetectContentType(data []byte, filename string) SniffedType {
}
}
- if ct == "application/octet-stream" &&
- filename != "" &&
- !strings.HasSuffix(strings.ToUpper(filename), ".LCOM") &&
- bytes.Contains(data, []byte("(DEFINE-FILE-INFO ")) {
- ct = "text/vnd.interlisp"
- }
-
// GLTF is unsupported by http.DetectContentType
// hexdump -n 4 -C glTF.glb
if bytes.HasPrefix(data, []byte("glTF")) {
@@ -193,7 +186,7 @@ func DetectContentType(data []byte, filename string) SniffedType {
}
// DetectContentTypeFromReader guesses the content type contained in the reader.
-func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, error) {
+func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) {
buf := make([]byte, sniffLen)
n, err := util.ReadAtMost(r, buf)
if err != nil {
@@ -201,5 +194,5 @@ func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, err
}
buf = buf[:n]
- return DetectContentType(buf, filename), nil
+ return DetectContentType(buf), nil
}
diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go
index d2b7ed4f21..176d3658bb 100644
--- a/modules/typesniffer/typesniffer_test.go
+++ b/modules/typesniffer/typesniffer_test.go
@@ -16,63 +16,63 @@ import (
func TestDetectContentTypeLongerThanSniffLen(t *testing.T) {
// Pre-condition: Shorter than sniffLen detects SVG.
- assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``), "").contentType)
+ assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``)).contentType)
// Longer than sniffLen detects something else.
- assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(``), "").contentType)
+ assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(``)).contentType)
}
func TestIsTextFile(t *testing.T) {
- assert.True(t, DetectContentType([]byte{}, "").IsText())
- assert.True(t, DetectContentType([]byte("lorem ipsum"), "").IsText())
+ assert.True(t, DetectContentType([]byte{}).IsText())
+ assert.True(t, DetectContentType([]byte("lorem ipsum")).IsText())
}
func TestIsSvgImage(t *testing.T) {
- assert.True(t, DetectContentType([]byte(""), "").IsSvgImage())
- assert.True(t, DetectContentType([]byte(" "), "").IsSvgImage())
- assert.True(t, DetectContentType([]byte(``), "").IsSvgImage())
- assert.True(t, DetectContentType([]byte(``), "").IsSvgImage())
+ assert.True(t, DetectContentType([]byte("")).IsSvgImage())
+ assert.True(t, DetectContentType([]byte(" ")).IsSvgImage())
+ assert.True(t, DetectContentType([]byte(``)).IsSvgImage())
+ assert.True(t, DetectContentType([]byte(``)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`
- `), "").IsSvgImage())
+ `)).IsSvgImage())
// the DetectContentType should work for incomplete data, because only beginning bytes are used for detection
- assert.True(t, DetectContentType([]byte(`