2023-09-06 10:41:06 +03:00
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
2024-08-02 03:42:08 +03:00
"fmt"
"time"
2023-09-06 10:41:06 +03:00
2024-08-02 03:42:08 +03:00
actions_model "code.gitea.io/gitea/models/actions"
actions_module "code.gitea.io/gitea/modules/actions"
2023-09-06 10:41:06 +03:00
"code.gitea.io/gitea/modules/log"
2024-08-02 03:42:08 +03:00
"code.gitea.io/gitea/modules/setting"
2023-09-06 10:41:06 +03:00
"code.gitea.io/gitea/modules/storage"
2024-08-02 03:42:08 +03:00
"code.gitea.io/gitea/modules/timeutil"
2023-09-06 10:41:06 +03:00
)
// Cleanup removes expired actions logs, data and artifacts
2024-08-02 03:42:08 +03:00
func Cleanup ( ctx context . Context ) error {
2023-09-06 10:41:06 +03:00
// clean up expired artifacts
2024-08-02 03:42:08 +03:00
if err := CleanupArtifacts ( ctx ) ; err != nil {
return fmt . Errorf ( "cleanup artifacts: %w" , err )
}
// clean up old logs
if err := CleanupLogs ( ctx ) ; err != nil {
return fmt . Errorf ( "cleanup logs: %w" , err )
}
return nil
2023-09-06 10:41:06 +03:00
}
2024-02-18 13:33:50 +03:00
// CleanupArtifacts removes expired add need-deleted artifacts and set records expired status
2023-09-06 10:41:06 +03:00
func CleanupArtifacts ( taskCtx context . Context ) error {
2024-02-18 13:33:50 +03:00
if err := cleanExpiredArtifacts ( taskCtx ) ; err != nil {
return err
}
return cleanNeedDeleteArtifacts ( taskCtx )
}
func cleanExpiredArtifacts ( taskCtx context . Context ) error {
2024-08-02 03:42:08 +03:00
artifacts , err := actions_model . ListNeedExpiredArtifacts ( taskCtx )
2023-09-06 10:41:06 +03:00
if err != nil {
return err
}
log . Info ( "Found %d expired artifacts" , len ( artifacts ) )
for _ , artifact := range artifacts {
2024-08-02 03:42:08 +03:00
if err := actions_model . SetArtifactExpired ( taskCtx , artifact . ID ) ; err != nil {
2023-09-06 10:41:06 +03:00
log . Error ( "Cannot set artifact %d expired: %v" , artifact . ID , err )
continue
}
2024-02-18 17:25:14 +03:00
if err := storage . ActionsArtifacts . Delete ( artifact . StoragePath ) ; err != nil {
log . Error ( "Cannot delete artifact %d: %v" , artifact . ID , err )
continue
}
2023-09-06 10:41:06 +03:00
log . Info ( "Artifact %d set expired" , artifact . ID )
}
return nil
}
2024-02-18 13:33:50 +03:00
// deleteArtifactBatchSize is the batch size of deleting artifacts
const deleteArtifactBatchSize = 100
func cleanNeedDeleteArtifacts ( taskCtx context . Context ) error {
for {
2024-08-02 03:42:08 +03:00
artifacts , err := actions_model . ListPendingDeleteArtifacts ( taskCtx , deleteArtifactBatchSize )
2024-02-18 13:33:50 +03:00
if err != nil {
return err
}
log . Info ( "Found %d artifacts pending deletion" , len ( artifacts ) )
for _ , artifact := range artifacts {
2024-08-02 03:42:08 +03:00
if err := actions_model . SetArtifactDeleted ( taskCtx , artifact . ID ) ; err != nil {
2024-02-18 13:33:50 +03:00
log . Error ( "Cannot set artifact %d deleted: %v" , artifact . ID , err )
continue
}
2024-02-18 17:25:14 +03:00
if err := storage . ActionsArtifacts . Delete ( artifact . StoragePath ) ; err != nil {
log . Error ( "Cannot delete artifact %d: %v" , artifact . ID , err )
continue
}
2024-02-18 13:33:50 +03:00
log . Info ( "Artifact %d set deleted" , artifact . ID )
}
if len ( artifacts ) < deleteArtifactBatchSize {
log . Debug ( "No more artifacts pending deletion" )
break
}
}
return nil
}
2024-08-02 03:42:08 +03:00
const deleteLogBatchSize = 100
// CleanupLogs removes logs which are older than the configured retention time
func CleanupLogs ( ctx context . Context ) error {
olderThan := timeutil . TimeStampNow ( ) . AddDuration ( - time . Duration ( setting . Actions . LogRetentionDays ) * 24 * time . Hour )
count := 0
for {
tasks , err := actions_model . FindOldTasksToExpire ( ctx , olderThan , deleteLogBatchSize )
if err != nil {
return fmt . Errorf ( "find old tasks: %w" , err )
}
for _ , task := range tasks {
if err := actions_module . RemoveLogs ( ctx , task . LogInStorage , task . LogFilename ) ; err != nil {
log . Error ( "Failed to remove log %s (in storage %v) of task %v: %v" , task . LogFilename , task . LogInStorage , task . ID , err )
// do not return error here, continue to next task
continue
}
task . LogIndexes = nil // clear log indexes since it's a heavy field
task . LogExpired = true
if err := actions_model . UpdateTask ( ctx , task , "log_indexes" , "log_expired" ) ; err != nil {
log . Error ( "Failed to update task %v: %v" , task . ID , err )
// do not return error here, continue to next task
continue
}
count ++
log . Trace ( "Removed log %s of task %v" , task . LogFilename , task . ID )
}
if len ( tasks ) < deleteLogBatchSize {
break
}
}
log . Info ( "Removed %d logs" , count )
return nil
}