@@ -6,6 +6,7 @@ package rubygems
66import (
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+
303429func 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