fix: reduce runtime of container cleanup by relying on mass digest cleanup (#10297)

The package cleanup routine checks every container version for whether it is referenced by a multi-platform manifest, which appears to be a performance problem indicated by CPU profiling collected in #9358 on SQLite systems.  This PR removes that check completely, which isn't necessary since #4698 added a much more performant mass-cleanup of these dangling platform versions.

May fix #9358 completely, but it leaves fundamental scalability concerns with SQLite due to long-running transactions.  The transactions will be shorter with this change.  Requires end-user testing to confirm if sufficiently fixed.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/10297): <!--number 10297 --><!--line 0 --><!--description cmVkdWNlIHJ1bnRpbWUgb2YgY29udGFpbmVyIGNsZWFudXAgYnkgcmVseWluZyBvbiBtYXNzIGRpZ2VzdCBjbGVhbnVw-->reduce runtime of container cleanup by relying on mass digest cleanup<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10297
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
This commit is contained in:
Mathieu Fenniak 2025-12-05 15:45:47 +01:00 committed by Mathieu Fenniak
parent d5fa12ded1
commit ac487e93d9
10 changed files with 506 additions and 141 deletions

View file

@ -7,20 +7,18 @@ import (
"errors"
"fmt"
"net/http"
"time"
packages_model "forgejo.org/models/packages"
repo_model "forgejo.org/models/repo"
user_model "forgejo.org/models/user"
"forgejo.org/modules/base"
"forgejo.org/modules/log"
"forgejo.org/modules/optional"
"forgejo.org/modules/util"
"forgejo.org/modules/web"
"forgejo.org/services/context"
"forgejo.org/services/forms"
cargo_service "forgejo.org/services/packages/cargo"
container_service "forgejo.org/services/packages/container"
cleanup_service "forgejo.org/services/packages/cleanup"
)
func SetPackagesContext(ctx *context.Context, owner *user_model.User) {
@ -147,66 +145,18 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) {
return
}
if err := pcr.CompiledPattern(); err != nil {
ctx.ServerError("CompiledPattern", err)
return
}
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
versionsToRemove, err := cleanup_service.GetCleanupTargets(ctx, pcr, false)
if err != nil {
ctx.ServerError("GetPackagesByType", err)
ctx.ServerError("GetCleanupTargets", err)
return
}
versionsToRemove := make([]*packages_model.PackageDescriptor, 0, 10)
for _, p := range packages {
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
PackageID: p.ID,
IsInternal: optional.Some(false),
Sort: packages_model.SortCreatedDesc,
})
if err != nil {
ctx.ServerError("SearchVersions", err)
return
}
keep := min(len(pvs), pcr.KeepCount)
for _, pv := range pvs[keep:] {
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
ctx.ServerError("ShouldBeSkipped", err)
return
} else if skip {
continue
}
toMatch := pv.LowerVersion
if pcr.MatchFullName {
toMatch = p.LowerName + "/" + pv.LowerVersion
}
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
continue
}
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
continue
}
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
continue
}
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
ctx.ServerError("GetPackageDescriptor", err)
return
}
versionsToRemove = append(versionsToRemove, pd)
}
packageDescriptors := make([]*packages_model.PackageDescriptor, len(versionsToRemove))
for i := range len(versionsToRemove) {
packageDescriptors[i] = versionsToRemove[i].PackageDescriptor
}
ctx.Data["CleanupRule"] = pcr
ctx.Data["VersionsToRemove"] = versionsToRemove
ctx.Data["VersionsToRemove"] = packageDescriptors
}
func getCleanupRuleByContext(ctx *context.Context, owner *user_model.User) *packages_model.PackageCleanupRule {

View file

@ -47,76 +47,29 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
default:
}
if err := pcr.CompiledPattern(); err != nil {
return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err)
}
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
versionsToRemove, err := GetCleanupTargets(ctx, pcr, true)
if err != nil {
return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err)
return fmt.Errorf("CleanupRule [%d]: GetCleanupTargets failed: %w", pcr.ID, err)
}
anyVersionDeleted := false
for _, p := range packages {
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
PackageID: p.ID,
IsInternal: optional.Some(false),
Sort: packages_model.SortCreatedDesc,
})
if err != nil {
return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
packageWithVersionDeleted := make(map[int64]bool) // set of Package.ID's where at least one package version was removed
for _, ct := range versionsToRemove {
if err := packages_service.DeletePackageVersionAndReferences(ctx, ct.PackageVersion); err != nil {
return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err)
}
versionDeleted := false
keep := min(len(pvs), pcr.KeepCount)
for _, pv := range pvs[keep:] {
if pcr.Type == packages_model.TypeContainer {
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)
} else if skip {
log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
continue
}
}
packageWithVersionDeleted[ct.Package.ID] = true
anyVersionDeleted = true
}
toMatch := pv.LowerVersion
if pcr.MatchFullName {
toMatch = p.LowerName + "/" + pv.LowerVersion
if pcr.Type == packages_model.TypeCargo {
for packageID := range packageWithVersionDeleted {
owner, err := user_model.GetUserByID(ctx, pcr.OwnerID)
if err != nil {
return fmt.Errorf("GetUserByID failed: %w", err)
}
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
continue
}
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
continue
}
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
continue
}
log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err)
}
versionDeleted = true
anyVersionDeleted = true
}
if versionDeleted {
if pcr.Type == packages_model.TypeCargo {
owner, err := user_model.GetUserByID(ctx, pcr.OwnerID)
if err != nil {
return fmt.Errorf("GetUserByID failed: %w", err)
}
if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, p.ID); err != nil {
return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err)
}
if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, packageID); err != nil {
return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err)
}
}
}
@ -154,6 +107,83 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
return committer.Commit()
}
type CleanupTarget struct {
Package *packages_model.Package
PackageVersion *packages_model.PackageVersion
PackageDescriptor *packages_model.PackageDescriptor
}
func GetCleanupTargets(ctx context.Context, pcr *packages_model.PackageCleanupRule, skipPackageDescriptor bool) ([]*CleanupTarget, error) {
if err := pcr.CompiledPattern(); err != nil {
return nil, err
}
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
if err != nil {
return nil, fmt.Errorf("failure to GetPackagesByType for package cleanup rule: %w", err)
}
versionsToRemove := make([]*CleanupTarget, 0, 10)
for _, p := range packages {
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
PackageID: p.ID,
IsInternal: optional.Some(false),
Sort: packages_model.SortCreatedDesc,
})
if err != nil {
return nil, fmt.Errorf("failure to SearchVersions for package cleanup rule: %w", err)
}
keep := min(len(pvs), pcr.KeepCount)
for _, pv := range pvs[keep:] {
if pcr.Type == packages_model.TypeContainer {
if skip := container_service.ShouldBeSkipped(pv); skip {
log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
continue
}
}
toMatch := pv.LowerVersion
if pcr.MatchFullName {
toMatch = p.LowerName + "/" + pv.LowerVersion
}
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
continue
}
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
continue
}
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
continue
}
log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
var pd *packages_model.PackageDescriptor
// GetPackageDescriptor is a bit expensive and can be skipped; only used for cleanup preview to display the package to the UI
if !skipPackageDescriptor {
pd, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
return nil, fmt.Errorf("failure to GetPackageDescriptor for package cleanup rule: %w", err)
}
}
versionsToRemove = append(versionsToRemove, &CleanupTarget{
Package: p,
PackageVersion: pv,
PackageDescriptor: pd,
})
}
}
return versionsToRemove, nil
}
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
ctx, committer, err := db.TxContext(outerCtx)
if err != nil {

View file

@ -10,7 +10,6 @@ import (
packages_model "forgejo.org/models/packages"
container_model "forgejo.org/models/packages/container"
"forgejo.org/modules/optional"
container_module "forgejo.org/modules/packages/container"
packages_service "forgejo.org/services/packages"
digest "github.com/opencontainers/go-digest"
@ -82,30 +81,18 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e
return nil
}
func ShouldBeSkipped(ctx context.Context, pcr *packages_model.PackageCleanupRule, p *packages_model.Package, pv *packages_model.PackageVersion) (bool, error) {
func ShouldBeSkipped(pv *packages_model.PackageVersion) bool {
// Always skip the "latest" tag
if pv.LowerVersion == "latest" {
return true, nil
return true
}
// Check if the version is a digest (or untagged)
if digest.Digest(pv.LowerVersion).Validate() == nil {
// Check if there is another manifest referencing this version
has, err := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{
PackageID: p.ID,
Properties: map[string]string{
container_module.PropertyManifestReference: pv.LowerVersion,
},
})
if err != nil {
return false, err
}
// Skip it if the version is referenced
if has {
return true, nil
}
// Don't apply PackageCleanupRules to image versions that aren't tags; rely on `CleanupSHA256` to do mass
// cleanup of them once they're dangling due to not being referenced.
return true
}
return false, nil
return false
}

View file

@ -0,0 +1,42 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package container
import (
"testing"
packages_model "forgejo.org/models/packages"
"github.com/stretchr/testify/assert"
)
func TestShouldBeSkipped(t *testing.T) {
tests := []struct {
name string
pv *packages_model.PackageVersion
skipped bool
}{
{
name: "latest: never cleaned up",
pv: &packages_model.PackageVersion{LowerVersion: "latest"},
skipped: true,
},
{
name: "sha256:* never cleaned up",
pv: &packages_model.PackageVersion{LowerVersion: "sha256:8a4d01effd20bcc5c65857885013efdbbdd466ac0a5a26b3fac573095533e3fc"},
skipped: true,
},
{
name: "sha256:* never cleaned up",
pv: &packages_model.PackageVersion{LowerVersion: "v1.0.0"},
skipped: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
skipped := ShouldBeSkipped(tt.pv)
assert.Equal(t, tt.skipped, skipped)
})
}
}

View file

@ -29,7 +29,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestPackagesContainerCleanupSHA256(t *testing.T) {
func TestPackageContainerCleanupSHA256(t *testing.T) {
defer tests.PrepareTestEnv(t, 1)()
defer test.MockVariableValue(&setting.Packages.Storage.Type, setting.LocalStorageType)()
defer test.MockVariableValue(&packages_container.SHA256BatchSize, 1)()

View file

@ -0,0 +1,101 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package integration
import (
"testing"
"time"
packages_model "forgejo.org/models/packages"
"forgejo.org/models/unittest"
packages_service "forgejo.org/services/packages"
packages_cleanup "forgejo.org/services/packages/cleanup"
"forgejo.org/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPackageContainerCleanup(t *testing.T) {
// Test fixture data contains three images; one that is a standard single-platform image (v2.0), and two that are a
// manifest multi-platform image (v1.0).
//
// The goal of testing here is to ensure that package cleanup can only remove the tags, and leave the `sha256:*`
// versions for cleanup once they're dangling by having no tags referencing them.
testCases := []struct {
name string
cleanupRule *packages_model.PackageCleanupRule
expectedCleanupVersion []string
expectedRemainingVersion []string
}{
{
name: "keep single-platform",
cleanupRule: &packages_model.PackageCleanupRule{
KeepCount: 1,
},
expectedCleanupVersion: []string{"v1.0"},
expectedRemainingVersion: []string{"v2.0"},
},
{
name: "keep multi-platform",
cleanupRule: &packages_model.PackageCleanupRule{
RemovePattern: "v2\\.0",
},
expectedCleanupVersion: []string{"v2.0"},
expectedRemainingVersion: []string{
"v1.0",
"sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12", // linux/arm64/v8 version of v1.0
"sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08", // linux/amd64 version of v1.0
},
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
defer unittest.OverrideFixtures("tests/integration/fixtures/TestPackageContainerCleanup")()
defer tests.PrepareTestEnvWithPackageData(t)()
// Reduce repetition in the test case array
tt.cleanupRule.Type = packages_model.TypeContainer
tt.cleanupRule.OwnerID = 1
targets, err := packages_cleanup.GetCleanupTargets(t.Context(), tt.cleanupRule, true)
require.NoError(t, err)
// Verify we have the expected targets
targetVersions := make([]string, len(targets))
for i := range targets {
targetVersions[i] = targets[i].PackageVersion.LowerVersion
}
assert.Len(t, targetVersions, len(tt.expectedCleanupVersion))
for _, expected := range tt.expectedCleanupVersion {
assert.Containsf(t, targetVersions, expected, "expected to cleanup %s, but didn't", expected)
}
// Delete the target packages
for _, ct := range targets {
err := packages_service.DeletePackageVersionAndReferences(t.Context(), ct.PackageVersion)
require.NoError(t, err)
}
// Perform the sha256 cleanup routine
require.NoError(t, packages_cleanup.CleanupExpiredData(t.Context(), -1*time.Hour))
// Verify the remaining versions are correct
remainingVersions, _, err := packages_model.SearchVersions(t.Context(), &packages_model.PackageSearchOptions{
OwnerID: tt.cleanupRule.OwnerID,
Type: tt.cleanupRule.Type,
})
require.NoError(t, err)
remainingVersionsStr := make([]string, len(remainingVersions))
for i := range remainingVersions {
remainingVersionsStr[i] = remainingVersions[i].LowerVersion
}
assert.Len(t, remainingVersionsStr, len(tt.expectedRemainingVersion))
for _, expected := range tt.expectedRemainingVersion {
assert.Contains(t, remainingVersionsStr, expected, "expected to find remaining version %s, but didn't", expected)
}
})
}
}

View file

@ -0,0 +1,9 @@
- id: 7
owner_id: 1
repo_id: 0
type: container
name: forgejo-runner-test
lower_name: forgejo-runner-test
semver_compatible: false
is_internal: false

View file

@ -0,0 +1,128 @@
- id: 4090
version_id: 736
blob_id: 1214
name: sha256_efc44d65797de15fdc9787e194f9e24ea42adc2fa2821203bb65ead939908375
lower_name: sha256_efc44d65797de15fdc9787e194f9e24ea42adc2fa2821203bb65ead939908375
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4091
version_id: 736
blob_id: 222
name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
lower_name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4092
version_id: 736
blob_id: 1212
name: sha256_9a0d1f70c6da347f1fa0c3856bbe2642a317361a54ca8b0e44b64c362b45e01d
lower_name: sha256_9a0d1f70c6da347f1fa0c3856bbe2642a317361a54ca8b0e44b64c362b45e01d
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4093
version_id: 736
blob_id: 1213
name: sha256_5a0f445468c4d9b40c4efb4bb292967672f5e9a2dd23e605a34fe10898e4cc4e
lower_name: sha256_5a0f445468c4d9b40c4efb4bb292967672f5e9a2dd23e605a34fe10898e4cc4e
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4094
version_id: 736
blob_id: 1215
name: manifest.json
lower_name: manifest.json
composite_key: ''
is_lead: true
created_unix: 1764694509
- id: 4099
version_id: 737
blob_id: 1219
name: sha256_495a889816ababcd9e1af0ed66ccc55c7225c6fcf171a7eac3f24ee62fa30f74
lower_name: sha256_495a889816ababcd9e1af0ed66ccc55c7225c6fcf171a7eac3f24ee62fa30f74
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4100
version_id: 737
blob_id: 1216
name: sha256_5096682701dd58e54ccbeb5d1fa285e9980a86568cfb0ca522fd4871892ab318
lower_name: sha256_5096682701dd58e54ccbeb5d1fa285e9980a86568cfb0ca522fd4871892ab318
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4101
version_id: 737
blob_id: 1218
name: sha256_714573ef664bd4969f0cb9b445efbb337f0d2f52d906f3f1323063ca9bdcb6a6
lower_name: sha256_714573ef664bd4969f0cb9b445efbb337f0d2f52d906f3f1323063ca9bdcb6a6
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4102
version_id: 737
blob_id: 1217
name: sha256_3dbb52544aa310f7819092b6c556aa1fb446e2c77711eda8e4680851445f8f18
lower_name: sha256_3dbb52544aa310f7819092b6c556aa1fb446e2c77711eda8e4680851445f8f18
composite_key: ''
is_lead: false
created_unix: 1764694509
- id: 4103
version_id: 737
blob_id: 1220
name: manifest.json
lower_name: manifest.json
composite_key: ''
is_lead: true
created_unix: 1764694509
- id: 4104
version_id: 738
blob_id: 1221
name: manifest.json
lower_name: manifest.json
composite_key: ''
is_lead: true
created_unix: 1764694509
- id: 4115
version_id: 740
blob_id: 2114
name: sha256_dd4d2c29faafea81b0b44cf8df13b8d0e01bc42059cdb69bf8f5d147b29904bf
lower_name: sha256_dd4d2c29faafea81b0b44cf8df13b8d0e01bc42059cdb69bf8f5d147b29904bf
composite_key: ''
is_lead: false
created_unix: 1764695172
- id: 4116
version_id: 740
blob_id: 222
name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
lower_name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
composite_key: ''
is_lead: false
created_unix: 1764695172
- id: 4117
version_id: 740
blob_id: 1222
name: sha256_9c3b61a2981c0986b91339acca08bff16acc239d1909037652d6f439d3e84578
lower_name: sha256_9c3b61a2981c0986b91339acca08bff16acc239d1909037652d6f439d3e84578
composite_key: ''
is_lead: false
created_unix: 1764695172
- id: 4118
version_id: 740
blob_id: 2113
name: sha256_bbd073df829f756775f9e7143f99bd5a0637a4be4ba95280a496c732e9ed45ea
lower_name: sha256_bbd073df829f756775f9e7143f99bd5a0637a4be4ba95280a496c732e9ed45ea
composite_key: ''
is_lead: false
created_unix: 1764695172
- id: 4119
version_id: 740
blob_id: 2115
name: manifest.json
lower_name: manifest.json
composite_key: ''
is_lead: true
created_unix: 1764695172

View file

@ -0,0 +1,112 @@
- id: 735
package_id: 7
creator_id: 1
version: _upload
lower_version: _upload
created_unix: 1764694509
is_internal: true
metadata_json: 'null'
download_count: 0
- id: 736
package_id: 7
creator_id: 1
version: 'sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08'
lower_version: 'sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08'
created_unix: 1764694509
is_internal: false
metadata_json: >-
{"type":"oci","is_tagged":false,"platform":"linux/amd64","description":"A
runner for Forgejo
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
runner for Forgejo
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"1"},"layer_creation":["ADD
alpine-minirootfs-3.22.2-x86_64.tar.gz / # buildkit","CMD
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=1 /bin/sh -c apk
add --no-cache git bash","COPY
file:abe2aa9c7d7eee672ac5953937e3ae56536980f485e40b8a70d94476ce576d39 in
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
org.opencontainers.image.authors=\"Forgejo\"
org.opencontainers.image.url=\"https://forgejo.org\"
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
org.opencontainers.image.vendor=\"Forgejo\"
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
org.opencontainers.image.title=\"Forgejo Runner\"
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
[\"/bin/forgejo-runner\"]"]}
download_count: 0
- id: 737
package_id: 7
creator_id: 1
version: 'sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12'
lower_version: 'sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12'
created_unix: 1764694509
is_internal: false
metadata_json: >-
{"type":"oci","is_tagged":false,"platform":"linux/arm64/v8","description":"A
runner for Forgejo
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
runner for Forgejo
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"1"},"layer_creation":["ADD
alpine-minirootfs-3.22.2-aarch64.tar.gz / # buildkit","CMD
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=1 /bin/sh -c apk
add --no-cache git bash","COPY
file:26caa6ee49a60c7da3a00fe966069655c32d484f1659bb0389dc18fd9616b521 in
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
org.opencontainers.image.authors=\"Forgejo\"
org.opencontainers.image.url=\"https://forgejo.org\"
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
org.opencontainers.image.vendor=\"Forgejo\"
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
org.opencontainers.image.title=\"Forgejo Runner\"
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
[\"/bin/forgejo-runner\"]"]}
download_count: 0
- id: 738
package_id: 7
creator_id: 1
version: v1.0
lower_version: v1.0
created_unix: 1764694509
is_internal: false
metadata_json: >-
{"type":"oci","is_tagged":true,"manifests":[{"platform":"linux/amd64","digest":"sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08","size":19758113},{"platform":"linux/arm64","digest":"sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12","size":19297077}]}
download_count: 0
- id: 740
package_id: 7
creator_id: 1
version: v2.0
lower_version: v2.0
created_unix: 1764695172
is_internal: false
metadata_json: >-
{"type":"oci","is_tagged":true,"platform":"linux/amd64","description":"A
runner for Forgejo
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
runner for Forgejo
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"2"},"layer_creation":["ADD
alpine-minirootfs-3.22.2-x86_64.tar.gz / # buildkit","CMD
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=2 /bin/sh -c apk
add --no-cache git bash","COPY
file:3c24d4a08c7006979c8b4397315c24dbb40df6b8fa474d8744daaf6fe17dce6f in
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
org.opencontainers.image.authors=\"Forgejo\"
org.opencontainers.image.url=\"https://forgejo.org\"
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
org.opencontainers.image.vendor=\"Forgejo\"
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
org.opencontainers.image.title=\"Forgejo Runner\"
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
[\"/bin/forgejo-runner\"]"]}
download_count: 0

View file

@ -324,6 +324,13 @@ func PrepareCleanPackageData(t testing.TB) {
var inTestEnv atomic.Bool
func PrepareTestEnv(t testing.TB, skip ...int) func() {
deferFn := PrepareTestEnvWithPackageData(t, skip...)
PrepareCleanPackageData(t)
return deferFn
}
// Doesn't perform the `PrepareCleanPackageData` that `PrepareTestEnv` does...
func PrepareTestEnvWithPackageData(t testing.TB, skip ...int) func() {
t.Helper()
if !inTestEnv.CompareAndSwap(false, true) {
@ -348,7 +355,6 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
// do not add more Prepare* functions here, only call necessary ones in the related test functions
PrepareGitRepoDirectory(t)
PrepareLFSStorage(t)
PrepareCleanPackageData(t)
return deferFn
}