feat: support grouping by any path for arch package (#4903)

Previous arch package grouping was not well-suited for complex or multi-architecture environments. It now supports the following content:

- Support grouping by any path.
- New support for packages in `xz` format.
- Fix clean up rules

<!--start release-notes-assistant-->

## Draft release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/4903): <!--number 4903 --><!--line 0 --><!--description c3VwcG9ydCBncm91cGluZyBieSBhbnkgcGF0aCBmb3IgYXJjaCBwYWNrYWdl-->support grouping by any path for arch package<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4903
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Exploding Dragon <explodingfkl@gmail.com>
Co-committed-by: Exploding Dragon <explodingfkl@gmail.com>
This commit is contained in:
Exploding Dragon 2024-08-11 10:35:11 +00:00 committed by Earl Warren
parent a4da672134
commit 87d50eca87
7 changed files with 309 additions and 218 deletions

View file

@ -143,10 +143,59 @@ func CommonRoutes() *web.Route {
r.Head("", arch.GetRepositoryKey)
r.Get("", arch.GetRepositoryKey)
})
r.Group("/{distro}", func() {
r.Put("", reqPackageAccess(perm.AccessModeWrite), arch.PushPackage)
r.Get("/{arch}/{file}", arch.GetPackageOrDB)
r.Delete("/{package}/{version}", reqPackageAccess(perm.AccessModeWrite), arch.RemovePackage)
r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
pathGroups := strings.Split(strings.Trim(ctx.Params("*"), "/"), "/")
groupLen := len(pathGroups)
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
isPut := ctx.Req.Method == "PUT"
isDelete := ctx.Req.Method == "DELETE"
if isGetHead {
if groupLen < 2 {
ctx.Status(http.StatusNotFound)
return
}
if groupLen == 2 {
ctx.SetParams("group", "")
ctx.SetParams("arch", pathGroups[0])
ctx.SetParams("file", pathGroups[1])
} else {
ctx.SetParams("group", strings.Join(pathGroups[:groupLen-2], "/"))
ctx.SetParams("arch", pathGroups[groupLen-2])
ctx.SetParams("file", pathGroups[groupLen-1])
}
arch.GetPackageOrDB(ctx)
return
} else if isPut {
ctx.SetParams("group", strings.Join(pathGroups, "/"))
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
arch.PushPackage(ctx)
return
} else if isDelete {
if groupLen < 2 {
ctx.Status(http.StatusBadRequest)
return
}
if groupLen == 2 {
ctx.SetParams("group", "")
ctx.SetParams("package", pathGroups[0])
ctx.SetParams("version", pathGroups[1])
} else {
ctx.SetParams("group", strings.Join(pathGroups[:groupLen-2], "/"))
ctx.SetParams("package", pathGroups[groupLen-2])
ctx.SetParams("version", pathGroups[groupLen-1])
}
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
arch.RemovePackage(ctx)
return
}
ctx.Status(http.StatusNotFound)
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/cargo", func() {

View file

@ -9,6 +9,7 @@ import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
@ -21,6 +22,11 @@ import (
arch_service "code.gitea.io/gitea/services/packages/arch"
)
var (
archPkgOrSig = regexp.MustCompile(`^.*\.pkg\.tar\.\w+(\.sig)*$`)
archDBOrSig = regexp.MustCompile(`^.*.db(\.tar\.gz)*(\.sig)*$`)
)
func apiError(ctx *context.Context, status int, obj any) {
helper.LogAndProcessError(ctx, status, obj, func(message string) {
ctx.PlainText(status, message)
@ -41,7 +47,7 @@ func GetRepositoryKey(ctx *context.Context) {
}
func PushPackage(ctx *context.Context) {
distro := ctx.Params("distro")
group := ctx.Params("group")
upload, needToClose, err := ctx.UploadStream()
if err != nil {
@ -61,7 +67,7 @@ func PushPackage(ctx *context.Context) {
p, err := arch_module.ParsePackage(buf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
apiError(ctx, http.StatusBadRequest, err)
return
}
@ -97,7 +103,7 @@ func PushPackage(ctx *context.Context) {
properties := map[string]string{
arch_module.PropertyDescription: p.Desc(),
arch_module.PropertyArch: p.FileMetadata.Arch,
arch_module.PropertyDistribution: distro,
arch_module.PropertyDistribution: group,
}
version, _, err := packages_service.CreatePackageOrAddFileToExisting(
@ -114,8 +120,8 @@ func PushPackage(ctx *context.Context) {
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s-%s.pkg.tar.zst", p.Name, p.Version, p.FileMetadata.Arch),
CompositeKey: distro,
Filename: fmt.Sprintf("%s-%s-%s.pkg.tar.%s", p.Name, p.Version, p.FileMetadata.Arch, p.CompressType),
CompositeKey: group,
},
OverwriteExisting: false,
IsLead: true,
@ -138,8 +144,8 @@ func PushPackage(ctx *context.Context) {
// add sign file
_, err = packages_service.AddFileToPackageVersionInternal(ctx, version, &packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
CompositeKey: distro,
Filename: fmt.Sprintf("%s-%s-%s.pkg.tar.zst.sig", p.Name, p.Version, p.FileMetadata.Arch),
CompositeKey: group,
Filename: fmt.Sprintf("%s-%s-%s.pkg.tar.%s.sig", p.Name, p.Version, p.FileMetadata.Arch, p.CompressType),
},
OverwriteExisting: true,
IsLead: false,
@ -149,7 +155,7 @@ func PushPackage(ctx *context.Context) {
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
}
if err = arch_service.BuildPacmanDB(ctx, ctx.Package.Owner.ID, distro, p.FileMetadata.Arch); err != nil {
if err = arch_service.BuildPacmanDB(ctx, ctx.Package.Owner.ID, group, p.FileMetadata.Arch); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
@ -158,13 +164,12 @@ func PushPackage(ctx *context.Context) {
func GetPackageOrDB(ctx *context.Context) {
var (
file = ctx.Params("file")
distro = ctx.Params("distro")
arch = ctx.Params("arch")
file = ctx.Params("file")
group = ctx.Params("group")
arch = ctx.Params("arch")
)
if strings.HasSuffix(file, ".pkg.tar.zst") || strings.HasSuffix(file, ".pkg.tar.zst.sig") {
pkg, err := arch_service.GetPackageFile(ctx, distro, file, ctx.Package.Owner.ID)
if archPkgOrSig.MatchString(file) {
pkg, err := arch_service.GetPackageFile(ctx, group, file, ctx.Package.Owner.ID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
@ -180,11 +185,8 @@ func GetPackageOrDB(ctx *context.Context) {
return
}
if strings.HasSuffix(file, ".db.tar.gz") ||
strings.HasSuffix(file, ".db") ||
strings.HasSuffix(file, ".db.tar.gz.sig") ||
strings.HasSuffix(file, ".db.sig") {
pkg, err := arch_service.GetPackageDBFile(ctx, distro, arch, ctx.Package.Owner.ID,
if archDBOrSig.MatchString(file) {
pkg, err := arch_service.GetPackageDBFile(ctx, group, arch, ctx.Package.Owner.ID,
strings.HasSuffix(file, ".sig"))
if err != nil {
if errors.Is(err, util.ErrNotExist) {
@ -205,9 +207,9 @@ func GetPackageOrDB(ctx *context.Context) {
func RemovePackage(ctx *context.Context) {
var (
distro = ctx.Params("distro")
pkg = ctx.Params("package")
ver = ctx.Params("version")
group = ctx.Params("group")
pkg = ctx.Params("package")
ver = ctx.Params("version")
)
pv, err := packages_model.GetVersionByNameAndVersion(
ctx, ctx.Package.Owner.ID, packages_model.TypeArch, pkg, ver,
@ -227,7 +229,7 @@ func RemovePackage(ctx *context.Context) {
}
deleted := false
for _, file := range files {
if file.CompositeKey == distro {
if file.CompositeKey == group {
deleted = true
err := packages_service.RemovePackageFileAndVersionIfUnreferenced(ctx, ctx.ContextUser, file)
if err != nil {
@ -237,7 +239,7 @@ func RemovePackage(ctx *context.Context) {
}
}
if deleted {
err = arch_service.BuildCustomRepositoryFiles(ctx, ctx.Package.Owner.ID, distro)
err = arch_service.BuildCustomRepositoryFiles(ctx, ctx.Package.Owner.ID, group)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
}