diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 3d024f5f9e..4dd7567d77 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -32,7 +32,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v6 - id: forgejo - uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.11 + uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.12 with: user: root password: admin1234 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 067e903e59..b3e0cbca90 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -459,6 +459,11 @@ "path": "github.com/chi-middleware/proxy/LICENSE", "licenseText": "Copyright (c) 2020 Lauris BH\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\nall copies 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\nTHE SOFTWARE.\n" }, + { + "name": "github.com/clipperhouse/uax29/v2/graphemes", + "path": "github.com/clipperhouse/uax29/v2/graphemes/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2020 Matt Sherman\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/cloudflare/circl", "path": "github.com/cloudflare/circl/LICENSE", diff --git a/build/lint-locale/lint-locale.go b/build/lint-locale/lint-locale.go index dc4088c73c..03ddab616e 100644 --- a/build/lint-locale/lint-locale.go +++ b/build/lint-locale/lint-locale.go @@ -191,5 +191,24 @@ func main() { } } + if exitCode != 0 { + fmt.Println(dmp.DiffPrettyText([]diffmatchpatch.Diff{{ + Type: diffmatchpatch.DiffEqual, + Text: "Please adjust the locale files as suggested above (", + }, { + Type: diffmatchpatch.DiffDelete, + Text: "red", + }, { + Type: diffmatchpatch.DiffEqual, + Text: ": removal, ", + }, { + Type: diffmatchpatch.DiffInsert, + Text: "green", + }, { + Type: diffmatchpatch.DiffEqual, + Text: ": insertion)", + }})) + } + os.Exit(exitCode) } diff --git a/go.mod b/go.mod index f9454fbec9..e211a1b743 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.1.0 code.forgejo.org/forgejo/reply v1.0.2 - code.forgejo.org/forgejo/runner/v12 v12.10.1 + code.forgejo.org/forgejo/runner/v12 v12.10.2 code.forgejo.org/go-chi/binding v1.0.1 code.forgejo.org/go-chi/cache v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2 @@ -76,7 +76,7 @@ require ( github.com/klauspost/compress v1.18.6 github.com/klauspost/cpuid/v2 v2.3.0 github.com/markbates/goth v1.82.0 - github.com/mattn/go-isatty v0.0.21 + github.com/mattn/go-isatty v0.0.22 github.com/mattn/go-sqlite3 v1.14.44 github.com/meilisearch/meilisearch-go v0.36.2 github.com/mholt/archives v0.1.5 @@ -147,7 +147,7 @@ require ( github.com/blevesearch/zapx/v15 v15.4.3 // indirect github.com/blevesearch/zapx/v16 v16.3.4 // indirect github.com/blevesearch/zapx/v17 v17.1.2 // indirect - github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/sevenzip v1.6.1 // indirect github.com/bodgit/windows v1.0.1 // indirect @@ -156,6 +156,7 @@ require ( github.com/caddyserver/zerossl v0.1.5 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -165,7 +166,7 @@ require ( github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect @@ -206,7 +207,7 @@ require ( 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-runewidth v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.21 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mholt/acmez/v3 v3.1.6 // indirect github.com/miekg/dns v1.1.72 // indirect @@ -232,7 +233,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rhysd/actionlint v1.7.10 // indirect + github.com/rhysd/actionlint v1.7.12 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/xid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 9ec521e8c7..cb2e3a5708 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ code.forgejo.org/forgejo/levelqueue v1.1.0 h1:IgDbeZBdzJhbI8M0hXCQ1qyJIk83cGnBS3 code.forgejo.org/forgejo/levelqueue v1.1.0/go.mod h1:flCo3rqxrybUXQR2I8TFyiDSsLkOjb71CZOEdAuJmgc= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= -code.forgejo.org/forgejo/runner/v12 v12.10.1 h1:qRKjWItVDc2lEMl3jGlKyFpqgX4xHeOdEyl/irO/Nk8= -code.forgejo.org/forgejo/runner/v12 v12.10.1/go.mod h1:A51GyZJlril5cIpVMvOn3NqE8upOE6ePjwC6s31kRHk= +code.forgejo.org/forgejo/runner/v12 v12.10.2 h1:tU4XiBlmMpQwutKL5QYV1FRTkv2FPrbuymYsucNdNWE= +code.forgejo.org/forgejo/runner/v12 v12.10.2/go.mod h1:QnCx84rMQJ+DJ4aS9r2BZL0z2a0ZFaVl9EfMnXYl23g= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= @@ -145,8 +145,8 @@ github.com/blevesearch/zapx/v16 v16.3.4 h1:hDAqA8qusZTNbPEL7//w5P65UZ2de6yhSeUaT github.com/blevesearch/zapx/v16 v16.3.4/go.mod h1:zqkPPqs9GS9FzVWzCO3Wf1X044yWAV17+4zb+FTiEHg= github.com/blevesearch/zapx/v17 v17.1.2 h1:avbOk2igaASNoiy0BE/jPgcxAnRI2PGeydeP4hg7Ikk= github.com/blevesearch/zapx/v17 v17.1.2/go.mod h1:WQObxKrqUX7cd0G1GMvDfc/bmZzQvoy7APOPimx7DiI= -github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= -github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= +github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= @@ -185,6 +185,8 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -242,8 +244,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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -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.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= 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= @@ -510,10 +512,10 @@ github.com/markbates/goth v1.82.0 h1:8j/c34AjBSTNzO7zTsOyP5IYCQCMBTRBHAbBt/PI0bQ github.com/markbates/goth v1.82.0/go.mod h1:/DRlcq0pyqkKToyZjsL2KgiA1zbF1HIjE7u2uC79rUk= 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-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= -github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mattn/go-runewidth v0.0.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ= -github.com/mattn/go-runewidth v0.0.17/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8= @@ -604,9 +606,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= -github.com/rhysd/actionlint v1.7.10 h1:FL3XIEs72G4/++168vlv5FKOWMSWvWIQw1kBCadyOcM= -github.com/rhysd/actionlint v1.7.10/go.mod h1:ZHX/hrmknlsJN73InPTKsKdXpAv9wVdrJy8h8HAwFHg= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rhysd/actionlint v1.7.12 h1:vQ4GeJN86C0QH+gTUQcs8McmK62OLT3kmakPMtEWYnY= +github.com/rhysd/actionlint v1.7.12/go.mod h1:krOUhujIsJusovkaYzQ/VNH8PFexjNKqU0q5XI/4w+g= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= diff --git a/models/packages/debian/search.go b/models/packages/debian/search.go index c528a25194..f7b56f4fc2 100644 --- a/models/packages/debian/search.go +++ b/models/packages/debian/search.go @@ -76,22 +76,35 @@ func ExistPackages(ctx context.Context, opts *PackageSearchOptions) (bool, error // SearchPackages gets the packages matching the search options func SearchPackages(ctx context.Context, opts *PackageSearchOptions, iter func(*packages.PackageFileDescriptor)) error { - return db.GetEngine(ctx). + // Forgejo's db library doesn't have an iterate method that allows us to do this query, *sorted*, in chunks. xorm's + // `Iterate` isn't usable because sometimes we are in a transaction, and GetPackageFileDescriptor will make + // supplemental DB calls which will fail in xorm's Iterate as the same connection will come back from + // db.GetEngine(ctx) due to the transaction, and that connection is already being used for the iterate. + // + // Aside from the reasons we can't do this iteratively, `SearchPackages` is only used when we're rebuilding the + // package index, and the caller `buildPackagesIndices` is already building *three* in-memory files referencing all + // this same information -- so the memory usage to load this information in one chunk shouldn't be significantly + // worse than the index rebuild side anyway. + pfs := make([]*packages.PackageFile, 0, 100) + err := db.GetEngine(ctx). Table("package_file"). Select("package_file.*"). Join("INNER", "package_version", "package_version.id = package_file.version_id"). Join("INNER", "package", "package.id = package_version.package_id"). Where(opts.toCond()). Asc("package.lower_name", "package_version.created_unix"). - Iterate(&packages.PackageFile{}, func(i int, bean any) error { - pf := bean.(*packages.PackageFile) - pfd, err := packages.GetPackageFileDescriptor(ctx, pf) - if err != nil { - return err - } - iter(pfd) - return nil - }) + Find(&pfs) + if err != nil { + return err + } + for _, pf := range pfs { + pfd, err := packages.GetPackageFileDescriptor(ctx, pf) + if err != nil { + return err + } + iter(pfd) + } + return nil } // GetDistributions gets all available distributions diff --git a/package-lock.json b/package-lock.json index fa0042caa5..75c94329d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "chart.js": "4.5.1", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", - "clippie": "4.1.15", + "clippie": "4.2.0", "css-loader": "7.1.3", "dayjs": "1.11.19", "dropzone": "6.0.0-beta.2", @@ -6296,9 +6296,9 @@ } }, "node_modules/clippie": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.15.tgz", - "integrity": "sha512-K4z5MF32z7Gr1fUcfuUZvmrzpdNs5q8zAm2yqsWhA8mZ9Q6GL4HNdR2k3h3ubEEEGoyb8fvMA48LDr2MKYDASA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.2.0.tgz", + "integrity": "sha512-NcWaVzqChJ69+foFhwJXta3KXWNDJlpicxcfZG5udobyszOSBDhmFubKv1b/1nIZiVAsPoKqME2iV1SITZqFoQ==", "license": "BSD-2-Clause" }, "node_modules/cliui": { diff --git a/package.json b/package.json index 3cf78b624a..40582adfac 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "chart.js": "4.5.1", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", - "clippie": "4.1.15", + "clippie": "4.2.0", "css-loader": "7.1.3", "dayjs": "1.11.19", "dropzone": "6.0.0-beta.2", diff --git a/services/lfs/server.go b/services/lfs/server.go index 1dfd58a3c0..41401e8bfd 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -183,7 +183,7 @@ func BatchHandler(ctx *context.Context) { } if isUpload { - ok, err := quota_model.EvaluateForUser(ctx, ctx.Doer.ID, quota_model.LimitSubjectSizeGitLFS) + ok, err := quota_model.EvaluateForUser(ctx, repository.OwnerID, quota_model.LimitSubjectSizeGitLFS) if err != nil { log.Error("quota_model.EvaluateForUser: %v", err) writeStatus(ctx, http.StatusInternalServerError) @@ -191,6 +191,7 @@ func BatchHandler(ctx *context.Context) { } if !ok { writeStatusMessage(ctx, http.StatusRequestEntityTooLarge, "quota exceeded") + return } } @@ -317,8 +318,8 @@ func UploadHandler(ctx *context.Context) { return } - if exists { - ok, err := quota_model.EvaluateForUser(ctx, ctx.Doer.ID, quota_model.LimitSubjectSizeGitLFS) + if !exists { + ok, err := quota_model.EvaluateForUser(ctx, repository.OwnerID, quota_model.LimitSubjectSizeGitLFS) if err != nil { log.Error("quota_model.EvaluateForUser: %v", err) writeStatus(ctx, http.StatusInternalServerError) @@ -326,6 +327,7 @@ func UploadHandler(ctx *context.Context) { } if !ok { writeStatusMessage(ctx, http.StatusRequestEntityTooLarge, "quota exceeded") + return } } diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go index 44c0057941..025bad6648 100644 --- a/tests/integration/api_packages_test.go +++ b/tests/integration/api_packages_test.go @@ -528,9 +528,7 @@ func TestPackageCleanup(t *testing.T) { duration, _ := time.ParseDuration("-1h") t.Run("Debian", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - // Debian does a repository rebuild. - + // Debian does a repository rebuild; these tests cover validation of that process. distribution := "forgejo" component := "main" architecture := "amd64" @@ -540,27 +538,64 @@ func TestPackageCleanup(t *testing.T) { rootURL := fmt.Sprintf("/api/packages/%s/debian", user.Name) uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component) - req := NewRequestWithBody(t, "PUT", uploadURL, - createDebianArchive(packageName, "1.0.0", architecture, packageDescription)). - AddBasicAuth(user.Name) - MakeRequest(t, req, http.StatusCreated) + t.Run("empty repository after cleanup", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - resp := MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusOK) - assert.Contains(t, resp.Body.String(), "pool/forgejo/main/runner_1.0.0_amd64.deb") + req := NewRequestWithBody(t, "PUT", uploadURL, + createDebianArchive(packageName, "1.0.0", architecture, packageDescription)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) - pcr, err := packages_model.InsertCleanupRule(t.Context(), &packages_model.PackageCleanupRule{ - Enabled: true, - RemovePattern: `.+`, - OwnerID: user.ID, - Type: packages_model.TypeDebian, + resp := MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusOK) + assert.Contains(t, resp.Body.String(), "pool/forgejo/main/runner_1.0.0_amd64.deb") + + pcr, err := packages_model.InsertCleanupRule(t.Context(), &packages_model.PackageCleanupRule{ + Enabled: true, + RemovePattern: `.+`, + OwnerID: user.ID, + Type: packages_model.TypeDebian, + }) + require.NoError(t, err) + + require.NoError(t, packages_cleanup_service.CleanupTask(t.Context(), duration)) + + MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusNotFound) + + require.NoError(t, packages_model.DeleteCleanupRuleByID(t.Context(), pcr.ID)) }) - require.NoError(t, err) - require.NoError(t, packages_cleanup_service.CleanupTask(t.Context(), duration)) + t.Run("non-empty repository after cleanup", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusNotFound) + req := NewRequestWithBody(t, "PUT", uploadURL, + createDebianArchive(packageName, "1.0.0", architecture, packageDescription)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) + req = NewRequestWithBody(t, "PUT", uploadURL, + createDebianArchive(packageName, "1.0.1", architecture, packageDescription)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) - require.NoError(t, packages_model.DeleteCleanupRuleByID(t.Context(), pcr.ID)) + resp := MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusOK) + assert.Contains(t, resp.Body.String(), "pool/forgejo/main/runner_1.0.0_amd64.deb") + assert.Contains(t, resp.Body.String(), "pool/forgejo/main/runner_1.0.1_amd64.deb") + + pcr, err := packages_model.InsertCleanupRule(t.Context(), &packages_model.PackageCleanupRule{ + Enabled: true, + RemovePattern: `.+`, + OwnerID: user.ID, + Type: packages_model.TypeDebian, + KeepCount: 1, + }) + require.NoError(t, err) + + require.NoError(t, packages_cleanup_service.CleanupTask(t.Context(), duration)) + + resp = MakeRequest(t, NewRequestf(t, "GET", "%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture), http.StatusOK) + assert.Contains(t, resp.Body.String(), "pool/forgejo/main/runner_1.0.1_amd64.deb") + + require.NoError(t, packages_model.DeleteCleanupRuleByID(t.Context(), pcr.ID)) + }) }) t.Run("Common", func(t *testing.T) { diff --git a/tests/integration/quota_use_test.go b/tests/integration/quota_use_test.go index fb9619adee..3c518df8b5 100644 --- a/tests/integration/quota_use_test.go +++ b/tests/integration/quota_use_test.go @@ -16,11 +16,13 @@ import ( "testing" "forgejo.org/models/db" + git_model "forgejo.org/models/git" org_model "forgejo.org/models/organization" quota_model "forgejo.org/models/quota" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" "forgejo.org/modules/git" + "forgejo.org/modules/lfs" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" "forgejo.org/modules/test" @@ -365,7 +367,7 @@ func TestWebQuotaEnforcementRepoTransfer(t *testing.T) { }) } -func TestGitQuotaEnforcement(t *testing.T) { +func TestQuotaGitEnforcement(t *testing.T) { onApplicationRun(t, func(t *testing.T, u *url.URL) { env := createQuotaWebEnv(t) defer env.Cleanup() @@ -548,6 +550,55 @@ func TestGitQuotaEnforcement(t *testing.T) { }) } +func TestQuotaGitLfsEnforcement(t *testing.T) { + defer test.MockVariableValue(&setting.LFS.StartServer, true)() + + onApplicationRun(t, func(t *testing.T, u *url.URL) { + env := createQuotaWebEnv(t) + defer env.Cleanup() + + t.Run("UploadHandler", func(t *testing.T) { + // Uploading to our repo => 413 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Users.Limited.Repo}). + PushLFSObject(). + ExpectStatus(http.StatusRequestEntityTooLarge) + + // Uploading to the limited org repo => 413 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Orgs.Limited.Repo}). + PushLFSObject(). + ExpectStatus(http.StatusRequestEntityTooLarge) + + // Uploading to the unlimited org repo => 200 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Orgs.Unlimited.Repo}). + PushLFSObject(). + ExpectStatus(http.StatusOK) + }) + + t.Run("BatchHandler", func(t *testing.T) { + // Uploading to our repo => 413 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Users.Limited.Repo}). + BatchPushLFSObject(). + ExpectStatus(http.StatusRequestEntityTooLarge) + + // Uploading to the limited org repo => 413 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Orgs.Limited.Repo}). + BatchPushLFSObject(). + ExpectStatus(http.StatusRequestEntityTooLarge) + + // Uploading to the unlimited org repo => 200 + env.As(t, env.Users.Limited). + With(Context{Repo: env.Orgs.Unlimited.Repo}). + BatchPushLFSObject(). + ExpectStatus(http.StatusOK) + }) + }) +} + func TestQuotaConfigDefault(t *testing.T) { onApplicationRun(t, func(t *testing.T, u *url.URL) { env := createQuotaWebEnv(t) @@ -793,6 +844,42 @@ func (ctx *quotaWebEnvAsContext) CreateReleaseAttachment(filename string) *quota return ctx.CreateAttachment(filename, "releases") } +func (ctx *quotaWebEnvAsContext) PushLFSObject() *quotaWebEnvAsContext { + ctx.t.Helper() + + p := lfs.Pointer{Oid: "6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d", Size: 5} + ctx.request = NewRequestWithBody(ctx.t, "PUT", + fmt.Sprintf("%s.git/info/lfs/objects/%s/%d", + ctx.Repo.Link(), p.Oid, p.Size), strings.NewReader("gitea")) + + ctx.t.Cleanup(func() { + git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, ctx.Repo.ID, p.Oid) + }) + + return ctx +} + +func (ctx *quotaWebEnvAsContext) BatchPushLFSObject() *quotaWebEnvAsContext { + ctx.t.Helper() + + batch := &lfs.BatchRequest{ + Operation: "upload", + Objects: []lfs.Pointer{ + {Oid: "d6f175817f886ec6fbbc1515326465fa96c3bfd54a4ea06cfd6dbbd8340e0153", Size: 1}, + }, + } + ctx.request = NewRequestWithJSON(ctx.t, "POST", + fmt.Sprintf("%s.git/info/lfs/objects/batch", ctx.Repo.Link()), batch). + SetHeader("Accept", lfs.AcceptHeader). + SetHeader("Content-Type", lfs.MediaType) + + ctx.t.Cleanup(func() { + git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, ctx.Repo.ID, batch.Objects[0].Oid) + }) + + return ctx +} + func (ctx *quotaWebEnvAsContext) WithoutQuota(task func(ctx *quotaWebEnvAsContext)) *quotaWebEnvAsContext { ctx.t.Helper() diff --git a/web_src/css/actions.css b/web_src/css/actions.css index 82149378b9..e61fac84b7 100644 --- a/web_src/css/actions.css +++ b/web_src/css/actions.css @@ -177,8 +177,8 @@ white-space: nowrap; } #workflow_dispatch_dropdown .menu { + /* FIXME: max-height is ineffective without `overflow:`, but clipping overflow breaks dropdown menus */ max-height: 500px; - overflow-inline: auto; } @media (max-width: 640px) or (767.98px < width < 854px) { #workflow_dispatch_dropdown .menu {