91 lines
1.9 KiB
Go
91 lines
1.9 KiB
Go
package backup
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// RetentionPolicy defines how many backups to keep.
|
|
type RetentionPolicy struct {
|
|
Count int // keep at most N most recent backups
|
|
Days int // also delete backups older than N days (0 = disabled)
|
|
}
|
|
|
|
// ApplyRetention removes old backups from the project's backup directory.
|
|
// Returns the number of files removed.
|
|
func ApplyRetention(backupDir, projectName string, policy RetentionPolicy) (int, error) {
|
|
dir := filepath.Join(backupDir, projectName)
|
|
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return 0, nil
|
|
}
|
|
return 0, err
|
|
}
|
|
|
|
type fileInfo struct {
|
|
name string
|
|
modTime time.Time
|
|
}
|
|
|
|
var backups []fileInfo
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !isBackupArchive(projectName, entry.Name()) {
|
|
continue
|
|
}
|
|
info, err := entry.Info()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
backups = append(backups, fileInfo{
|
|
name: entry.Name(),
|
|
modTime: info.ModTime(),
|
|
})
|
|
}
|
|
|
|
if len(backups) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
// Sort newest-first by modification time.
|
|
sort.Slice(backups, func(i, j int) bool {
|
|
return backups[i].modTime.After(backups[j].modTime)
|
|
})
|
|
|
|
removed := 0
|
|
|
|
// Enforce count-based retention.
|
|
if policy.Count > 0 && len(backups) > policy.Count {
|
|
for _, b := range backups[policy.Count:] {
|
|
path := filepath.Join(dir, b.name)
|
|
if err := os.Remove(path); err == nil {
|
|
removed++
|
|
}
|
|
}
|
|
// Trim the list for the next check.
|
|
backups = backups[:policy.Count]
|
|
}
|
|
|
|
// Enforce age-based retention.
|
|
if policy.Days > 0 {
|
|
cutoff := time.Now().AddDate(0, 0, -policy.Days)
|
|
for _, b := range backups {
|
|
if b.modTime.Before(cutoff) {
|
|
path := filepath.Join(dir, b.name)
|
|
if err := os.Remove(path); err == nil {
|
|
removed++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return removed, nil
|
|
}
|
|
|
|
func isBackupArchive(projectName, name string) bool {
|
|
return strings.HasPrefix(name, projectName+"-") && strings.HasSuffix(name, ".tar.gz")
|
|
} |