feat: add option to prune old test run directories, keeping a configurable maximum number.

This commit is contained in:
CJACK
2026-02-17 02:57:21 +08:00
parent d3ac698129
commit f8effc5e84
4 changed files with 71 additions and 1 deletions

4
.gitignore vendored
View File

@@ -68,6 +68,10 @@ pnpm-lock.yaml
static/admin/
internal/webui/assets/admin/
# Go compiled binaries
ds2api
ds2api-tests
# Environment
.env.local
.env.*.local

View File

@@ -61,7 +61,8 @@ go run ./cmd/ds2api-tests \
--port 0 \
--timeout 120 \
--retries 2 \
--no-preflight=false
--no-preflight=false \
--keep 5
```
| 参数 | 说明 | 默认值 |
@@ -73,6 +74,17 @@ go run ./cmd/ds2api-tests \
| `--timeout` | 单个请求超时秒数 | `120` |
| `--retries` | 网络/5xx 请求重试次数 | `2` |
| `--no-preflight` | 跳过 preflight 检查 | `false` |
| `--keep` | 保留最近几次测试结果(`0` = 全部保留) | `5` |
---
## 自动清理 | Auto Cleanup
每次测试运行完成后,程序会自动扫描输出目录(`--out`),按时间排序保留最近 `--keep` 次运行的结果,超出部分自动删除。
- 默认保留 **5**
- 设置 `--keep 0` 可关闭自动清理
- 被删除的旧运行目录会打印日志提示
---

View File

@@ -21,6 +21,7 @@ func main() {
flag.IntVar(&timeoutSeconds, "timeout", int(opts.Timeout.Seconds()), "Per-request timeout in seconds")
flag.IntVar(&opts.Retries, "retries", opts.Retries, "Retry count for network/5xx requests")
flag.BoolVar(&opts.NoPreflight, "no-preflight", opts.NoPreflight, "Skip preflight checks")
flag.IntVar(&opts.MaxKeepRuns, "keep", opts.MaxKeepRuns, "Max test runs to keep (0 = keep all)")
flag.Parse()
if timeoutSeconds <= 0 {

View File

@@ -31,6 +31,7 @@ type Options struct {
Timeout time.Duration
Retries int
NoPreflight bool
MaxKeepRuns int
}
type runSummary struct {
@@ -161,6 +162,7 @@ func DefaultOptions() Options {
Timeout: 120 * time.Second,
Retries: 2,
NoPreflight: false,
MaxKeepRuns: 5,
}
}
@@ -212,6 +214,11 @@ func Run(ctx context.Context, opts Options) error {
return err
}
// Prune old test runs, keeping only the most recent N.
if err := r.pruneOldRuns(); err != nil {
r.warnings = append(r.warnings, "prune old runs: "+err.Error())
}
failed := 0
for _, cs := range r.results {
if !cs.Passed {
@@ -269,6 +276,52 @@ func (r *Runner) prepareRunDir() error {
return nil
}
// pruneOldRuns removes old test run directories, keeping the most recent MaxKeepRuns.
// Run IDs use the format "20060102T150405Z", so alphabetical order == chronological order.
func (r *Runner) pruneOldRuns() error {
keep := r.opts.MaxKeepRuns
if keep <= 0 {
return nil // 0 or negative means no pruning
}
entries, err := os.ReadDir(r.opts.OutputDir)
if err != nil {
return err
}
// Collect only directories (each run is a directory).
var runDirs []string
for _, e := range entries {
if !e.IsDir() {
continue
}
runDirs = append(runDirs, e.Name())
}
sort.Strings(runDirs)
if len(runDirs) <= keep {
return nil
}
// Remove oldest runs (those at the beginning of the sorted list).
toRemove := runDirs[:len(runDirs)-keep]
var errs []string
for _, name := range toRemove {
dirPath := filepath.Join(r.opts.OutputDir, name)
if err := os.RemoveAll(dirPath); err != nil {
errs = append(errs, fmt.Sprintf("remove %s: %v", name, err))
} else {
fmt.Fprintf(os.Stdout, "pruned old test run: %s\n", name)
}
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "; "))
}
return nil
}
func (r *Runner) runPreflight(ctx context.Context) error {
steps := [][]string{
{"go", "test", "./...", "-count=1"},