Skip to content

Commit a2ae7c6

Browse files
authored
Fix some package registry problems (#34759)
1. Fix #33787 2. Fix container image display
1 parent 7954f25 commit a2ae7c6

File tree

11 files changed

+118
-60
lines changed

11 files changed

+118
-60
lines changed

models/packages/package_property.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
9292
return err
9393
}
9494

95-
// DeletePropertyByName deletes properties by name
96-
func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
95+
// DeletePropertiesByName deletes properties by name
96+
func DeletePropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
9797
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
9898
return err
9999
}

modules/packages/content_store.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ func NewContentStore() *ContentStore {
2828
return contentStore
2929
}
3030

31-
// Get gets a package blob
32-
func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
31+
func (s *ContentStore) OpenBlob(key BlobHash256Key) (storage.Object, error) {
3332
return s.store.Open(KeyToRelativePath(key))
3433
}
3534

routers/api/packages/container/manifest.go

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"code.gitea.io/gitea/modules/util"
2424
notify_service "code.gitea.io/gitea/services/notify"
2525
packages_service "code.gitea.io/gitea/services/packages"
26+
container_service "code.gitea.io/gitea/services/packages/container"
2627

2728
"github.com/opencontainers/go-digest"
2829
oci "github.com/opencontainers/image-spec/specs-go/v1"
@@ -84,12 +85,11 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
8485
manifestDigest := ""
8586

8687
err := func() error {
87-
var manifest oci.Manifest
88-
if err := json.NewDecoder(buf).Decode(&manifest); err != nil {
88+
manifest, configDescriptor, metadata, err := container_service.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
89+
if err != nil {
8990
return err
9091
}
91-
92-
if _, err := buf.Seek(0, io.SeekStart); err != nil {
92+
if _, err = buf.Seek(0, io.SeekStart); err != nil {
9393
return err
9494
}
9595

@@ -99,28 +99,7 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
9999
}
100100
defer committer.Close()
101101

102-
configDescriptor, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
103-
OwnerID: mci.Owner.ID,
104-
Image: mci.Image,
105-
Digest: string(manifest.Config.Digest),
106-
})
107-
if err != nil {
108-
return err
109-
}
110-
111-
configReader, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(configDescriptor.Blob.HashSHA256))
112-
if err != nil {
113-
return err
114-
}
115-
defer configReader.Close()
116-
117-
metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
118-
if err != nil {
119-
return err
120-
}
121-
122102
blobReferences := make([]*blobReference, 0, 1+len(manifest.Layers))
123-
124103
blobReferences = append(blobReferences, &blobReference{
125104
Digest: manifest.Config.Digest,
126105
MediaType: manifest.Config.MediaType,
@@ -388,19 +367,16 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
388367
return nil, err
389368
}
390369
} else {
391-
props, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged)
392-
if err != nil {
370+
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged); err != nil {
393371
return nil, err
394372
}
395-
for _, prop := range props {
396-
if err = packages_model.DeletePropertyByID(ctx, prop.ID); err != nil {
397-
return nil, err
398-
}
399-
}
400373
}
401374

375+
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference); err != nil {
376+
return nil, err
377+
}
402378
for _, manifest := range metadata.Manifests {
403-
if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
379+
if _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
404380
return nil, err
405381
}
406382
}

routers/web/user/package.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
package user
55

66
import (
7+
gocontext "context"
8+
"errors"
79
"net/http"
810
"net/url"
911

@@ -20,6 +22,7 @@ import (
2022
"code.gitea.io/gitea/modules/optional"
2123
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
2224
arch_module "code.gitea.io/gitea/modules/packages/arch"
25+
container_module "code.gitea.io/gitea/modules/packages/container"
2326
debian_module "code.gitea.io/gitea/modules/packages/debian"
2427
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
2528
"code.gitea.io/gitea/modules/setting"
@@ -31,6 +34,7 @@ import (
3134
"code.gitea.io/gitea/services/context"
3235
"code.gitea.io/gitea/services/forms"
3336
packages_service "code.gitea.io/gitea/services/packages"
37+
container_service "code.gitea.io/gitea/services/packages/container"
3438
)
3539

3640
const (
@@ -162,13 +166,32 @@ func RedirectToLastVersion(ctx *context.Context) {
162166
ctx.Redirect(pd.VersionWebLink())
163167
}
164168

169+
func viewPackageContainerImage(ctx gocontext.Context, pd *packages_model.PackageDescriptor, digest string) (*container_module.Metadata, error) {
170+
manifestBlob, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
171+
OwnerID: pd.Owner.ID,
172+
Image: pd.Package.LowerName,
173+
Digest: digest,
174+
})
175+
if err != nil {
176+
return nil, err
177+
}
178+
manifestReader, err := packages_service.OpenBlobStream(manifestBlob.Blob)
179+
if err != nil {
180+
return nil, err
181+
}
182+
defer manifestReader.Close()
183+
_, _, metadata, err := container_service.ParseManifestMetadata(ctx, manifestReader, pd.Owner.ID, pd.Package.LowerName)
184+
return metadata, err
185+
}
186+
165187
// ViewPackageVersion displays a single package version
166188
func ViewPackageVersion(ctx *context.Context) {
167189
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
168190
ctx.ServerError("RenderUserOrgHeader", err)
169191
return
170192
}
171193

194+
versionSub := ctx.PathParam("version_sub")
172195
pd := ctx.Package.Descriptor
173196
ctx.Data["Title"] = pd.Package.Name
174197
ctx.Data["IsPackagesPage"] = true
@@ -180,6 +203,9 @@ func ViewPackageVersion(ctx *context.Context) {
180203
}
181204
ctx.Data["PackageRegistryHost"] = registryHostURL.Host
182205

206+
var pvs []*packages_model.PackageVersion
207+
pvsTotal := int64(0)
208+
183209
switch pd.Package.Type {
184210
case packages_model.TypeAlpine:
185211
branches := make(container.Set[string])
@@ -257,21 +283,26 @@ func ViewPackageVersion(ctx *context.Context) {
257283

258284
ctx.Data["Groups"] = util.Sorted(groups.Values())
259285
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
260-
}
261-
262-
var (
263-
total int64
264-
pvs []*packages_model.PackageVersion
265-
)
266-
switch pd.Package.Type {
267286
case packages_model.TypeContainer:
268-
pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
287+
imageMetadata := pd.Metadata
288+
if versionSub != "" {
289+
imageMetadata, err = viewPackageContainerImage(ctx, pd, versionSub)
290+
if errors.Is(err, util.ErrNotExist) {
291+
ctx.NotFound(nil)
292+
return
293+
} else if err != nil {
294+
ctx.ServerError("viewPackageContainerImage", err)
295+
return
296+
}
297+
}
298+
ctx.Data["ContainerImageMetadata"] = imageMetadata
299+
pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
269300
Paginator: db.NewAbsoluteListOptions(0, 5),
270301
PackageID: pd.Package.ID,
271302
IsTagged: true,
272303
})
273304
default:
274-
pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
305+
pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
275306
Paginator: db.NewAbsoluteListOptions(0, 5),
276307
PackageID: pd.Package.ID,
277308
IsInternal: optional.Some(false),
@@ -283,7 +314,7 @@ func ViewPackageVersion(ctx *context.Context) {
283314
}
284315

285316
ctx.Data["LatestVersions"] = pvs
286-
ctx.Data["TotalVersionCount"] = total
317+
ctx.Data["TotalVersionCount"] = pvsTotal
287318

288319
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
289320

routers/web/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ func registerWebRoutes(m *web.Router) {
10121012
m.Get("/versions", user.ListPackageVersions)
10131013
m.Group("/{version}", func() {
10141014
m.Get("", user.ViewPackageVersion)
1015+
m.Get("/{version_sub}", user.ViewPackageVersion)
10151016
m.Get("/files/{fileid}", user.DownloadPackageFile)
10161017
m.Group("/settings", func() {
10171018
m.Get("", user.PackageSettings)

services/packages/container/common.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ package container
55

66
import (
77
"context"
8+
"io"
89
"strings"
910

1011
packages_model "code.gitea.io/gitea/models/packages"
12+
container_service "code.gitea.io/gitea/models/packages/container"
1113
user_model "code.gitea.io/gitea/models/user"
14+
"code.gitea.io/gitea/modules/json"
15+
"code.gitea.io/gitea/modules/packages"
1216
container_module "code.gitea.io/gitea/modules/packages/container"
17+
18+
"github.com/opencontainers/image-spec/specs-go/v1"
1319
)
1420

1521
// UpdateRepositoryNames updates the repository name property for all packages of the specific owner
@@ -22,7 +28,7 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
2228
newOwnerName = strings.ToLower(newOwnerName)
2329

2430
for _, p := range ps {
25-
if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
31+
if err := packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
2632
return err
2733
}
2834

@@ -33,3 +39,26 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
3339

3440
return nil
3541
}
42+
43+
func ParseManifestMetadata(ctx context.Context, rd io.Reader, ownerID int64, imageName string) (*v1.Manifest, *packages_model.PackageFileDescriptor, *container_module.Metadata, error) {
44+
var manifest v1.Manifest
45+
if err := json.NewDecoder(rd).Decode(&manifest); err != nil {
46+
return nil, nil, nil, err
47+
}
48+
configDescriptor, err := container_service.GetContainerBlob(ctx, &container_service.BlobSearchOptions{
49+
OwnerID: ownerID,
50+
Image: imageName,
51+
Digest: string(manifest.Config.Digest),
52+
})
53+
if err != nil {
54+
return nil, nil, nil, err
55+
}
56+
57+
configReader, err := packages.NewContentStore().OpenBlob(packages.BlobHash256Key(configDescriptor.Blob.HashSHA256))
58+
if err != nil {
59+
return nil, nil, nil, err
60+
}
61+
defer configReader.Close()
62+
metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
63+
return &manifest, configDescriptor, metadata, err
64+
}

services/packages/packages.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,12 @@ func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (
599599
return GetPackageBlobStream(ctx, pf, pb, nil)
600600
}
601601

602+
func OpenBlobStream(pb *packages_model.PackageBlob) (io.ReadSeekCloser, error) {
603+
cs := packages_module.NewContentStore()
604+
key := packages_module.BlobHash256Key(pb.HashSHA256)
605+
return cs.OpenBlob(key)
606+
}
607+
602608
// GetPackageBlobStream returns the content of the specific package blob
603609
// If the storage supports direct serving and it's enabled, only the direct serving url is returned.
604610
func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
@@ -617,7 +623,7 @@ func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, p
617623
}
618624
}
619625
if u == nil {
620-
s, err = cs.Get(key)
626+
s, err = cs.OpenBlob(key)
621627
}
622628

623629
if err == nil {

templates/package/content/container.tmpl

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@
4949
{{/* "unknown/unknown" is attestation-manifest, so we should skip it */}}
5050
{{if ne .Platform "unknown/unknown"}}
5151
<tr>
52-
<td><a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}</a></td>
52+
<td>
53+
<a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{$.PackageDescriptor.Version.LowerVersion}}/{{PathEscape .Digest}}">
54+
{{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}
55+
</a>
56+
</td>
5357
<td>{{.Platform}}</td>
5458
<td>{{FileSize .Size}}</td>
5559
</tr>
@@ -65,12 +69,24 @@
6569
{{.PackageDescriptor.Metadata.Description}}
6670
</div>
6771
{{end}}
68-
{{if .PackageDescriptor.Metadata.ImageLayers}}
69-
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.layers"}}</h4>
72+
73+
{{/* a container manifest may contain sub manifests, so here we try to display some information of the sub manifest,
74+
not perfect, just better than before */}}
75+
{{$imageMetadata := .ContainerImageMetadata}}
76+
{{if $imageMetadata.ImageLayers}}
77+
<h4 class="ui top attached header flex-text-block">
78+
{{ctx.Locale.Tr "packages.container.layers"}}
79+
{{/* only show the platform if the image metadata is not the package's, which means that it is a sub manifest */}}
80+
{{if ne .ContainerImageMetadata .PackageDescriptor.Metadata}}
81+
<span class="tw-text-sm flex-text-inline" title="{{ctx.Locale.Tr "packages.container.details.platform"}}">
82+
({{svg "octicon-cpu" 12}} {{.ContainerImageMetadata.Platform}})
83+
</span>
84+
{{end}}
85+
</h4>
7086
<div class="ui attached segment tw-break-anywhere">
7187
<table class="ui very basic compact table">
7288
<tbody>
73-
{{range .PackageDescriptor.Metadata.ImageLayers}}
89+
{{range $imageMetadata.ImageLayers}}
7490
<tr>
7591
<td>{{.}}</td>
7692
</tr>
@@ -79,7 +95,7 @@
7995
</table>
8096
</div>
8197
{{end}}
82-
{{if .PackageDescriptor.Metadata.Labels}}
98+
{{if $imageMetadata.Labels}}
8399
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.labels"}}</h4>
84100
<div class="ui attached segment">
85101
<table class="ui very basic compact table">
@@ -90,7 +106,7 @@
90106
</tr>
91107
</thead>
92108
<tbody>
93-
{{range $key, $value := .PackageDescriptor.Metadata.Labels}}
109+
{{range $key, $value := $imageMetadata.Labels}}
94110
<tr>
95111
<td class="tw-align-top">{{$key}}</td>
96112
<td class="tw-break-anywhere">{{$value}}</td>

templates/package/content/pypi.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div class="ui form">
55
<div class="field">
66
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label>
7-
<div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> {{.PackageDescriptor.Package.Name}}</code></pre></div>
7+
<div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> --extra-index-url https://pypi.org/ {{.PackageDescriptor.Package.Name}}</code></pre></div>
88
</div>
99
<div class="field">
1010
<label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://docs.gitea.com/usage/packages/pypi/"}}</label>

templates/package/shared/view.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<div class="issue-title-header">
2+
{{$packageVersionLink := print $.PackageDescriptor.PackageWebLink "/" (PathEscape .PackageDescriptor.Version.LowerVersion)}}
23
<h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
34
<div>
45
{{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
@@ -74,7 +75,7 @@
7475
<div class="ui relaxed list">
7576
{{range .PackageDescriptor.Files}}
7677
<div class="item">
77-
<a href="{{$.Link}}/files/{{.File.ID}}">{{.File.Name}}</a>
78+
<a href="{{$packageVersionLink}}/files/{{.File.ID}}">{{.File.Name}}</a>
7879
<span class="text small file-size">{{FileSize .Blob.Size}}</span>
7980
</div>
8081
{{end}}
@@ -98,7 +99,7 @@
9899
<div class="item">{{svg "octicon-issue-opened"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{ctx.Locale.Tr "repo.issues"}}</a></div>
99100
{{end}}
100101
{{if .CanWritePackages}}
101-
<div class="item">{{svg "octicon-tools"}} <a href="{{.Link}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
102+
<div class="item">{{svg "octicon-tools"}} <a href="{{$packageVersionLink}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
102103
{{end}}
103104
</div>
104105
{{end}}

tests/integration/api_packages_container_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,7 @@ func TestPackageContainer(t *testing.T) {
562562
assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository))
563563
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
564564

565-
// only the last manifest digest is associated with the version (OCI builders will push the index manifest digest as the final step)
566-
assert.ElementsMatch(t, []string{untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference))
565+
assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference))
567566

568567
assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
569568
metadata := pd.Metadata.(*container_module.Metadata)

0 commit comments

Comments
 (0)