diff --git a/.gitea/workflows/release-tag.yml b/.gitea/workflows/release-tag.yml index f402272..e857b79 100644 --- a/.gitea/workflows/release-tag.yml +++ b/.gitea/workflows/release-tag.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: linux-latest strategy: matrix: include: @@ -26,7 +26,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v4 with: go-version-file: 'go.mod' cache: false @@ -52,7 +52,7 @@ jobs: release: needs: build - runs-on: ubuntu-latest + runs-on: linux-latest steps: - uses: actions/checkout@v4 diff --git a/act_runner-darwin-amd64 b/act_runner-darwin-amd64 new file mode 100755 index 0000000..e744903 Binary files /dev/null and b/act_runner-darwin-amd64 differ diff --git a/act_runner-windows-amd64.exe b/act_runner-windows-amd64.exe new file mode 100755 index 0000000..2bffbc0 Binary files /dev/null and b/act_runner-windows-amd64.exe differ diff --git a/act_runner_test b/act_runner_test new file mode 100755 index 0000000..45cc736 Binary files /dev/null and b/act_runner_test differ diff --git a/internal/app/cmd/daemon.go b/internal/app/cmd/daemon.go index eda2e40..2423e7a 100644 --- a/internal/app/cmd/daemon.go +++ b/internal/app/cmd/daemon.go @@ -187,6 +187,19 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu // Start periodic capabilities update goroutine go periodicCapabilitiesUpdate(ctx, runner, ls.Names(), dockerHost, cfg.Container.WorkdirParent) + // Start periodic stale job cache cleanup (every hour, remove caches older than 2 hours) + go func() { + ticker := time.NewTicker(1 * time.Hour) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + runner.CleanStaleJobCaches(2 * time.Hour) + } + } + }() poller := poll.New(cfg, cli, runner) poller.SetBandwidthManager(bandwidthManager) diff --git a/internal/app/run/runner.go b/internal/app/run/runner.go index 295dfe6..36b3fd8 100644 --- a/internal/app/run/runner.go +++ b/internal/app/run/runner.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "os" "path/filepath" "strings" "sync" @@ -41,6 +42,49 @@ type Runner struct { runningTasks sync.Map } + +// getJobCacheDir returns a job-isolated cache directory +func (r *Runner) getJobCacheDir(taskID int64) string { + return filepath.Join(r.cfg.Host.WorkdirParent, "jobs", fmt.Sprintf("%d", taskID)) +} + +// cleanupJobCache removes the job-specific cache directory after completion +func (r *Runner) cleanupJobCache(taskID int64) { + jobCacheDir := r.getJobCacheDir(taskID) + if err := os.RemoveAll(jobCacheDir); err != nil { + log.Warnf("failed to cleanup job cache %s: %v", jobCacheDir, err) + } else { + log.Infof("cleaned up job cache: %s", jobCacheDir) + } +} + +// CleanStaleJobCaches removes job cache directories older than maxAge +func (r *Runner) CleanStaleJobCaches(maxAge time.Duration) { + jobsDir := filepath.Join(r.cfg.Host.WorkdirParent, "jobs") + entries, err := os.ReadDir(jobsDir) + if err != nil { + return // directory may not exist yet + } + + cutoff := time.Now().Add(-maxAge) + for _, entry := range entries { + if !entry.IsDir() { + continue + } + info, err := entry.Info() + if err != nil { + continue + } + if info.ModTime().Before(cutoff) { + jobPath := filepath.Join(jobsDir, entry.Name()) + if err := os.RemoveAll(jobPath); err != nil { + log.Warnf("failed to remove stale job cache %s: %v", jobPath, err) + } else { + log.Infof("evicted stale job cache: %s", jobPath) + } + } + } +} func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) *Runner { ls := labels.Labels{} for _, v := range reg.Labels { @@ -95,6 +139,7 @@ func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error { } r.runningTasks.Store(task.Id, struct{}{}) defer r.runningTasks.Delete(task.Id) + defer r.cleanupJobCache(task.Id) ctx, cancel := context.WithTimeout(ctx, r.cfg.Runner.Timeout) defer cancel() @@ -202,7 +247,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report. // On Windows, Workdir will be like "\\\" Workdir: filepath.FromSlash(fmt.Sprintf("/%s/%s", strings.TrimLeft(r.cfg.Container.WorkdirParent, "/"), preset.Repository)), BindWorkdir: false, - ActionCacheDir: filepath.FromSlash(r.cfg.Host.WorkdirParent), + ActionCacheDir: filepath.FromSlash(r.getJobCacheDir(task.Id)), ReuseContainers: false, ForcePull: r.cfg.Container.ForcePull,