package taskdebug import ( "context" "devops/agent/script" "devops/agent/tasks" "devops/server/apps/task" "fmt" "strings" "time" ) const ( TASK_NAME = "task_debug" ) func init() { tasks.RegisterTaskRunner(TASK_NAME, &TaskDebugRunner{}) } // 实现一个 task_debug 任务 type TaskDebugRunner struct{} func (t *TaskDebugRunner) Run(ctx context.Context, spec *task.TaskSpec) (*task.Task, error) { // 直接使用单元测试的上下文, 方便取消 req := script.NewExecuteScriptRequest("task_debug.sh", []string{}) req.SetWorkDir(spec.GetWorkDir()) req.SetTimeout(spec.GetTimeout()) req.SetDebugScript(true) req.SetLogFile("stdout.txt") // Task的日志,后面会通过Websocket 实时上报给Api Server 右API Server进行日志的实时展示 req.SetLogCallback(func(s string) { fmt.Print(s) }) // 添加输入参数 for k, v := range spec.InputParams { req.SetEnv(k, v) } resp, err := script.ExecuteScript(ctx, req) if err != nil { return nil, err } // 将resp 转化为 TaskStatus taskIns := task.NewTask(spec) MapExecutionResultToTask(taskIns, resp) // 使用脚本执行 return taskIns, nil } // MapExecutionResultToTask updates a Task with execution results func MapExecutionResultToTask(task *task.Task, result *script.ExecutionResult) { if task == nil || result == nil { return } // Update TaskStatus fields from ExecutionResult task.TaskStatus = convertExecutionResultToStatus(result) // Optionally update some TaskSpec fields if needed // For example, if you want to store output params in the task spec extras if len(result.OutputParams) > 0 && task.TaskSpec.Extras == nil { task.TaskSpec.Extras = make(map[string]string) } for k, v := range result.OutputParams { task.TaskSpec.Extras["output_"+k] = v } } // convertExecutionResultToStatus converts ExecutionResult to TaskStatus func convertExecutionResultToStatus(result *script.ExecutionResult) *task.TaskStatus { status := &task.TaskStatus{ Status: mapSuccessToStatus(result.Success, result.Skipped), Message: getMessage(result), Detail: getDetail(result), StartAt: &result.StartTime, EndAt: getEndTime(result), UpdateAt: time.Now(), Output: result.OutputParams, Extras: make(map[string]string), } // Add file contents to extras if present if len(result.FileContents) > 0 { for k, v := range result.FileContents { status.Extras["file_"+k] = v } } // Add command execution details if result.Command != "" { status.Extras["executed_command"] = result.Command } if result.ExitCode != 0 { status.Extras["exit_code"] = fmt.Sprintf("%d", result.ExitCode) } if result.Duration > 0 { status.Extras["duration"] = result.Duration.String() } return status } // mapSuccessToStatus maps execution result success/skipped to task status func mapSuccessToStatus(success bool, skipped bool) task.STATUS { if skipped { return task.STATUS_SKIP } if success { return task.STATUS_SUCCESS } return task.STATUS_FAILED } // getMessage combines error and message from execution result func getMessage(result *script.ExecutionResult) string { if result.Error != "" { return result.Error } return result.Message } // getDetail creates a detailed message from execution result func getDetail(result *script.ExecutionResult) string { var details []string if result.Command != "" { details = append(details, fmt.Sprintf("Command: %s", result.Command)) } if result.ExitCode != 0 { details = append(details, fmt.Sprintf("Exit Code: %d", result.ExitCode)) } if result.Duration > 0 { details = append(details, fmt.Sprintf("Duration: %v", result.Duration)) } if result.Error != "" && result.Error != result.Message { details = append(details, fmt.Sprintf("Error: %s", result.Error)) } if result.Message != "" && result.Message != result.Error { details = append(details, fmt.Sprintf("Message: %s", result.Message)) } return strings.Join(details, "\n") } // getEndTime returns the end time or current time if nil func getEndTime(result *script.ExecutionResult) time.Time { if result.EndTime != nil { return *result.EndTime } return time.Now() }