mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Allow package cleanup from admin page (#25307)
Until now expired package data gets deleted daily by a cronjob. The admin page shows the size of all packages and the size of unreferenced data. The users (#25035, #20631) expect the deletion of this data if they run the cronjob from the admin page but the job only deletes data older than 24h. This PR adds a new button which deletes all expired data.  --------- Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		| @@ -2833,6 +2833,7 @@ repos.lfs_size = LFS Size | |||||||
| packages.package_manage_panel = Package Management | packages.package_manage_panel = Package Management | ||||||
| packages.total_size = Total Size: %s | packages.total_size = Total Size: %s | ||||||
| packages.unreferenced_size = Unreferenced Size: %s | packages.unreferenced_size = Unreferenced Size: %s | ||||||
|  | packages.cleanup = Clean up expired data | ||||||
| packages.owner = Owner | packages.owner = Owner | ||||||
| packages.creator = Creator | packages.creator = Creator | ||||||
| packages.name = Name | packages.name = Name | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package admin | |||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| @@ -14,6 +15,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	packages_service "code.gitea.io/gitea/services/packages" | 	packages_service "code.gitea.io/gitea/services/packages" | ||||||
|  | 	packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -99,3 +101,13 @@ func DeletePackageVersion(ctx *context.Context) { | |||||||
| 	ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) | 	ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) | ||||||
| 	ctx.JSONRedirect(setting.AppSubURL + "/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type"))) | 	ctx.JSONRedirect(setting.AppSubURL + "/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type"))) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func CleanupExpiredData(ctx *context.Context) { | ||||||
|  | 	if err := packages_cleanup_service.CleanupExpiredData(ctx, time.Duration(0)); err != nil { | ||||||
|  | 		ctx.ServerError("CleanupExpiredData", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("packages.cleanup.success")) | ||||||
|  | 	ctx.Redirect(setting.AppSubURL + "/admin/packages") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -597,6 +597,7 @@ func registerRoutes(m *web.Route) { | |||||||
| 		m.Group("/packages", func() { | 		m.Group("/packages", func() { | ||||||
| 			m.Get("", admin.Packages) | 			m.Get("", admin.Packages) | ||||||
| 			m.Post("/delete", admin.DeletePackageVersion) | 			m.Post("/delete", admin.DeletePackageVersion) | ||||||
|  | 			m.Post("/cleanup", admin.CleanupExpiredData) | ||||||
| 		}, packagesEnabled) | 		}, packagesEnabled) | ||||||
|  |  | ||||||
| 		m.Group("/hooks", func() { | 		m.Group("/hooks", func() { | ||||||
|   | |||||||
| @@ -152,7 +152,7 @@ func registerCleanupPackages() { | |||||||
| 		OlderThan: 24 * time.Hour, | 		OlderThan: 24 * time.Hour, | ||||||
| 	}, func(ctx context.Context, _ *user_model.User, config Config) error { | 	}, func(ctx context.Context, _ *user_model.User, config Config) error { | ||||||
| 		realConfig := config.(*OlderThanConfig) | 		realConfig := config.(*OlderThanConfig) | ||||||
| 		return packages_cleanup_service.Cleanup(ctx, realConfig.OlderThan) | 		return packages_cleanup_service.CleanupTask(ctx, realConfig.OlderThan) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,9 +20,17 @@ import ( | |||||||
| 	debian_service "code.gitea.io/gitea/services/packages/debian" | 	debian_service "code.gitea.io/gitea/services/packages/debian" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Cleanup removes expired package data | // Task method to execute cleanup rules and cleanup expired package data | ||||||
| func Cleanup(taskCtx context.Context, olderThan time.Duration) error { | func CleanupTask(ctx context.Context, olderThan time.Duration) error { | ||||||
| 	ctx, committer, err := db.TxContext(taskCtx) | 	if err := ExecuteCleanupRules(ctx); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return CleanupExpiredData(ctx, olderThan) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExecuteCleanupRules(outerCtx context.Context) error { | ||||||
|  | 	ctx, committer, err := db.TxContext(outerCtx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -30,7 +38,7 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error { | |||||||
|  |  | ||||||
| 	err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error { | 	err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error { | ||||||
| 		select { | 		select { | ||||||
| 		case <-taskCtx.Done(): | 		case <-outerCtx.Done(): | ||||||
| 			return db.ErrCancelledf("While processing package cleanup rules") | 			return db.ErrCancelledf("While processing package cleanup rules") | ||||||
| 		default: | 		default: | ||||||
| 		} | 		} | ||||||
| @@ -122,6 +130,16 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	return committer.Commit() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error { | ||||||
|  | 	ctx, committer, err := db.TxContext(outerCtx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  |  | ||||||
| 	if err := container_service.Cleanup(ctx, olderThan); err != nil { | 	if err := container_service.Cleanup(ctx, olderThan); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -4,6 +4,12 @@ | |||||||
| 			{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}}, | 			{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}}, | ||||||
| 			{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}}, | 			{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}}, | ||||||
| 			{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}}) | 			{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}}) | ||||||
|  | 			<div class="ui right"> | ||||||
|  | 				<form method="post" action="/admin/packages/cleanup"> | ||||||
|  | 					{{.CsrfTokenHtml}} | ||||||
|  | 					<button class="ui primary tiny button">{{.locale.Tr "admin.packages.cleanup"}}</button> | ||||||
|  | 				</form> | ||||||
|  | 			</div> | ||||||
| 		</h4> | 		</h4> | ||||||
| 		<div class="ui attached segment"> | 		<div class="ui attached segment"> | ||||||
| 			<form class="ui form ignore-dirty"> | 			<form class="ui form ignore-dirty"> | ||||||
|   | |||||||
| @@ -475,7 +475,7 @@ func TestPackageCleanup(t *testing.T) { | |||||||
| 		_, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion) | 		_, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
|  |  | ||||||
| 		err = packages_cleanup_service.Cleanup(db.DefaultContext, duration) | 		err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
|  |  | ||||||
| 		pbs, err = packages_model.FindExpiredUnreferencedBlobs(db.DefaultContext, duration) | 		pbs, err = packages_model.FindExpiredUnreferencedBlobs(db.DefaultContext, duration) | ||||||
| @@ -610,7 +610,7 @@ func TestPackageCleanup(t *testing.T) { | |||||||
| 				pcr, err := packages_model.InsertCleanupRule(db.DefaultContext, c.Rule) | 				pcr, err := packages_model.InsertCleanupRule(db.DefaultContext, c.Rule) | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
| 				err = packages_cleanup_service.Cleanup(db.DefaultContext, duration) | 				err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration) | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
| 				for _, v := range c.Versions { | 				for _, v := range c.Versions { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user