Skip to content

Commit b36e2ca

Browse files
authored
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.
1 parent 02dae3f commit b36e2ca

File tree

5 files changed

+70
-42
lines changed

5 files changed

+70
-42
lines changed

models/packages/container/search.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ func (opts *ImageTagsSearchOptions) configureOrderBy(e db.Engine) {
207207
default:
208208
e.Desc("package_version.created_unix")
209209
}
210+
211+
// Sort by id for stable order with duplicates in the other field
212+
e.Asc("package_version.id")
210213
}
211214

212215
// SearchImageTags gets a sorted list of the tags of an image

models/packages/debian/search.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ type PackageSearchOptions struct {
2121
Architecture string
2222
}
2323

24-
// SearchLatestPackages gets the latest packages matching the search options
25-
func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*packages.PackageFileDescriptor, error) {
24+
func (opts *PackageSearchOptions) toCond() builder.Cond {
2625
var cond builder.Cond = builder.Eq{
2726
"package_file.is_lead": true,
2827
"package.type": packages.TypeDebian,
@@ -62,28 +61,40 @@ func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*p
6261
})
6362
}
6463

65-
cond = cond.
66-
And(builder.Expr("pv2.id IS NULL"))
64+
return cond
65+
}
6766

68-
joinCond := builder.
69-
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))").
70-
And(builder.Eq{"pv2.is_internal": false})
67+
// ExistPackages tests if there are packages matching the search options
68+
func ExistPackages(ctx context.Context, opts *PackageSearchOptions) (bool, error) {
69+
return db.GetEngine(ctx).
70+
Table("package_file").
71+
Join("INNER", "package_version", "package_version.id = package_file.version_id").
72+
Join("INNER", "package", "package.id = package_version.package_id").
73+
Where(opts.toCond()).
74+
Exist(new(packages.PackageFile))
75+
}
7176

72-
pfs := make([]*packages.PackageFile, 0, 10)
73-
err := db.GetEngine(ctx).
77+
// SearchPackages gets the packages matching the search options
78+
func SearchPackages(ctx context.Context, opts *PackageSearchOptions, iter func(*packages.PackageFileDescriptor)) error {
79+
return db.GetEngine(ctx).
7480
Table("package_file").
7581
Select("package_file.*").
7682
Join("INNER", "package_version", "package_version.id = package_file.version_id").
77-
Join("LEFT", "package_version pv2", joinCond).
7883
Join("INNER", "package", "package.id = package_version.package_id").
79-
Where(cond).
80-
Desc("package_version.created_unix").
81-
Find(&pfs)
82-
if err != nil {
83-
return nil, err
84-
}
84+
Where(opts.toCond()).
85+
Asc("package.lower_name", "package_version.created_unix").
86+
Iterate(new(packages.PackageFile), func(_ int, bean any) error {
87+
pf := bean.(*packages.PackageFile)
88+
89+
pfd, err := packages.GetPackageFileDescriptor(ctx, pf)
90+
if err != nil {
91+
return err
92+
}
8593

86-
return packages.GetPackageFileDescriptors(ctx, pfs)
94+
iter(pfd)
95+
96+
return nil
97+
})
8798
}
8899

89100
// GetDistributions gets all available distributions

models/packages/package_version.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
278278
default:
279279
e.Desc("package_version.created_unix")
280280
}
281+
282+
// Sort by id for stable order with duplicates in the other field
283+
e.Asc("package_version.id")
281284
}
282285

283286
// SearchVersions gets all versions of packages matching the search options

services/packages/debian/repository.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,18 +165,17 @@ func buildRepositoryFiles(ctx context.Context, ownerID int64, repoVersion *packa
165165

166166
// https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices
167167
func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packages_model.PackageVersion, distribution, component, architecture string) error {
168-
pfds, err := debian_model.SearchLatestPackages(ctx, &debian_model.PackageSearchOptions{
168+
opts := &debian_model.PackageSearchOptions{
169169
OwnerID: ownerID,
170170
Distribution: distribution,
171171
Component: component,
172172
Architecture: architecture,
173-
})
174-
if err != nil {
175-
return err
176173
}
177174

178175
// Delete the package indices if there are no packages
179-
if len(pfds) == 0 {
176+
if has, err := debian_model.ExistPackages(ctx, opts); err != nil {
177+
return err
178+
} else if !has {
180179
key := fmt.Sprintf("%s|%s|%s", distribution, component, architecture)
181180
for _, filename := range []string{"Packages", "Packages.gz", "Packages.xz"} {
182181
pf, err := packages_model.GetFileForVersionByName(ctx, repoVersion.ID, filename, key)
@@ -211,7 +210,7 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa
211210
w := io.MultiWriter(packagesContent, gzw, xzw)
212211

213212
addSeparator := false
214-
for _, pfd := range pfds {
213+
if err := debian_model.SearchPackages(ctx, opts, func(pfd *packages_model.PackageFileDescriptor) {
215214
if addSeparator {
216215
fmt.Fprintln(w)
217216
}
@@ -225,6 +224,8 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa
225224
fmt.Fprintf(w, "SHA1: %s\n", pfd.Blob.HashSHA1)
226225
fmt.Fprintf(w, "SHA256: %s\n", pfd.Blob.HashSHA256)
227226
fmt.Fprintf(w, "SHA512: %s\n", pfd.Blob.HashSHA512)
227+
}); err != nil {
228+
return err
228229
}
229230

230231
gzw.Close()
@@ -238,7 +239,7 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa
238239
{"Packages.gz", packagesGzipContent},
239240
{"Packages.xz", packagesXzContent},
240241
} {
241-
_, err = packages_service.AddFileToPackageVersionInternal(
242+
_, err := packages_service.AddFileToPackageVersionInternal(
242243
ctx,
243244
repoVersion,
244245
&packages_service.PackageFileCreationInfo{

tests/integration/api_packages_debian_test.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func TestPackageDebian(t *testing.T) {
3131

3232
packageName := "gitea"
3333
packageVersion := "1.0.3"
34+
packageVersion2 := "1.0.4"
3435
packageDescription := "Package Description"
3536

3637
createArchive := func(name, version, architecture string) io.Reader {
@@ -80,11 +81,11 @@ func TestPackageDebian(t *testing.T) {
8081
for _, component := range components {
8182
for _, architecture := range architectures {
8283
t.Run(fmt.Sprintf("[Component:%s,Architecture:%s]", component, architecture), func(t *testing.T) {
84+
uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component)
85+
8386
t.Run("Upload", func(t *testing.T) {
8487
defer tests.PrintCurrentTest(t)()
8588

86-
uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component)
87-
8889
req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
8990
MakeRequest(t, req, http.StatusUnauthorized)
9091

@@ -100,18 +101,17 @@ func TestPackageDebian(t *testing.T) {
100101
AddBasicAuthHeader(req, user.Name)
101102
MakeRequest(t, req, http.StatusCreated)
102103

103-
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeDebian)
104+
pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeDebian, packageName, packageVersion)
104105
assert.NoError(t, err)
105-
assert.Len(t, pvs, 1)
106106

107-
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
107+
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
108108
assert.NoError(t, err)
109109
assert.Nil(t, pd.SemVer)
110110
assert.IsType(t, &debian_module.Metadata{}, pd.Metadata)
111111
assert.Equal(t, packageName, pd.Package.Name)
112112
assert.Equal(t, packageVersion, pd.Version.Version)
113113

114-
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
114+
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pv.ID)
115115
assert.NoError(t, err)
116116
assert.NotEmpty(t, pfs)
117117
assert.Condition(t, func() bool {
@@ -162,17 +162,23 @@ func TestPackageDebian(t *testing.T) {
162162
t.Run("Packages", func(t *testing.T) {
163163
defer tests.PrintCurrentTest(t)()
164164

165+
req := NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, packageVersion2, architecture))
166+
AddBasicAuthHeader(req, user.Name)
167+
MakeRequest(t, req, http.StatusCreated)
168+
165169
url := fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture)
166170

167-
req := NewRequest(t, "GET", url)
171+
req = NewRequest(t, "GET", url)
168172
resp := MakeRequest(t, req, http.StatusOK)
169173

170174
body := resp.Body.String()
171175

172-
assert.Contains(t, body, "Package: "+packageName)
173-
assert.Contains(t, body, "Version: "+packageVersion)
174-
assert.Contains(t, body, "Architecture: "+architecture)
175-
assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb", distribution, component, packageName, packageVersion, architecture))
176+
assert.Contains(t, body, "Package: "+packageName+"\n")
177+
assert.Contains(t, body, "Version: "+packageVersion+"\n")
178+
assert.Contains(t, body, "Version: "+packageVersion2+"\n")
179+
assert.Contains(t, body, "Architecture: "+architecture+"\n")
180+
assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion, architecture))
181+
assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion2, architecture))
176182

177183
req = NewRequest(t, "GET", url+".gz")
178184
MakeRequest(t, req, http.StatusOK)
@@ -198,14 +204,14 @@ func TestPackageDebian(t *testing.T) {
198204

199205
body := resp.Body.String()
200206

201-
assert.Contains(t, body, "Components: "+strings.Join(components, " "))
202-
assert.Contains(t, body, "Architectures: "+strings.Join(architectures, " "))
207+
assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n")
208+
assert.Contains(t, body, "Architectures: "+strings.Join(architectures, " ")+"\n")
203209

204210
for _, component := range components {
205211
for _, architecture := range architectures {
206-
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages", component, architecture))
207-
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.gz", component, architecture))
208-
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.xz", component, architecture))
212+
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages\n", component, architecture))
213+
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.gz\n", component, architecture))
214+
assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.xz\n", component, architecture))
209215
}
210216
}
211217

@@ -241,6 +247,10 @@ func TestPackageDebian(t *testing.T) {
241247
AddBasicAuthHeader(req, user.Name)
242248
MakeRequest(t, req, http.StatusNoContent)
243249

250+
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/pool/%s/%s/%s/%s/%s", rootURL, distribution, component, packageName, packageVersion2, architecture))
251+
AddBasicAuthHeader(req, user.Name)
252+
MakeRequest(t, req, http.StatusNoContent)
253+
244254
req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture))
245255
MakeRequest(t, req, http.StatusNotFound)
246256
}
@@ -250,7 +260,7 @@ func TestPackageDebian(t *testing.T) {
250260

251261
body := resp.Body.String()
252262

253-
assert.Contains(t, body, "Components: "+strings.Join(components, " "))
254-
assert.Contains(t, body, "Architectures: "+architectures[1])
263+
assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n")
264+
assert.Contains(t, body, "Architectures: "+architectures[1]+"\n")
255265
})
256266
}

0 commit comments

Comments
 (0)