aboutsummaryrefslogtreecommitdiffstats
path: root/routers/api
diff options
context:
space:
mode:
Diffstat (limited to 'routers/api')
-rw-r--r--routers/api/packages/api.go14
-rw-r--r--routers/api/packages/cargo/cargo.go5
-rw-r--r--routers/api/packages/composer/composer.go5
-rw-r--r--routers/api/packages/container/blob.go10
-rw-r--r--routers/api/packages/container/container.go34
-rw-r--r--routers/api/packages/container/manifest.go132
-rw-r--r--routers/api/packages/nuget/api_v2.go46
-rw-r--r--routers/api/packages/rubygems/rubygems.go48
-rw-r--r--routers/api/packages/rubygems/rubygems_test.go41
-rw-r--r--routers/api/v1/repo/issue_dependency.go10
-rw-r--r--routers/api/v1/repo/migrate.go2
-rw-r--r--routers/api/v1/repo/release.go4
-rw-r--r--routers/api/v1/repo/wiki.go10
13 files changed, 235 insertions, 126 deletions
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index ae4ea7ea87..41e89ae567 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -701,18 +701,18 @@ func ContainerRoutes() *web.Router {
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
r.Group("/{username}", func() {
r.PathGroup("/*", func(g *web.RouterPathGroup) {
- g.MatchPath("POST", "/<image:*>/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.InitiateUploadBlob)
- g.MatchPath("GET", "/<image:*>/tags/list", container.VerifyImageName, container.GetTagList)
+ g.MatchPath("POST", "/<image:*>/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.PostBlobsUploads)
+ g.MatchPath("GET", "/<image:*>/tags/list", container.VerifyImageName, container.GetTagsList)
g.MatchPath("GET,PATCH,PUT,DELETE", `/<image:*>/blobs/uploads/<uuid:[-.=\w]+>`, reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, func(ctx *context.Context) {
switch ctx.Req.Method {
case http.MethodGet:
- container.GetUploadBlob(ctx)
+ container.GetBlobsUpload(ctx)
case http.MethodPatch:
- container.UploadBlob(ctx)
+ container.PatchBlobsUpload(ctx)
case http.MethodPut:
- container.EndUploadBlob(ctx)
+ container.PutBlobsUpload(ctx)
default: /* DELETE */
- container.CancelUploadBlob(ctx)
+ container.DeleteBlobsUpload(ctx)
}
})
g.MatchPath("HEAD", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.HeadBlob)
@@ -721,7 +721,7 @@ func ContainerRoutes() *web.Router {
g.MatchPath("HEAD", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.HeadManifest)
g.MatchPath("GET", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.GetManifest)
- g.MatchPath("PUT", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.UploadManifest)
+ g.MatchPath("PUT", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.PutManifest)
g.MatchPath("DELETE", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
})
}, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go
index 710c614c6e..57cb83404f 100644
--- a/routers/api/packages/cargo/cargo.go
+++ b/routers/api/packages/cargo/cargo.go
@@ -95,10 +95,7 @@ type SearchResultMeta struct {
// https://doc.rust-lang.org/cargo/reference/registries.html#search
func SearchPackages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
perPage := ctx.FormInt("per_page")
paginator := db.ListOptions{
Page: page,
diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go
index c6c14e5cf4..3713805579 100644
--- a/routers/api/packages/composer/composer.go
+++ b/routers/api/packages/composer/composer.go
@@ -53,10 +53,7 @@ func ServiceIndex(ctx *context.Context) {
// SearchPackages searches packages, only "q" is supported
// https://packagist.org/apidoc#search-packages
func SearchPackages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
perPage := ctx.FormInt("per_page")
paginator := db.ListOptions{
Page: page,
diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go
index 4a2320ab76..2ea9b3839c 100644
--- a/routers/api/packages/container/blob.go
+++ b/routers/api/packages/container/blob.go
@@ -20,6 +20,8 @@ import (
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
+
+ "github.com/opencontainers/go-digest"
)
// saveAsPackageBlob creates a package blob from an upload
@@ -128,8 +130,8 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
pv := &packages_model.PackageVersion{
PackageID: p.ID,
CreatorID: pi.Owner.ID,
- Version: container_model.UploadVersion,
- LowerVersion: container_model.UploadVersion,
+ Version: container_module.UploadVersion,
+ LowerVersion: container_module.UploadVersion,
IsInternal: true,
MetadataJSON: "null",
}
@@ -175,7 +177,7 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
return nil
}
-func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error {
+func deleteBlob(ctx context.Context, ownerID int64, image string, digest digest.Digest) error {
releaser, err := globallock.Lock(ctx, containerPkgName(ownerID, image))
if err != nil {
return err
@@ -186,7 +188,7 @@ func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error
pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
OwnerID: ownerID,
Image: image,
- Digest: digest,
+ Digest: string(digest),
})
if err != nil {
return err
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 2316657498..477c3bc71a 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -231,7 +231,7 @@ func GetRepositoryList(ctx *context.Context) {
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func InitiateUploadBlob(ctx *context.Context) {
+func PostBlobsUploads(ctx *context.Context) {
image := ctx.PathParam("image")
mount := ctx.FormTrim("mount")
@@ -319,7 +319,7 @@ func InitiateUploadBlob(ctx *context.Context) {
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func GetUploadBlob(ctx *context.Context) {
+func GetBlobsUpload(ctx *context.Context) {
uuid := ctx.PathParam("uuid")
upload, err := packages_model.GetBlobUploadByID(ctx, uuid)
@@ -345,7 +345,7 @@ func GetUploadBlob(ctx *context.Context) {
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func UploadBlob(ctx *context.Context) {
+func PatchBlobsUpload(ctx *context.Context) {
image := ctx.PathParam("image")
uploader, err := container_service.NewBlobUploader(ctx, ctx.PathParam("uuid"))
@@ -393,7 +393,7 @@ func UploadBlob(ctx *context.Context) {
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func EndUploadBlob(ctx *context.Context) {
+func PutBlobsUpload(ctx *context.Context) {
image := ctx.PathParam("image")
digest := ctx.FormTrim("digest")
@@ -462,7 +462,7 @@ func EndUploadBlob(ctx *context.Context) {
}
// https://docs.docker.com/registry/spec/api/#delete-blob-upload
-func CancelUploadBlob(ctx *context.Context) {
+func DeleteBlobsUpload(ctx *context.Context) {
uuid := ctx.PathParam("uuid")
_, err := packages_model.GetBlobUploadByID(ctx, uuid)
@@ -486,16 +486,15 @@ func CancelUploadBlob(ctx *context.Context) {
}
func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) {
- d := ctx.PathParam("digest")
-
- if digest.Digest(d).Validate() != nil {
+ d := digest.Digest(ctx.PathParam("digest"))
+ if d.Validate() != nil {
return nil, container_model.ErrContainerBlobNotExist
}
return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Image: ctx.PathParam("image"),
- Digest: d,
+ Digest: string(d),
})
}
@@ -535,9 +534,8 @@ func GetBlob(ctx *context.Context) {
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs
func DeleteBlob(ctx *context.Context) {
- d := ctx.PathParam("digest")
-
- if digest.Digest(d).Validate() != nil {
+ d := digest.Digest(ctx.PathParam("digest"))
+ if d.Validate() != nil {
apiErrorDefined(ctx, errBlobUnknown)
return
}
@@ -553,7 +551,7 @@ func DeleteBlob(ctx *context.Context) {
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
-func UploadManifest(ctx *context.Context) {
+func PutManifest(ctx *context.Context) {
reference := ctx.PathParam("reference")
mci := &manifestCreationInfo{
@@ -609,18 +607,18 @@ func UploadManifest(ctx *context.Context) {
}
func getBlobSearchOptionsFromContext(ctx *context.Context) (*container_model.BlobSearchOptions, error) {
- reference := ctx.PathParam("reference")
-
opts := &container_model.BlobSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Image: ctx.PathParam("image"),
IsManifest: true,
}
- if digest.Digest(reference).Validate() == nil {
- opts.Digest = reference
+ reference := ctx.PathParam("reference")
+ if d := digest.Digest(reference); d.Validate() == nil {
+ opts.Digest = string(d)
} else if referencePattern.MatchString(reference) {
opts.Tag = reference
+ opts.OnlyLead = true
} else {
return nil, container_model.ErrContainerBlobNotExist
}
@@ -737,7 +735,7 @@ func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor)
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
-func GetTagList(ctx *context.Context) {
+func GetTagsList(ctx *context.Context) {
image := ctx.PathParam("image")
if _, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.TypeContainer, image); err != nil {
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index 26faa7b024..0cbd46e943 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -10,6 +10,7 @@ import (
"io"
"os"
"strings"
+ "time"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
@@ -23,19 +24,19 @@ import (
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
oci "github.com/opencontainers/image-spec/specs-go/v1"
)
-func isValidMediaType(mt string) bool {
+func isMediaTypeValid(mt string) bool {
return strings.HasPrefix(mt, "application/vnd.docker.") || strings.HasPrefix(mt, "application/vnd.oci.")
}
-func isImageManifestMediaType(mt string) bool {
+func isMediaTypeImageManifest(mt string) bool {
return strings.EqualFold(mt, oci.MediaTypeImageManifest) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.v2+json")
}
-func isImageIndexMediaType(mt string) bool {
+func isMediaTypeImageIndex(mt string) bool {
return strings.EqualFold(mt, oci.MediaTypeImageIndex) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.list.v2+json")
}
@@ -64,22 +65,22 @@ func processManifest(ctx context.Context, mci *manifestCreationInfo, buf *packag
return "", err
}
- if !isValidMediaType(mci.MediaType) {
+ if !isMediaTypeValid(mci.MediaType) {
mci.MediaType = index.MediaType
- if !isValidMediaType(mci.MediaType) {
+ if !isMediaTypeValid(mci.MediaType) {
return "", errManifestInvalid.WithMessage("MediaType not recognized")
}
}
- if isImageManifestMediaType(mci.MediaType) {
- return processImageManifest(ctx, mci, buf)
- } else if isImageIndexMediaType(mci.MediaType) {
- return processImageManifestIndex(ctx, mci, buf)
+ if isMediaTypeImageManifest(mci.MediaType) {
+ return processOciImageManifest(ctx, mci, buf)
+ } else if isMediaTypeImageIndex(mci.MediaType) {
+ return processOciImageIndex(ctx, mci, buf)
}
return "", errManifestInvalid
}
-func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
+func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := ""
err := func() error {
@@ -150,13 +151,13 @@ func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *p
return err
}
- uploadVersion, err := packages_model.GetInternalVersionByNameAndVersion(ctx, mci.Owner.ID, packages_model.TypeContainer, mci.Image, container_model.UploadVersion)
+ uploadVersion, err := packages_model.GetInternalVersionByNameAndVersion(ctx, mci.Owner.ID, packages_model.TypeContainer, mci.Image, container_module.UploadVersion)
if err != nil && err != packages_model.ErrPackageNotExist {
return err
}
for _, ref := range blobReferences {
- if err := createFileFromBlobReference(ctx, pv, uploadVersion, ref); err != nil {
+ if _, err = createFileFromBlobReference(ctx, pv, uploadVersion, ref); err != nil {
return err
}
}
@@ -196,7 +197,7 @@ func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *p
return manifestDigest, nil
}
-func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
+func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := ""
err := func() error {
@@ -221,7 +222,7 @@ func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, b
}
for _, manifest := range index.Manifests {
- if !isImageManifestMediaType(manifest.MediaType) {
+ if !isMediaTypeImageManifest(manifest.MediaType) {
return errManifestInvalid
}
@@ -349,24 +350,31 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
LowerVersion: strings.ToLower(mci.Reference),
MetadataJSON: string(metadataJSON),
}
- var pv *packages_model.PackageVersion
- if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
+ pv, err := packages_model.GetOrInsertVersion(ctx, _pv)
+ if err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return nil, err
}
- if err = packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
- return nil, err
- }
-
- // keep download count on overwrite
- _pv.DownloadCount = pv.DownloadCount
-
- if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
- if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
- log.Error("Error inserting package: %v", err)
- return nil, err
+ if isMediaTypeImageIndex(mci.MediaType) {
+ if pv.CreatedUnix.AsTime().Before(time.Now().Add(-24 * time.Hour)) {
+ if err = packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
+ return nil, err
+ }
+ // keep download count on overwriting
+ _pv.DownloadCount = pv.DownloadCount
+ if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
+ if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
+ log.Error("Error inserting package: %v", err)
+ return nil, err
+ }
+ }
+ } else {
+ err = packages_model.UpdateVersion(ctx, &packages_model.PackageVersion{ID: pv.ID, MetadataJSON: _pv.MetadataJSON})
+ if err != nil {
+ return nil, err
+ }
}
}
}
@@ -376,14 +384,23 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
}
if mci.IsTagged {
- if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
- log.Error("Error setting package version property: %v", err)
+ if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
return nil, err
}
+ } else {
+ props, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged)
+ if err != nil {
+ return nil, err
+ }
+ for _, prop := range props {
+ if err = packages_model.DeletePropertyByID(ctx, prop.ID); err != nil {
+ return nil, err
+ }
+ }
}
+
for _, manifest := range metadata.Manifests {
- if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
- log.Error("Error setting package version property: %v", err)
+ if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
return nil, err
}
}
@@ -400,9 +417,9 @@ type blobReference struct {
IsLead bool
}
-func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *packages_model.PackageVersion, ref *blobReference) error {
+func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *packages_model.PackageVersion, ref *blobReference) (*packages_model.PackageFile, error) {
if ref.File.Blob.Size != ref.ExpectedSize {
- return errSizeInvalid
+ return nil, errSizeInvalid
}
if ref.Name == "" {
@@ -410,20 +427,21 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package
}
pf := &packages_model.PackageFile{
- VersionID: pv.ID,
- BlobID: ref.File.Blob.ID,
- Name: ref.Name,
- LowerName: ref.Name,
- IsLead: ref.IsLead,
+ VersionID: pv.ID,
+ BlobID: ref.File.Blob.ID,
+ Name: ref.Name,
+ LowerName: ref.Name,
+ CompositeKey: string(ref.Digest),
+ IsLead: ref.IsLead,
}
var err error
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
// Skip this blob because the manifest contains the same filesystem layer multiple times.
- return nil
+ return pf, nil
}
log.Error("Error inserting package file: %v", err)
- return err
+ return nil, err
}
props := map[string]string{
@@ -433,18 +451,18 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package
for name, value := range props {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, name, value); err != nil {
log.Error("Error setting package file property: %v", err)
- return err
+ return nil, err
}
}
- // Remove the file from the blob upload version
+ // Remove the ref file (old file) from the blob upload version
if uploadVersion != nil && ref.File.File != nil && uploadVersion.ID == ref.File.File.VersionID {
if err := packages_service.DeletePackageFile(ctx, ref.File.File); err != nil {
- return err
+ return nil, err
}
}
- return nil
+ return pf, nil
}
func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *packages_model.PackageVersion, buf *packages_module.HashedBuffer) (*packages_model.PackageBlob, bool, string, error) {
@@ -471,14 +489,34 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack
}
manifestDigest := digestFromHashSummer(buf)
- err = createFileFromBlobReference(ctx, pv, nil, &blobReference{
+ pf, err := createFileFromBlobReference(ctx, pv, nil, &blobReference{
Digest: digest.Digest(manifestDigest),
MediaType: mci.MediaType,
- Name: container_model.ManifestFilename,
+ Name: container_module.ManifestFilename,
File: &packages_model.PackageFileDescriptor{Blob: pb},
ExpectedSize: pb.Size,
IsLead: true,
})
+ if err != nil {
+ return nil, false, "", err
+ }
+ oldManifestFiles, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{
+ OwnerID: mci.Owner.ID,
+ PackageType: packages_model.TypeContainer,
+ VersionID: pv.ID,
+ Query: container_module.ManifestFilename,
+ })
+ if err != nil {
+ return nil, false, "", err
+ }
+ for _, oldManifestFile := range oldManifestFiles {
+ if oldManifestFile.ID != pf.ID && oldManifestFile.IsLead {
+ err = packages_model.UpdateFile(ctx, &packages_model.PackageFile{ID: oldManifestFile.ID, IsLead: false}, []string{"is_lead"})
+ if err != nil {
+ return nil, false, "", err
+ }
+ }
+ }
return pb, !exists, manifestDigest, err
}
diff --git a/routers/api/packages/nuget/api_v2.go b/routers/api/packages/nuget/api_v2.go
index a726065ad0..801c60af13 100644
--- a/routers/api/packages/nuget/api_v2.go
+++ b/routers/api/packages/nuget/api_v2.go
@@ -246,21 +246,30 @@ type TypedValue[T any] struct {
}
type FeedEntryProperties struct {
- Version string `xml:"d:Version"`
- NormalizedVersion string `xml:"d:NormalizedVersion"`
Authors string `xml:"d:Authors"`
+ Copyright string `xml:"d:Copyright,omitempty"`
+ Created TypedValue[time.Time] `xml:"d:Created"`
Dependencies string `xml:"d:Dependencies"`
Description string `xml:"d:Description"`
- VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
+ DevelopmentDependency TypedValue[bool] `xml:"d:DevelopmentDependency"`
DownloadCount TypedValue[int64] `xml:"d:DownloadCount"`
- PackageSize TypedValue[int64] `xml:"d:PackageSize"`
- Created TypedValue[time.Time] `xml:"d:Created"`
+ ID string `xml:"d:Id"`
+ IconURL string `xml:"d:IconUrl,omitempty"`
+ Language string `xml:"d:Language,omitempty"`
LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"`
- Published TypedValue[time.Time] `xml:"d:Published"`
+ LicenseURL string `xml:"d:LicenseUrl,omitempty"`
+ MinClientVersion string `xml:"d:MinClientVersion,omitempty"`
+ NormalizedVersion string `xml:"d:NormalizedVersion"`
+ Owners string `xml:"d:Owners,omitempty"`
+ PackageSize TypedValue[int64] `xml:"d:PackageSize"`
ProjectURL string `xml:"d:ProjectUrl,omitempty"`
+ Published TypedValue[time.Time] `xml:"d:Published"`
ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"`
RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"`
- Title string `xml:"d:Title"`
+ Tags string `xml:"d:Tags,omitempty"`
+ Title string `xml:"d:Title,omitempty"`
+ Version string `xml:"d:Version"`
+ VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
}
type FeedEntry struct {
@@ -353,21 +362,30 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames
Author: metadata.Authors,
Content: content,
Properties: &FeedEntryProperties{
- Version: pd.Version.Version,
- NormalizedVersion: pd.Version.Version,
Authors: metadata.Authors,
+ Copyright: metadata.Copyright,
+ Created: createdValue,
Dependencies: buildDependencyString(metadata),
Description: metadata.Description,
- VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
+ DevelopmentDependency: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.DevelopmentDependency},
DownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
- PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()},
- Created: createdValue,
+ ID: pd.Package.Name,
+ IconURL: metadata.IconURL,
+ Language: metadata.Language,
LastUpdated: createdValue,
- Published: createdValue,
+ LicenseURL: metadata.LicenseURL,
+ MinClientVersion: metadata.MinClientVersion,
+ NormalizedVersion: pd.Version.Version,
+ Owners: metadata.Owners,
+ PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()},
ProjectURL: metadata.ProjectURL,
+ Published: createdValue,
ReleaseNotes: metadata.ReleaseNotes,
RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance},
- Title: pd.Package.Name,
+ Tags: metadata.Tags,
+ Title: metadata.Title,
+ Version: pd.Version.Version,
+ VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
},
}
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index de8c7ef3ed..cb880c8bdb 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -14,6 +14,7 @@ import (
"strings"
packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/optional"
packages_module "code.gitea.io/gitea/modules/packages"
rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
@@ -309,7 +310,7 @@ func GetPackageInfo(ctx *context.Context) {
apiError(ctx, http.StatusNotFound, nil)
return
}
- infoContent, err := makePackageInfo(ctx, versions)
+ infoContent, err := makePackageInfo(ctx, versions, cache.NewEphemeralCache())
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -317,7 +318,7 @@ func GetPackageInfo(ctx *context.Context) {
ctx.PlainText(http.StatusOK, infoContent)
}
-// GetAllPackagesVersions returns a custom text based format containing information about all versions of all rubygems.
+// GetAllPackagesVersions returns a custom text-based format containing information about all versions of all rubygems.
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
func GetAllPackagesVersions(ctx *context.Context) {
packages, err := packages_model.GetPackagesByType(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems)
@@ -326,6 +327,7 @@ func GetAllPackagesVersions(ctx *context.Context) {
return
}
+ ephemeralCache := cache.NewEphemeralCache()
out := &strings.Builder{}
out.WriteString("---\n")
for _, pkg := range packages {
@@ -338,7 +340,7 @@ func GetAllPackagesVersions(ctx *context.Context) {
continue
}
- info, err := makePackageInfo(ctx, versions)
+ info, err := makePackageInfo(ctx, versions, ephemeralCache)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -348,7 +350,14 @@ func GetAllPackagesVersions(ctx *context.Context) {
_, _ = fmt.Fprintf(out, "%s ", pkg.Name)
for i, v := range versions {
sep := util.Iif(i == len(versions)-1, "", ",")
- _, _ = fmt.Fprintf(out, "%s%s", v.Version, sep)
+ pd, err := packages_model.GetPackageDescriptorWithCache(ctx, v, ephemeralCache)
+ if errors.Is(err, util.ErrNotExist) {
+ continue
+ } else if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ writePackageVersionForList(pd.Metadata, v.Version, sep, out)
}
_, _ = fmt.Fprintf(out, " %x\n", md5.Sum([]byte(info)))
}
@@ -356,6 +365,16 @@ func GetAllPackagesVersions(ctx *context.Context) {
ctx.PlainText(http.StatusOK, out.String())
}
+func writePackageVersionForList(metadata any, version, sep string, out *strings.Builder) {
+ if metadata, _ := metadata.(*rubygems_module.Metadata); metadata != nil && metadata.Platform != "" && metadata.Platform != "ruby" {
+ // VERSION_PLATFORM (see comment above in GetAllPackagesVersions)
+ _, _ = fmt.Fprintf(out, "%s_%s%s", version, metadata.Platform, sep)
+ } else {
+ // VERSION only
+ _, _ = fmt.Fprintf(out, "%s%s", version, sep)
+ }
+}
+
func writePackageVersionRequirements(prefix string, reqs []rubygems_module.VersionRequirement, out *strings.Builder) {
out.WriteString(prefix)
if len(reqs) == 0 {
@@ -367,11 +386,21 @@ func writePackageVersionRequirements(prefix string, reqs []rubygems_module.Versi
}
}
-func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion) (string, error) {
+func writePackageVersionForDependency(version, platform string, out *strings.Builder) {
+ if platform != "" && platform != "ruby" {
+ // VERSION-PLATFORM (see comment below in makePackageVersionDependency)
+ _, _ = fmt.Fprintf(out, "%s-%s ", version, platform)
+ } else {
+ // VERSION only
+ _, _ = fmt.Fprintf(out, "%s ", version)
+ }
+}
+
+func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion, c *cache.EphemeralCache) (string, error) {
// format: VERSION[-PLATFORM] [DEPENDENCY[,DEPENDENCY,...]]|REQUIREMENT[,REQUIREMENT,...]
// DEPENDENCY: GEM:CONSTRAINT[&CONSTRAINT]
// REQUIREMENT: KEY:VALUE (always contains "checksum")
- pd, err := packages_model.GetPackageDescriptor(ctx, version)
+ pd, err := packages_model.GetPackageDescriptorWithCache(ctx, version, c)
if err != nil {
return "", err
}
@@ -388,8 +417,7 @@ func makePackageVersionDependency(ctx *context.Context, version *packages_model.
}
buf := &strings.Builder{}
- buf.WriteString(version.Version)
- buf.WriteByte(' ')
+ writePackageVersionForDependency(version.Version, metadata.Platform, buf)
for i, dep := range metadata.RuntimeDependencies {
sep := util.Iif(i == 0, "", ",")
writePackageVersionRequirements(fmt.Sprintf("%s%s:", sep, dep.Name), dep.Version, buf)
@@ -404,10 +432,10 @@ func makePackageVersionDependency(ctx *context.Context, version *packages_model.
return buf.String(), nil
}
-func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion) (string, error) {
+func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion, c *cache.EphemeralCache) (string, error) {
ret := "---\n"
for _, v := range versions {
- dep, err := makePackageVersionDependency(ctx, v)
+ dep, err := makePackageVersionDependency(ctx, v, c)
if err != nil {
return "", err
}
diff --git a/routers/api/packages/rubygems/rubygems_test.go b/routers/api/packages/rubygems/rubygems_test.go
new file mode 100644
index 0000000000..a07e12a7d3
--- /dev/null
+++ b/routers/api/packages/rubygems/rubygems_test.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package rubygems
+
+import (
+ "strings"
+ "testing"
+
+ rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestWritePackageVersion(t *testing.T) {
+ buf := &strings.Builder{}
+
+ writePackageVersionForList(nil, "1.0", " ", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForList(&rubygems_module.Metadata{Platform: "ruby"}, "1.0", " ", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForList(&rubygems_module.Metadata{Platform: "linux"}, "1.0", " ", buf)
+ assert.Equal(t, "1.0_linux ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "ruby", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "os", buf)
+ assert.Equal(t, "1.0-os ", buf.String())
+ buf.Reset()
+}
diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go
index 2048c76ea0..1b58beb7b6 100644
--- a/routers/api/v1/repo/issue_dependency.go
+++ b/routers/api/v1/repo/issue_dependency.go
@@ -77,10 +77,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit == 0 {
limit = setting.API.DefaultPagingNum
@@ -328,10 +325,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit <= 1 {
limit = setting.API.DefaultPagingNum
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index f2e0cad86c..c1e0b47d33 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -203,7 +203,7 @@ func Migrate(ctx *context.APIContext) {
}
if repo != nil {
- if errDelete := repo_service.DeleteRepositoryDirectly(ctx, ctx.Doer, repo.ID); errDelete != nil {
+ if errDelete := repo_service.DeleteRepositoryDirectly(ctx, repo.ID); errDelete != nil {
log.Error("DeleteRepository: %v", errDelete)
}
}
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index b6f5a3ac9e..272b395dfb 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -247,7 +247,9 @@ func CreateRelease(ctx *context.APIContext) {
IsTag: false,
Repo: ctx.Repo.Repository,
}
- if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
+ // GitHub doesn't have "tag_message", GitLab has: https://docs.gitlab.com/api/releases/#create-a-release
+ // It doesn't need to be the same as the "release note"
+ if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, form.TagMessage); err != nil {
if repo_model.IsErrReleaseAlreadyExist(err) {
ctx.APIError(http.StatusConflict, err)
} else if release_service.IsErrProtectedTagName(err) {
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index d5840b4149..3094c1947c 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -298,10 +298,7 @@ func ListWikiPages(ctx *context.APIContext) {
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit <= 1 {
limit = setting.API.DefaultPagingNum
@@ -434,10 +431,7 @@ func ListPageRevisions(ctx *context.APIContext) {
// get commit count - wiki revisions
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
// get Commit Count
commitsHistory, err := wikiRepo.CommitsByFileAndRange(