Repository: go-gitea/gitea
File: services/actions/cleanup.go (Go)

Code before:
			}
			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
}

const deleteEphemeralRunnerBatchSize = 100

// CleanupEphemeralRunners removes used ephemeral runners which are no longer able to process jobs
func CleanupEphemeralRunners(ctx context.Context) error {
	runners := []*actions_model.ActionRunner{}
	err := db.GetEngine(ctx).Join("INNER", "`action_task`", "`action_task`.`runner_id` = `action_runner`.`id`").Where("`action_runner`.`ephemeral` and `action_task`.`status` != ? and `action_task`.`status` != ? and `action_task`.`status` != ?)", actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked).Limit(deleteEphemeralRunnerBatchSize).Find(&runners)
	if err != nil {
		return fmt.Errorf("find runners: %w", err)
	}
	count, err := db.GetEngine(ctx).Delete(&runners)
	if err != nil {
		return fmt.Errorf("delete runners: %w", err)
	}
	log.Info("Removed %d runners", count)
	return nil
}


Review comment (suggestion):
Maybe something like 

```
`action_runner`.`ephemeral`=? and `action_task`.`status` NOT IN (?,?,?)
```

Because IIRC "bool" types need XORM to translate.

Code after:
				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, go on
			}
			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
}

// CleanupEphemeralRunners removes used ephemeral runners which are no longer able to process jobs
func CleanupEphemeralRunners(ctx context.Context) error {
	subQuery := builder.Select("`action_runner`.id").
		From(builder.Select("*").From("`action_runner`"), "`action_runner`"). // mysql needs this redundant subquery
		Join("INNER", "`action_task`", "`action_task`.`runner_id` = `action_runner`.`id`").
		Where(builder.Eq{"`action_runner`.`ephemeral`": true}).
		And(builder.NotIn("`action_task`.`status`", actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked))
	b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
	res, err := db.GetEngine(ctx).Exec(b)
	if err != nil {
		return fmt.Errorf("find runners: %w", err)
	}
	affected, _ := res.RowsAffected()
	log.Info("Removed %d runners", affected)
	return nil
}