Skip to content

Commit 70e3efc

Browse files
committed
fix
1 parent e61e9a3 commit 70e3efc

File tree

3 files changed

+346
-108
lines changed

3 files changed

+346
-108
lines changed

routers/api/packages/api.go

+2
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,8 @@ func CommonRoutes() *web.Route {
588588
r.Get("/prerelease_specs.4.8.gz", rubygems.EnumeratePackagesPreRelease)
589589
r.Get("/quick/Marshal.4.8/{filename}", rubygems.ServePackageSpecification)
590590
r.Get("/gems/{filename}", rubygems.DownloadPackageFile)
591+
r.Get("/info/{packagename}", rubygems.GetPackageInfo)
592+
r.Get("/versions", rubygems.GetAllPackagesVersions)
591593
r.Group("/api/v1/gems", func() {
592594
r.Post("/", rubygems.UploadPackageFile)
593595
r.Delete("/yank", rubygems.DeletePackage)

routers/api/packages/rubygems/rubygems.go

+132-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package rubygems
66
import (
77
"compress/gzip"
88
"compress/zlib"
9+
"crypto/md5"
910
"errors"
1011
"fmt"
1112
"io"
@@ -227,12 +228,7 @@ func UploadPackageFile(ctx *context.Context) {
227228
return
228229
}
229230

230-
var filename string
231-
if rp.Metadata.Platform == "" || rp.Metadata.Platform == "ruby" {
232-
filename = strings.ToLower(fmt.Sprintf("%s-%s.gem", rp.Name, rp.Version))
233-
} else {
234-
filename = strings.ToLower(fmt.Sprintf("%s-%s-%s.gem", rp.Name, rp.Version, rp.Metadata.Platform))
235-
}
231+
filename := makeGemFullFileName(rp.Name, rp.Version, rp.Metadata.Platform)
236232

237233
_, _, err = packages_service.CreatePackageAndAddFile(
238234
ctx,
@@ -300,6 +296,136 @@ func DeletePackage(ctx *context.Context) {
300296
}
301297
}
302298

299+
// GetPackageInfo returns a custom text based format for the single rubygem with a line for each version of the rubygem
300+
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
301+
func GetPackageInfo(ctx *context.Context) {
302+
packageName := ctx.Params("packagename")
303+
versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, packageName)
304+
if err != nil {
305+
apiError(ctx, http.StatusInternalServerError, err)
306+
return
307+
}
308+
if len(versions) == 0 {
309+
apiError(ctx, http.StatusNotFound, nil)
310+
return
311+
}
312+
infoContent, err := makePackageInfo(ctx, versions)
313+
if err != nil {
314+
apiError(ctx, http.StatusInternalServerError, err)
315+
return
316+
}
317+
ctx.PlainText(http.StatusOK, infoContent)
318+
}
319+
320+
// GetAllPackagesVersions returns a custom text based format containing information about all versions of all rubygems.
321+
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
322+
func GetAllPackagesVersions(ctx *context.Context) {
323+
packages, err := packages_model.GetPackagesByType(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems)
324+
if err != nil {
325+
apiError(ctx, http.StatusInternalServerError, err)
326+
return
327+
}
328+
329+
out := &strings.Builder{}
330+
out.WriteString("---\n")
331+
for _, pkg := range packages {
332+
versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, pkg.Name)
333+
if err != nil {
334+
apiError(ctx, http.StatusInternalServerError, err)
335+
return
336+
}
337+
if len(versions) == 0 {
338+
continue
339+
}
340+
341+
info, err := makePackageInfo(ctx, versions)
342+
if err != nil {
343+
apiError(ctx, http.StatusInternalServerError, err)
344+
return
345+
}
346+
347+
// format: RUBYGEM [-]VERSION_PLATFORM[,VERSION_PLATFORM],...] MD5
348+
_, _ = fmt.Fprintf(out, "%s ", pkg.Name)
349+
for i, v := range versions {
350+
sep := util.Iif(i == len(versions)-1, "", ",")
351+
_, _ = fmt.Fprintf(out, "%s%s", v.Version, sep)
352+
}
353+
_, _ = fmt.Fprintf(out, " %x\n", md5.Sum([]byte(info)))
354+
}
355+
356+
ctx.PlainText(http.StatusOK, out.String())
357+
}
358+
359+
func writePackageVersionRequirements(prefix string, reqs []rubygems_module.VersionRequirement, out *strings.Builder) {
360+
out.WriteString(prefix)
361+
if len(reqs) == 0 {
362+
reqs = []rubygems_module.VersionRequirement{{Restriction: ">=", Version: "0"}}
363+
}
364+
for i, req := range reqs {
365+
sep := util.Iif(i == 0, "", "&")
366+
_, _ = fmt.Fprintf(out, "%s%s %s", sep, req.Restriction, req.Version)
367+
}
368+
}
369+
370+
func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion) (string, error) {
371+
// format: VERSION[-PLATFORM] [DEPENDENCY[,DEPENDENCY,...]]|REQUIREMENT[,REQUIREMENT,...]
372+
// DEPENDENCY: GEM:CONSTRAINT[&CONSTRAINT]
373+
// REQUIREMENT: KEY:VALUE (always contains "checksum")
374+
pd, err := packages_model.GetPackageDescriptor(ctx, version)
375+
if err != nil {
376+
return "", err
377+
}
378+
379+
metadata := pd.Metadata.(*rubygems_module.Metadata)
380+
fullFilename := makeGemFullFileName(pd.Package.Name, version.Version, metadata.Platform)
381+
file, err := packages_model.GetFileForVersionByName(ctx, version.ID, fullFilename, "")
382+
if err != nil {
383+
return "", err
384+
}
385+
blob, err := packages_model.GetBlobByID(ctx, file.BlobID)
386+
if err != nil {
387+
return "", err
388+
}
389+
390+
buf := &strings.Builder{}
391+
buf.WriteString(version.Version)
392+
buf.WriteByte(' ')
393+
for i, dep := range metadata.RuntimeDependencies {
394+
sep := util.Iif(i == 0, "", ",")
395+
writePackageVersionRequirements(fmt.Sprintf("%s%s:", sep, dep.Name), dep.Version, buf)
396+
}
397+
_, _ = fmt.Fprintf(buf, "|checksum:%s", blob.HashSHA256)
398+
if len(metadata.RequiredRubyVersion) != 0 {
399+
writePackageVersionRequirements(",ruby:", metadata.RequiredRubyVersion, buf)
400+
}
401+
if len(metadata.RequiredRubygemsVersion) != 0 {
402+
writePackageVersionRequirements(",rubygems:", metadata.RequiredRubygemsVersion, buf)
403+
}
404+
return buf.String(), nil
405+
}
406+
407+
func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion) (string, error) {
408+
ret := "---\n"
409+
for _, v := range versions {
410+
dep, err := makePackageVersionDependency(ctx, v)
411+
if err != nil {
412+
return "", err
413+
}
414+
ret += dep + "\n"
415+
}
416+
return ret, nil
417+
}
418+
419+
func makeGemFullFileName(gemName, version, platform string) string {
420+
var basename string
421+
if platform == "" || platform == "ruby" {
422+
basename = fmt.Sprintf("%s-%s", gemName, version)
423+
} else {
424+
basename = fmt.Sprintf("%s-%s-%s", gemName, version, platform)
425+
}
426+
return strings.ToLower(basename) + ".gem"
427+
}
428+
303429
func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_model.PackageVersion, error) {
304430
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
305431
OwnerID: ctx.Package.Owner.ID,

0 commit comments

Comments
 (0)