mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Improve rubygems package registry (#31357)
To make it work with Bundler: https://guides.rubygems.org/rubygems-org-compact-index-api/ It only adds 2 new API endpoints and improves some tests, existing logic is not changed.
This commit is contained in:
		@@ -6,6 +6,7 @@ package rubygems
 | 
			
		||||
import (
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"compress/zlib"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -227,12 +228,7 @@ func UploadPackageFile(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var filename string
 | 
			
		||||
	if rp.Metadata.Platform == "" || rp.Metadata.Platform == "ruby" {
 | 
			
		||||
		filename = strings.ToLower(fmt.Sprintf("%s-%s.gem", rp.Name, rp.Version))
 | 
			
		||||
	} else {
 | 
			
		||||
		filename = strings.ToLower(fmt.Sprintf("%s-%s-%s.gem", rp.Name, rp.Version, rp.Metadata.Platform))
 | 
			
		||||
	}
 | 
			
		||||
	filename := makeGemFullFileName(rp.Name, rp.Version, rp.Metadata.Platform)
 | 
			
		||||
 | 
			
		||||
	_, _, err = packages_service.CreatePackageAndAddFile(
 | 
			
		||||
		ctx,
 | 
			
		||||
@@ -300,6 +296,136 @@ func DeletePackage(ctx *context.Context) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPackageInfo returns a custom text based format for the single rubygem with a line for each version of the rubygem
 | 
			
		||||
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
 | 
			
		||||
func GetPackageInfo(ctx *context.Context) {
 | 
			
		||||
	packageName := ctx.Params("packagename")
 | 
			
		||||
	versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, packageName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(versions) == 0 {
 | 
			
		||||
		apiError(ctx, http.StatusNotFound, nil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	infoContent, err := makePackageInfo(ctx, versions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.PlainText(http.StatusOK, infoContent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := &strings.Builder{}
 | 
			
		||||
	out.WriteString("---\n")
 | 
			
		||||
	for _, pkg := range packages {
 | 
			
		||||
		versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, pkg.Name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if len(versions) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		info, err := makePackageInfo(ctx, versions)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// format: RUBYGEM [-]VERSION_PLATFORM[,VERSION_PLATFORM],...] MD5
 | 
			
		||||
		_, _ = 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)
 | 
			
		||||
		}
 | 
			
		||||
		_, _ = fmt.Fprintf(out, " %x\n", md5.Sum([]byte(info)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.PlainText(http.StatusOK, out.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writePackageVersionRequirements(prefix string, reqs []rubygems_module.VersionRequirement, out *strings.Builder) {
 | 
			
		||||
	out.WriteString(prefix)
 | 
			
		||||
	if len(reqs) == 0 {
 | 
			
		||||
		reqs = []rubygems_module.VersionRequirement{{Restriction: ">=", Version: "0"}}
 | 
			
		||||
	}
 | 
			
		||||
	for i, req := range reqs {
 | 
			
		||||
		sep := util.Iif(i == 0, "", "&")
 | 
			
		||||
		_, _ = fmt.Fprintf(out, "%s%s %s", sep, req.Restriction, req.Version)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion) (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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	metadata := pd.Metadata.(*rubygems_module.Metadata)
 | 
			
		||||
	fullFilename := makeGemFullFileName(pd.Package.Name, version.Version, metadata.Platform)
 | 
			
		||||
	file, err := packages_model.GetFileForVersionByName(ctx, version.ID, fullFilename, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	blob, err := packages_model.GetBlobByID(ctx, file.BlobID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := &strings.Builder{}
 | 
			
		||||
	buf.WriteString(version.Version)
 | 
			
		||||
	buf.WriteByte(' ')
 | 
			
		||||
	for i, dep := range metadata.RuntimeDependencies {
 | 
			
		||||
		sep := util.Iif(i == 0, "", ",")
 | 
			
		||||
		writePackageVersionRequirements(fmt.Sprintf("%s%s:", sep, dep.Name), dep.Version, buf)
 | 
			
		||||
	}
 | 
			
		||||
	_, _ = fmt.Fprintf(buf, "|checksum:%s", blob.HashSHA256)
 | 
			
		||||
	if len(metadata.RequiredRubyVersion) != 0 {
 | 
			
		||||
		writePackageVersionRequirements(",ruby:", metadata.RequiredRubyVersion, buf)
 | 
			
		||||
	}
 | 
			
		||||
	if len(metadata.RequiredRubygemsVersion) != 0 {
 | 
			
		||||
		writePackageVersionRequirements(",rubygems:", metadata.RequiredRubygemsVersion, buf)
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion) (string, error) {
 | 
			
		||||
	ret := "---\n"
 | 
			
		||||
	for _, v := range versions {
 | 
			
		||||
		dep, err := makePackageVersionDependency(ctx, v)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		ret += dep + "\n"
 | 
			
		||||
	}
 | 
			
		||||
	return ret, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeGemFullFileName(gemName, version, platform string) string {
 | 
			
		||||
	var basename string
 | 
			
		||||
	if platform == "" || platform == "ruby" {
 | 
			
		||||
		basename = fmt.Sprintf("%s-%s", gemName, version)
 | 
			
		||||
	} else {
 | 
			
		||||
		basename = fmt.Sprintf("%s-%s-%s", gemName, version, platform)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.ToLower(basename) + ".gem"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_model.PackageVersion, error) {
 | 
			
		||||
	pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
 | 
			
		||||
		OwnerID:         ctx.Package.Owner.ID,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user