mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	List all Debian package versions in Packages (#27786)
				
					
				
			Closes #27783 This PR lists all and not only the latest package versions in the `Packages` index.
This commit is contained in:
		| @@ -207,6 +207,9 @@ func (opts *ImageTagsSearchOptions) configureOrderBy(e db.Engine) { | |||||||
| 	default: | 	default: | ||||||
| 		e.Desc("package_version.created_unix") | 		e.Desc("package_version.created_unix") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Sort by id for stable order with duplicates in the other field | ||||||
|  | 	e.Asc("package_version.id") | ||||||
| } | } | ||||||
|  |  | ||||||
| // SearchImageTags gets a sorted list of the tags of an image | // SearchImageTags gets a sorted list of the tags of an image | ||||||
|   | |||||||
| @@ -21,8 +21,7 @@ type PackageSearchOptions struct { | |||||||
| 	Architecture string | 	Architecture string | ||||||
| } | } | ||||||
|  |  | ||||||
| // SearchLatestPackages gets the latest packages matching the search options | func (opts *PackageSearchOptions) toCond() builder.Cond { | ||||||
| func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*packages.PackageFileDescriptor, error) { |  | ||||||
| 	var cond builder.Cond = builder.Eq{ | 	var cond builder.Cond = builder.Eq{ | ||||||
| 		"package_file.is_lead":        true, | 		"package_file.is_lead":        true, | ||||||
| 		"package.type":                packages.TypeDebian, | 		"package.type":                packages.TypeDebian, | ||||||
| @@ -62,28 +61,40 @@ func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*p | |||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cond = cond. | 	return cond | ||||||
| 		And(builder.Expr("pv2.id IS NULL")) | } | ||||||
|  |  | ||||||
| 	joinCond := builder. | // ExistPackages tests if there are packages matching the search options | ||||||
| 		Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))"). | func ExistPackages(ctx context.Context, opts *PackageSearchOptions) (bool, error) { | ||||||
| 		And(builder.Eq{"pv2.is_internal": false}) | 	return db.GetEngine(ctx). | ||||||
|  | 		Table("package_file"). | ||||||
|  | 		Join("INNER", "package_version", "package_version.id = package_file.version_id"). | ||||||
|  | 		Join("INNER", "package", "package.id = package_version.package_id"). | ||||||
|  | 		Where(opts.toCond()). | ||||||
|  | 		Exist(new(packages.PackageFile)) | ||||||
|  | } | ||||||
|  |  | ||||||
| 	pfs := make([]*packages.PackageFile, 0, 10) | // SearchPackages gets the packages matching the search options | ||||||
| 	err := db.GetEngine(ctx). | func SearchPackages(ctx context.Context, opts *PackageSearchOptions, iter func(*packages.PackageFileDescriptor)) error { | ||||||
|  | 	return db.GetEngine(ctx). | ||||||
| 		Table("package_file"). | 		Table("package_file"). | ||||||
| 		Select("package_file.*"). | 		Select("package_file.*"). | ||||||
| 		Join("INNER", "package_version", "package_version.id = package_file.version_id"). | 		Join("INNER", "package_version", "package_version.id = package_file.version_id"). | ||||||
| 		Join("LEFT", "package_version pv2", joinCond). |  | ||||||
| 		Join("INNER", "package", "package.id = package_version.package_id"). | 		Join("INNER", "package", "package.id = package_version.package_id"). | ||||||
| 		Where(cond). | 		Where(opts.toCond()). | ||||||
| 		Desc("package_version.created_unix"). | 		Asc("package.lower_name", "package_version.created_unix"). | ||||||
| 		Find(&pfs) | 		Iterate(new(packages.PackageFile), func(_ int, bean any) error { | ||||||
|  | 			pf := bean.(*packages.PackageFile) | ||||||
|  |  | ||||||
|  | 			pfd, err := packages.GetPackageFileDescriptor(ctx, pf) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 		return nil, err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 	return packages.GetPackageFileDescriptors(ctx, pfs) | 			iter(pfd) | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetDistributions gets all available distributions | // GetDistributions gets all available distributions | ||||||
|   | |||||||
| @@ -278,6 +278,9 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) { | |||||||
| 	default: | 	default: | ||||||
| 		e.Desc("package_version.created_unix") | 		e.Desc("package_version.created_unix") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Sort by id for stable order with duplicates in the other field | ||||||
|  | 	e.Asc("package_version.id") | ||||||
| } | } | ||||||
|  |  | ||||||
| // SearchVersions gets all versions of packages matching the search options | // SearchVersions gets all versions of packages matching the search options | ||||||
|   | |||||||
| @@ -165,18 +165,17 @@ func buildRepositoryFiles(ctx context.Context, ownerID int64, repoVersion *packa | |||||||
|  |  | ||||||
| // https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices | // https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices | ||||||
| func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packages_model.PackageVersion, distribution, component, architecture string) error { | func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packages_model.PackageVersion, distribution, component, architecture string) error { | ||||||
| 	pfds, err := debian_model.SearchLatestPackages(ctx, &debian_model.PackageSearchOptions{ | 	opts := &debian_model.PackageSearchOptions{ | ||||||
| 		OwnerID:      ownerID, | 		OwnerID:      ownerID, | ||||||
| 		Distribution: distribution, | 		Distribution: distribution, | ||||||
| 		Component:    component, | 		Component:    component, | ||||||
| 		Architecture: architecture, | 		Architecture: architecture, | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Delete the package indices if there are no packages | 	// Delete the package indices if there are no packages | ||||||
| 	if len(pfds) == 0 { | 	if has, err := debian_model.ExistPackages(ctx, opts); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if !has { | ||||||
| 		key := fmt.Sprintf("%s|%s|%s", distribution, component, architecture) | 		key := fmt.Sprintf("%s|%s|%s", distribution, component, architecture) | ||||||
| 		for _, filename := range []string{"Packages", "Packages.gz", "Packages.xz"} { | 		for _, filename := range []string{"Packages", "Packages.gz", "Packages.xz"} { | ||||||
| 			pf, err := packages_model.GetFileForVersionByName(ctx, repoVersion.ID, filename, key) | 			pf, err := packages_model.GetFileForVersionByName(ctx, repoVersion.ID, filename, key) | ||||||
| @@ -211,7 +210,7 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa | |||||||
| 	w := io.MultiWriter(packagesContent, gzw, xzw) | 	w := io.MultiWriter(packagesContent, gzw, xzw) | ||||||
|  |  | ||||||
| 	addSeparator := false | 	addSeparator := false | ||||||
| 	for _, pfd := range pfds { | 	if err := debian_model.SearchPackages(ctx, opts, func(pfd *packages_model.PackageFileDescriptor) { | ||||||
| 		if addSeparator { | 		if addSeparator { | ||||||
| 			fmt.Fprintln(w) | 			fmt.Fprintln(w) | ||||||
| 		} | 		} | ||||||
| @@ -225,6 +224,8 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa | |||||||
| 		fmt.Fprintf(w, "SHA1: %s\n", pfd.Blob.HashSHA1) | 		fmt.Fprintf(w, "SHA1: %s\n", pfd.Blob.HashSHA1) | ||||||
| 		fmt.Fprintf(w, "SHA256: %s\n", pfd.Blob.HashSHA256) | 		fmt.Fprintf(w, "SHA256: %s\n", pfd.Blob.HashSHA256) | ||||||
| 		fmt.Fprintf(w, "SHA512: %s\n", pfd.Blob.HashSHA512) | 		fmt.Fprintf(w, "SHA512: %s\n", pfd.Blob.HashSHA512) | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	gzw.Close() | 	gzw.Close() | ||||||
| @@ -238,7 +239,7 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa | |||||||
| 		{"Packages.gz", packagesGzipContent}, | 		{"Packages.gz", packagesGzipContent}, | ||||||
| 		{"Packages.xz", packagesXzContent}, | 		{"Packages.xz", packagesXzContent}, | ||||||
| 	} { | 	} { | ||||||
| 		_, err = packages_service.AddFileToPackageVersionInternal( | 		_, err := packages_service.AddFileToPackageVersionInternal( | ||||||
| 			ctx, | 			ctx, | ||||||
| 			repoVersion, | 			repoVersion, | ||||||
| 			&packages_service.PackageFileCreationInfo{ | 			&packages_service.PackageFileCreationInfo{ | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ func TestPackageDebian(t *testing.T) { | |||||||
|  |  | ||||||
| 	packageName := "gitea" | 	packageName := "gitea" | ||||||
| 	packageVersion := "1.0.3" | 	packageVersion := "1.0.3" | ||||||
|  | 	packageVersion2 := "1.0.4" | ||||||
| 	packageDescription := "Package Description" | 	packageDescription := "Package Description" | ||||||
|  |  | ||||||
| 	createArchive := func(name, version, architecture string) io.Reader { | 	createArchive := func(name, version, architecture string) io.Reader { | ||||||
| @@ -80,11 +81,11 @@ func TestPackageDebian(t *testing.T) { | |||||||
| 			for _, component := range components { | 			for _, component := range components { | ||||||
| 				for _, architecture := range architectures { | 				for _, architecture := range architectures { | ||||||
| 					t.Run(fmt.Sprintf("[Component:%s,Architecture:%s]", component, architecture), func(t *testing.T) { | 					t.Run(fmt.Sprintf("[Component:%s,Architecture:%s]", component, architecture), func(t *testing.T) { | ||||||
|  | 						uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component) | ||||||
|  |  | ||||||
| 						t.Run("Upload", func(t *testing.T) { | 						t.Run("Upload", func(t *testing.T) { | ||||||
| 							defer tests.PrintCurrentTest(t)() | 							defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 							uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component) |  | ||||||
|  |  | ||||||
| 							req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})) | 							req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})) | ||||||
| 							MakeRequest(t, req, http.StatusUnauthorized) | 							MakeRequest(t, req, http.StatusUnauthorized) | ||||||
|  |  | ||||||
| @@ -100,18 +101,17 @@ func TestPackageDebian(t *testing.T) { | |||||||
| 							AddBasicAuthHeader(req, user.Name) | 							AddBasicAuthHeader(req, user.Name) | ||||||
| 							MakeRequest(t, req, http.StatusCreated) | 							MakeRequest(t, req, http.StatusCreated) | ||||||
|  |  | ||||||
| 							pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeDebian) | 							pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeDebian, packageName, packageVersion) | ||||||
| 							assert.NoError(t, err) | 							assert.NoError(t, err) | ||||||
| 							assert.Len(t, pvs, 1) |  | ||||||
|  |  | ||||||
| 							pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) | 							pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv) | ||||||
| 							assert.NoError(t, err) | 							assert.NoError(t, err) | ||||||
| 							assert.Nil(t, pd.SemVer) | 							assert.Nil(t, pd.SemVer) | ||||||
| 							assert.IsType(t, &debian_module.Metadata{}, pd.Metadata) | 							assert.IsType(t, &debian_module.Metadata{}, pd.Metadata) | ||||||
| 							assert.Equal(t, packageName, pd.Package.Name) | 							assert.Equal(t, packageName, pd.Package.Name) | ||||||
| 							assert.Equal(t, packageVersion, pd.Version.Version) | 							assert.Equal(t, packageVersion, pd.Version.Version) | ||||||
|  |  | ||||||
| 							pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) | 							pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pv.ID) | ||||||
| 							assert.NoError(t, err) | 							assert.NoError(t, err) | ||||||
| 							assert.NotEmpty(t, pfs) | 							assert.NotEmpty(t, pfs) | ||||||
| 							assert.Condition(t, func() bool { | 							assert.Condition(t, func() bool { | ||||||
| @@ -162,17 +162,23 @@ func TestPackageDebian(t *testing.T) { | |||||||
| 						t.Run("Packages", func(t *testing.T) { | 						t.Run("Packages", func(t *testing.T) { | ||||||
| 							defer tests.PrintCurrentTest(t)() | 							defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
|  | 							req := NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, packageVersion2, architecture)) | ||||||
|  | 							AddBasicAuthHeader(req, user.Name) | ||||||
|  | 							MakeRequest(t, req, http.StatusCreated) | ||||||
|  |  | ||||||
| 							url := fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture) | 							url := fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture) | ||||||
|  |  | ||||||
| 							req := NewRequest(t, "GET", url) | 							req = NewRequest(t, "GET", url) | ||||||
| 							resp := MakeRequest(t, req, http.StatusOK) | 							resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 							body := resp.Body.String() | 							body := resp.Body.String() | ||||||
|  |  | ||||||
| 							assert.Contains(t, body, "Package: "+packageName) | 							assert.Contains(t, body, "Package: "+packageName+"\n") | ||||||
| 							assert.Contains(t, body, "Version: "+packageVersion) | 							assert.Contains(t, body, "Version: "+packageVersion+"\n") | ||||||
| 							assert.Contains(t, body, "Architecture: "+architecture) | 							assert.Contains(t, body, "Version: "+packageVersion2+"\n") | ||||||
| 							assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb", distribution, component, packageName, packageVersion, architecture)) | 							assert.Contains(t, body, "Architecture: "+architecture+"\n") | ||||||
|  | 							assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion, architecture)) | ||||||
|  | 							assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion2, architecture)) | ||||||
|  |  | ||||||
| 							req = NewRequest(t, "GET", url+".gz") | 							req = NewRequest(t, "GET", url+".gz") | ||||||
| 							MakeRequest(t, req, http.StatusOK) | 							MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -198,14 +204,14 @@ func TestPackageDebian(t *testing.T) { | |||||||
|  |  | ||||||
| 				body := resp.Body.String() | 				body := resp.Body.String() | ||||||
|  |  | ||||||
| 				assert.Contains(t, body, "Components: "+strings.Join(components, " ")) | 				assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n") | ||||||
| 				assert.Contains(t, body, "Architectures: "+strings.Join(architectures, " ")) | 				assert.Contains(t, body, "Architectures: "+strings.Join(architectures, " ")+"\n") | ||||||
|  |  | ||||||
| 				for _, component := range components { | 				for _, component := range components { | ||||||
| 					for _, architecture := range architectures { | 					for _, architecture := range architectures { | ||||||
| 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages", component, architecture)) | 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages\n", component, architecture)) | ||||||
| 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.gz", component, architecture)) | 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.gz\n", component, architecture)) | ||||||
| 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.xz", component, architecture)) | 						assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.xz\n", component, architecture)) | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| @@ -241,6 +247,10 @@ func TestPackageDebian(t *testing.T) { | |||||||
| 			AddBasicAuthHeader(req, user.Name) | 			AddBasicAuthHeader(req, user.Name) | ||||||
| 			MakeRequest(t, req, http.StatusNoContent) | 			MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
|  | 			req = NewRequest(t, "DELETE", fmt.Sprintf("%s/pool/%s/%s/%s/%s/%s", rootURL, distribution, component, packageName, packageVersion2, architecture)) | ||||||
|  | 			AddBasicAuthHeader(req, user.Name) | ||||||
|  | 			MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 			req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture)) | 			req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture)) | ||||||
| 			MakeRequest(t, req, http.StatusNotFound) | 			MakeRequest(t, req, http.StatusNotFound) | ||||||
| 		} | 		} | ||||||
| @@ -250,7 +260,7 @@ func TestPackageDebian(t *testing.T) { | |||||||
|  |  | ||||||
| 		body := resp.Body.String() | 		body := resp.Body.String() | ||||||
|  |  | ||||||
| 		assert.Contains(t, body, "Components: "+strings.Join(components, " ")) | 		assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n") | ||||||
| 		assert.Contains(t, body, "Architectures: "+architectures[1]) | 		assert.Contains(t, body, "Architectures: "+architectures[1]+"\n") | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user