mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09: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:
		| @@ -588,6 +588,8 @@ func CommonRoutes() *web.Route { | ||||
| 			r.Get("/prerelease_specs.4.8.gz", rubygems.EnumeratePackagesPreRelease) | ||||
| 			r.Get("/quick/Marshal.4.8/{filename}", rubygems.ServePackageSpecification) | ||||
| 			r.Get("/gems/{filename}", rubygems.DownloadPackageFile) | ||||
| 			r.Get("/info/{packagename}", rubygems.GetPackageInfo) | ||||
| 			r.Get("/versions", rubygems.GetAllPackagesVersions) | ||||
| 			r.Group("/api/v1/gems", func() { | ||||
| 				r.Post("/", rubygems.UploadPackageFile) | ||||
| 				r.Delete("/yank", rubygems.DeletePackage) | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -4,7 +4,11 @@ | ||||
| package integration | ||||
|  | ||||
| import ( | ||||
| 	"archive/tar" | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"mime/multipart" | ||||
| @@ -21,101 +25,167 @@ import ( | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type tarFile struct { | ||||
| 	Name string | ||||
| 	Data []byte | ||||
| } | ||||
|  | ||||
| func makeArchiveFileTar(files []*tarFile) []byte { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 	tarWriter := tar.NewWriter(buf) | ||||
| 	for _, file := range files { | ||||
| 		_ = tarWriter.WriteHeader(&tar.Header{ | ||||
| 			Typeflag: tar.TypeReg, | ||||
| 			Name:     file.Name, | ||||
| 			Mode:     0o644, | ||||
| 			Size:     int64(len(file.Data)), | ||||
| 		}) | ||||
| 		_, _ = tarWriter.Write(file.Data) | ||||
| 	} | ||||
| 	_ = tarWriter.Close() | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| func makeArchiveFileGz(data []byte) []byte { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 	gzWriter, _ := gzip.NewWriterLevel(buf, gzip.NoCompression) | ||||
| 	_, _ = gzWriter.Write(data) | ||||
| 	_ = gzWriter.Close() | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| func makeRubyGem(name, version string) []byte { | ||||
| 	metadataContent := fmt.Sprintf(`--- !ruby/object:Gem::Specification | ||||
| name: %s | ||||
| version: !ruby/object:Gem::Version | ||||
|   version: %s | ||||
| platform: ruby | ||||
| authors: | ||||
| - Gitea | ||||
| autorequire: | ||||
| bindir: bin | ||||
| cert_chain: [] | ||||
| date: 2021-08-23 00:00:00.000000000 Z | ||||
| dependencies: | ||||
| - !ruby/object:Gem::Dependency | ||||
|   name: runtime-dep | ||||
|   requirement: !ruby/object:Gem::Requirement | ||||
|     requirements: | ||||
|     - - ">=" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: 1.2.0 | ||||
|     - - "<" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: '2.0' | ||||
|   type: :runtime | ||||
|   prerelease: false | ||||
|   version_requirements: !ruby/object:Gem::Requirement | ||||
|     requirements: | ||||
|     - - ">=" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: 1.2.0 | ||||
|     - - "<" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: '2.0' | ||||
| - !ruby/object:Gem::Dependency | ||||
|   name: dev-dep | ||||
|   requirement: !ruby/object:Gem::Requirement | ||||
|     requirements: | ||||
|     - - "~>" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: '5.2' | ||||
|   type: :development | ||||
|   prerelease: false | ||||
|   version_requirements: !ruby/object:Gem::Requirement | ||||
|     requirements: | ||||
|     - - "~>" | ||||
|       - !ruby/object:Gem::Version | ||||
|         version: '5.2' | ||||
| description: RubyGems package test | ||||
| email: rubygems@gitea.io | ||||
| executables: [] | ||||
| extensions: [] | ||||
| extra_rdoc_files: [] | ||||
| files: | ||||
| - lib/gitea.rb | ||||
| homepage: https://gitea.io/ | ||||
| licenses: | ||||
| - MIT | ||||
| metadata: {} | ||||
| post_install_message: | ||||
| rdoc_options: [] | ||||
| require_paths: | ||||
| - lib | ||||
| required_ruby_version: !ruby/object:Gem::Requirement | ||||
|   requirements: | ||||
|   - - ">=" | ||||
|     - !ruby/object:Gem::Version | ||||
|       version: 2.3.0 | ||||
| required_rubygems_version: !ruby/object:Gem::Requirement | ||||
|   requirements: | ||||
|   - - ">=" | ||||
|     - !ruby/object:Gem::Version | ||||
|       version: '1.0' | ||||
| requirements: [] | ||||
| rubyforge_project: | ||||
| rubygems_version: 2.7.6.2 | ||||
| signing_key: | ||||
| specification_version: 4 | ||||
| summary: Gitea package | ||||
| test_files: [] | ||||
| `, name, version) | ||||
|  | ||||
| 	metadataGz := makeArchiveFileGz([]byte(metadataContent)) | ||||
| 	dataTarGz := makeArchiveFileGz(makeArchiveFileTar([]*tarFile{ | ||||
| 		{ | ||||
| 			Name: "lib/gitea.rb", | ||||
| 			Data: []byte("class Gitea\nend"), | ||||
| 		}, | ||||
| 	})) | ||||
|  | ||||
| 	checksumsYaml := fmt.Sprintf(`--- | ||||
| SHA256: | ||||
|   metadata.gz: %x | ||||
|   data.tar.gz: %x | ||||
| SHA512: | ||||
|   metadata.gz: %x | ||||
|   data.tar.gz: %x | ||||
| `, sha256.Sum256(metadataGz), sha256.Sum256(dataTarGz), sha512.Sum512(metadataGz), sha512.Sum512(dataTarGz)) | ||||
|  | ||||
| 	files := []*tarFile{ | ||||
| 		{ | ||||
| 			Name: "data.tar.gz", | ||||
| 			Data: dataTarGz, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "metadata.gz", | ||||
| 			Data: metadataGz, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "checksums.yaml.gz", | ||||
| 			Data: makeArchiveFileGz([]byte(checksumsYaml)), | ||||
| 		}, | ||||
| 	} | ||||
| 	return makeArchiveFileTar(files) | ||||
| } | ||||
|  | ||||
| func TestPackageRubyGems(t *testing.T) { | ||||
| 	defer tests.PrepareTestEnv(t)() | ||||
|  | ||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||
|  | ||||
| 	packageName := "gitea" | ||||
| 	packageVersion := "1.0.5" | ||||
| 	packageFilename := "gitea-1.0.5.gem" | ||||
| 	testGemName := "gitea" | ||||
| 	testGemVersion := "1.0.5" | ||||
| 	testGemContent := makeRubyGem(testGemName, testGemVersion) | ||||
| 	testGemContentChecksum := fmt.Sprintf("%x", sha256.Sum256(testGemContent)) | ||||
|  | ||||
| 	gemContent, _ := base64.StdEncoding.DecodeString(`bWV0YWRhdGEuZ3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA0NDQAMDAwMDAw | ||||
| MAAwMDAwMDAwADAwMDAwMDAxMDQxADE0MTEwNzcyMzY2ADAxMzQ0MQAgMAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMHdoZWVsAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAd2hlZWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAw | ||||
| MDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf | ||||
| iwgA9vQjYQID1VVNb9QwEL37V5he9pRsmlJAFlQckCoOXAriQIUix5nNmsYf2JOqKwS/nYmz2d3Q | ||||
| qqCCKpFdadfjmfdm5nmcLMv4k9DXm6Wrv4BCcQ5GiPcelF5pJVE7y6w0IHirESS7hhDJJu4I+jhu | ||||
| Mc53Tsd5kZ8y30lcuWAEH2KY7HHtQhQs4+cJkwwuwNdeB6JhtbaNDoLTL1MQsFJrqQnr8jNrJJJH | ||||
| WZTHWfEiK094UYj0zYvp4Z9YAx5sA1ZpSCS3M30zeWwo2bG60FvUBjIKJts2GwMW76r0Yr9NzjN3 | ||||
| YhwsGX2Ozl4dpcWwvK9d43PQtDIv9igvHwSyIIwFmXHjqTqxLY8MPkCADmQk80p2EfZ6VbM6/ue6 | ||||
| /1D0Bq7/qeA/zh6W82leHmhFWUHn/JbsEfT6q7QbiCpoj8l0QcEUFLmX6kq2wBEiMjBSd+Pwt7T5 | ||||
| Ot0kuXYMbkD1KOuOBnWYb7hBsAP4bhlkFRqnqpWefMZ/pHCn6+WIFGq2dgY8EQq+RvRRLJcTyZJ1 | ||||
| WhHqGPTu7QdmACXdJFLwb9+ZdxErbSPKrqsMxJhAWCJ1qaqRdtu6yktcT/STsamG0qp7rsa5EL/K | ||||
| MBua30uw4ynzExqYWRJDfx8/kQWN3PwsDh2jYLr1W+pZcAmCs9splvnz/Flesqhbq21bXcGG/OLh | ||||
| +2fv/JTF3hgZyCW9OaZjxoZjdnBGfgKpxZyJ1QYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGF0 | ||||
| YS50YXIuZ3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA0NDQAMDAwMDAwMAAw | ||||
| MDAwMDAwADAwMDAwMDAwMjQyADE0MTEwNzcyMzY2ADAxMzM2MQAgMAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMHdoZWVsAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAd2hlZWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAwMDAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfiwgA | ||||
| 9vQjYQID7M/NCsMgDABgz32KrA/QxersK/Q17ExXIcyhlr7+HLv1sJ02KPhBCPk5JOyn881nsl2c | ||||
| xI+gRDRaC3zbZ8RBCamlxGHolTFlX11kLwDFH6wp21hO2RYi/rD3bb5/7iCubFOCMbBtABzNkIjn | ||||
| bvGlAnisOUE7EnOALUR2p7b06e6aV4iqqqrquJ4AAAD//wMA+sA/NQAIAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNoZWNr | ||||
| c3Vtcy55YW1sLmd6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwNDQ0ADAwMDAwMDAAMDAw | ||||
| MDAwMAAwMDAwMDAwMDQ1MAAxNDExMDc3MjM2NgAwMTQ2MTIAIDAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdXN0YXIAMDB3aGVlbAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAHdoZWVsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMDAwMAAwMDAwMDAwAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4sIAPb0 | ||||
| I2ECA2WQOa4UQAxE8znFXGCQ21vbPyMj5wRuL0Qk6EecnmZCyKyy9FSvXq/X4/u3ryj68Xg+f/Zn | ||||
| VHzGlx+/P57qvU4XxWalBKftSXOgCjNYkdRycrC5Axem+W4HqS12PNEv7836jF9vnlHxwSyxKY+y | ||||
| go0cPblyHzkrZ4HF1GSVhe7mOOoasXNk2fnbUxb+19Pp9tobD/QlJKMX7y204PREh6nQ5hG9Alw6 | ||||
| x4TnmtA+aekGfm6wAseog2LSgpR4Q7cYnAH3K4qAQa6A6JCC1gpuY7P+9YxE5SZ+j0eVGbaBTwBQ | ||||
| iIqRUyyzLCoFCBdYNWxniapTavD97blXTzFvgoVoAsKBAtlU48cdaOmeZDpwV01OtcGwjscfeUrY | ||||
| B9QBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`) | ||||
| 	testAnotherGemName := "gitea-another" | ||||
| 	testAnotherGemVersion := "0.99" | ||||
|  | ||||
| 	root := fmt.Sprintf("/api/packages/%s/rubygems", user.Name) | ||||
|  | ||||
| 	uploadFile := func(t *testing.T, expectedStatus int) { | ||||
| 		req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/api/v1/gems", root), bytes.NewReader(gemContent)). | ||||
| 	uploadFile := func(t *testing.T, content []byte, expectedStatus int) { | ||||
| 		req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/api/v1/gems", root), bytes.NewReader(content)). | ||||
| 			AddBasicAuth(user.Name) | ||||
| 		MakeRequest(t, req, expectedStatus) | ||||
| 	} | ||||
| @@ -123,7 +193,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`) | ||||
| 	t.Run("Upload", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
|  | ||||
| 		uploadFile(t, http.StatusCreated) | ||||
| 		uploadFile(t, testGemContent, http.StatusCreated) | ||||
|  | ||||
| 		pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems) | ||||
| 		assert.NoError(t, err) | ||||
| @@ -133,34 +203,33 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, pd.SemVer) | ||||
| 		assert.IsType(t, &rubygems.Metadata{}, pd.Metadata) | ||||
| 		assert.Equal(t, packageName, pd.Package.Name) | ||||
| 		assert.Equal(t, packageVersion, pd.Version.Version) | ||||
| 		assert.Equal(t, testGemName, pd.Package.Name) | ||||
| 		assert.Equal(t, testGemVersion, pd.Version.Version) | ||||
|  | ||||
| 		pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Len(t, pfs, 1) | ||||
| 		assert.Equal(t, packageFilename, pfs[0].Name) | ||||
| 		assert.Equal(t, fmt.Sprintf("%s-%s.gem", testGemName, testGemVersion), pfs[0].Name) | ||||
| 		assert.True(t, pfs[0].IsLead) | ||||
|  | ||||
| 		pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, int64(4608), pb.Size) | ||||
| 		assert.EqualValues(t, len(testGemContent), pb.Size) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("UploadExists", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
|  | ||||
| 		uploadFile(t, http.StatusConflict) | ||||
| 		uploadFile(t, testGemContent, http.StatusConflict) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Download", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
|  | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/gems/%s", root, packageFilename)). | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/gems/%s-%s.gem", root, testGemName, testGemVersion)). | ||||
| 			AddBasicAuth(user.Name) | ||||
| 		resp := MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
| 		assert.Equal(t, gemContent, resp.Body.Bytes()) | ||||
| 		assert.Equal(t, testGemContent, resp.Body.Bytes()) | ||||
|  | ||||
| 		pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems) | ||||
| 		assert.NoError(t, err) | ||||
| @@ -171,7 +240,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`) | ||||
| 	t.Run("DownloadGemspec", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
|  | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/quick/Marshal.4.8/%sspec.rz", root, packageFilename)). | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/quick/Marshal.4.8/%s-%s.gemspec.rz", root, testGemName, testGemVersion)). | ||||
| 			AddBasicAuth(user.Name) | ||||
| 		resp := MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
| @@ -206,22 +275,63 @@ gAAAAP//MS06Gw==`) | ||||
| 		enumeratePackages(t, "prerelease_specs.4.8.gz", b) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Delete", func(t *testing.T) { | ||||
| 	t.Run("UploadAnother", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
| 		uploadFile(t, makeRubyGem(testAnotherGemName, testAnotherGemVersion), http.StatusCreated) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("PackageInfo", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
|  | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name) | ||||
| 		resp := MakeRequest(t, req, http.StatusOK) | ||||
| 		expected := fmt.Sprintf(`--- | ||||
| 1.0.5 runtime-dep:>= 1.2.0&< 2.0|checksum:%s,ruby:>= 2.3.0,rubygems:>= 1.0 | ||||
| `, testGemContentChecksum) | ||||
| 		assert.Equal(t, expected, resp.Body.String()) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Versions", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name) | ||||
| 		resp := MakeRequest(t, req, http.StatusOK) | ||||
| 		assert.Equal(t, `--- | ||||
| gitea 1.0.5 08843c2dd0ea19910e6b056b98e38f1c | ||||
| gitea-another 0.99 8b639e4048d282941485368ec42609be | ||||
| `, resp.Body.String()) | ||||
| 	}) | ||||
|  | ||||
| 	deleteGemPackage := func(t *testing.T, packageName, packageVersion string) { | ||||
| 		body := bytes.Buffer{} | ||||
| 		writer := multipart.NewWriter(&body) | ||||
| 		writer.WriteField("gem_name", packageName) | ||||
| 		writer.WriteField("version", packageVersion) | ||||
| 		writer.Close() | ||||
|  | ||||
| 		_ = writer.WriteField("gem_name", packageName) | ||||
| 		_ = writer.WriteField("version", packageVersion) | ||||
| 		_ = writer.Close() | ||||
| 		req := NewRequestWithBody(t, "DELETE", fmt.Sprintf("%s/api/v1/gems/yank", root), &body). | ||||
| 			SetHeader("Content-Type", writer.FormDataContentType()). | ||||
| 			AddBasicAuth(user.Name) | ||||
| 		MakeRequest(t, req, http.StatusOK) | ||||
| 	} | ||||
|  | ||||
| 	t.Run("DeleteAll", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
| 		deleteGemPackage(t, testGemName, testGemVersion) | ||||
| 		deleteGemPackage(t, testAnotherGemName, testAnotherGemVersion) | ||||
| 		pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Empty(t, pvs) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("PackageInfoAfterDelete", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name) | ||||
| 		MakeRequest(t, req, http.StatusNotFound) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("VersionsAfterDelete", func(t *testing.T) { | ||||
| 		defer tests.PrintCurrentTest(t)() | ||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name) | ||||
| 		resp := MakeRequest(t, req, http.StatusOK) | ||||
| 		assert.Equal(t, "---\n", resp.Body.String()) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user