Skip to content

Commit 889fd15

Browse files
committed
fix
1 parent b388138 commit 889fd15

File tree

12 files changed

+124
-60
lines changed

12 files changed

+124
-60
lines changed

models/packages/container/manifest.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package container
5+
6+
import (
7+
"context"
8+
"io"
9+
10+
"code.gitea.io/gitea/models/packages"
11+
"code.gitea.io/gitea/modules/json"
12+
packages_module "code.gitea.io/gitea/modules/packages"
13+
container_module "code.gitea.io/gitea/modules/packages/container"
14+
15+
oci "github.com/opencontainers/image-spec/specs-go/v1"
16+
)
17+
18+
func ParseManifestMetadata(ctx context.Context, rd io.Reader, ownerID int64, imageName string) (*oci.Manifest, *packages.PackageFileDescriptor, *container_module.Metadata, error) {
19+
var manifest oci.Manifest
20+
if err := json.NewDecoder(rd).Decode(&manifest); err != nil {
21+
return nil, nil, nil, err
22+
}
23+
configDescriptor, err := GetContainerBlob(ctx, &BlobSearchOptions{
24+
OwnerID: ownerID,
25+
Image: imageName,
26+
Digest: string(manifest.Config.Digest),
27+
})
28+
if err != nil {
29+
return nil, nil, nil, err
30+
}
31+
32+
configReader, err := packages_module.NewContentStore().OpenBlob(packages_module.BlobHash256Key(configDescriptor.Blob.HashSHA256))
33+
if err != nil {
34+
return nil, nil, nil, err
35+
}
36+
defer configReader.Close()
37+
metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
38+
return &manifest, configDescriptor, metadata, err
39+
}

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: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,11 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
8484
manifestDigest := ""
8585

8686
err := func() error {
87-
var manifest oci.Manifest
88-
if err := json.NewDecoder(buf).Decode(&manifest); err != nil {
87+
manifest, configDescriptor, metadata, err := container_model.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
88+
if err != nil {
8989
return err
9090
}
91-
92-
if _, err := buf.Seek(0, io.SeekStart); err != nil {
91+
if _, err = buf.Seek(0, io.SeekStart); err != nil {
9392
return err
9493
}
9594

@@ -99,28 +98,7 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
9998
}
10099
defer committer.Close()
101100

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-
122101
blobReferences := make([]*blobReference, 0, 1+len(manifest.Layers))
123-
124102
blobReferences = append(blobReferences, &blobReference{
125103
Digest: manifest.Config.Digest,
126104
MediaType: manifest.Config.MediaType,
@@ -388,19 +366,16 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
388366
return nil, err
389367
}
390368
} else {
391-
props, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged)
392-
if err != nil {
369+
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged); err != nil {
393370
return nil, err
394371
}
395-
for _, prop := range props {
396-
if err = packages_model.DeletePropertyByID(ctx, prop.ID); err != nil {
397-
return nil, err
398-
}
399-
}
400372
}
401373

374+
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference); err != nil {
375+
return nil, err
376+
}
402377
for _, manifest := range metadata.Manifests {
403-
if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
378+
if _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
404379
return nil, err
405380
}
406381
}

routers/web/user/package.go

Lines changed: 40 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"
@@ -162,13 +165,32 @@ func RedirectToLastVersion(ctx *context.Context) {
162165
ctx.Redirect(pd.VersionWebLink())
163166
}
164167

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

193+
versionSub := ctx.PathParam("version_sub")
172194
pd := ctx.Package.Descriptor
173195
ctx.Data["Title"] = pd.Package.Name
174196
ctx.Data["IsPackagesPage"] = true
@@ -180,6 +202,9 @@ func ViewPackageVersion(ctx *context.Context) {
180202
}
181203
ctx.Data["PackageRegistryHost"] = registryHostURL.Host
182204

205+
var pvs []*packages_model.PackageVersion
206+
pvsTotal := int64(0)
207+
183208
switch pd.Package.Type {
184209
case packages_model.TypeAlpine:
185210
branches := make(container.Set[string])
@@ -257,21 +282,26 @@ func ViewPackageVersion(ctx *context.Context) {
257282

258283
ctx.Data["Groups"] = util.Sorted(groups.Values())
259284
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 {
267285
case packages_model.TypeContainer:
268-
pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
286+
pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
269287
Paginator: db.NewAbsoluteListOptions(0, 5),
270288
PackageID: pd.Package.ID,
271289
IsTagged: true,
272290
})
291+
ctx.Data["ContainerImageMetadata"] = pd.Metadata
292+
if versionSub != "" {
293+
imageMeta, err := viewPackageContainerImage(ctx, pd, versionSub)
294+
if errors.Is(err, util.ErrNotExist) {
295+
ctx.NotFound(nil)
296+
return
297+
} else if err != nil {
298+
ctx.ServerError("viewPackageContainerImage", err)
299+
return
300+
}
301+
ctx.Data["ContainerImageMetadata"] = imageMeta
302+
}
273303
default:
274-
pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
304+
pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
275305
Paginator: db.NewAbsoluteListOptions(0, 5),
276306
PackageID: pd.Package.ID,
277307
IsInternal: optional.Some(false),
@@ -283,7 +313,7 @@ func ViewPackageVersion(ctx *context.Context) {
283313
}
284314

285315
ctx.Data["LatestVersions"] = pvs
286-
ctx.Data["TotalVersionCount"] = total
316+
ctx.Data["TotalVersionCount"] = pvsTotal
287317

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

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
2222
newOwnerName = strings.ToLower(newOwnerName)
2323

2424
for _, p := range ps {
25-
if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
25+
if err := packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
2626
return err
2727
}
2828

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: 20 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,22 @@
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 the layers and labels of the sub manifest */}}
74+
{{$imageMetadata := .ContainerImageMetadata}}
75+
{{if $imageMetadata.ImageLayers}}
76+
<h4 class="ui top attached header flex-text-block">
77+
{{ctx.Locale.Tr "packages.container.layers"}}
78+
{{if ne .ContainerImageMetadata .PackageDescriptor.Metadata}}
79+
<span class="tw-text-sm flex-text-inline" title="{{ctx.Locale.Tr "packages.container.details.platform"}}">
80+
({{svg "octicon-cpu" 12}} {{.ContainerImageMetadata.Platform}})
81+
</span>
82+
{{end}}
83+
</h4>
7084
<div class="ui attached segment tw-break-anywhere">
7185
<table class="ui very basic compact table">
7286
<tbody>
73-
{{range .PackageDescriptor.Metadata.ImageLayers}}
87+
{{range $imageMetadata.ImageLayers}}
7488
<tr>
7589
<td>{{.}}</td>
7690
</tr>
@@ -79,7 +93,7 @@
7993
</table>
8094
</div>
8195
{{end}}
82-
{{if .PackageDescriptor.Metadata.Labels}}
96+
{{if $imageMetadata.Labels}}
8397
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.labels"}}</h4>
8498
<div class="ui attached segment">
8599
<table class="ui very basic compact table">
@@ -90,7 +104,7 @@
90104
</tr>
91105
</thead>
92106
<tbody>
93-
{{range $key, $value := .PackageDescriptor.Metadata.Labels}}
107+
{{range $key, $value := $imageMetadata.Labels}}
94108
<tr>
95109
<td class="tw-align-top">{{$key}}</td>
96110
<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>

0 commit comments

Comments
 (0)