From feda5d0dc5f726ad08f041691ade32ecd1ea2e2f Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 6 Apr 2023 17:03:15 -0700 Subject: [PATCH] Update data sent to vercel for runs - Add context and command when starting a Run - Update data sent for Tasks - Fix PATCH endpoint to mark Run as done - Improve error propagation --- cli/internal/runsummary/run_summary.go | 35 +++++++--- cli/internal/runsummary/vercel.go | 89 ++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 16 deletions(-) diff --git a/cli/internal/runsummary/run_summary.go b/cli/internal/runsummary/run_summary.go index eed6e2c9ad672..f9167297f79f0 100644 --- a/cli/internal/runsummary/run_summary.go +++ b/cli/internal/runsummary/run_summary.go @@ -26,6 +26,7 @@ const MissingFrameworkLabel = "" const runSummarySchemaVersion = "0" const runsEndpoint = "/v0/spaces/%s/runs" +const runsPatchEndpoint = "/v0/spaces/%s/runs/%s" const tasksEndpoint = "/v0/spaces/%s/runs/%s/tasks" type runType int @@ -144,8 +145,11 @@ func (rsm *Meta) Close(exitCode int, workspaceInfos workspace.Catalog) error { if rsm.shouldSave { if rsm.spaceID != "" && rsm.apiClient.IsLinked() { - if err := rsm.record(); err != nil { - rsm.ui.Warn(fmt.Sprintf("Error recording Run to Vercel: %v", err)) + if errs := rsm.record(); len(errs) > 0 { + rsm.ui.Warn("Errors recording run to Vercel") + for _, err := range errs { + rsm.ui.Warn(fmt.Sprintf("%v", err)) + } } } } @@ -207,23 +211,25 @@ func (rsm *Meta) record() []error { payload := newVercelRunCreatePayload(rsm.RunSummary) if startPayload, err := json.Marshal(payload); err == nil { if resp, err := rsm.apiClient.JSONPost(runsURL, startPayload); err != nil { - errs = append(errs, err) + errs = append(errs, fmt.Errorf("Failed to POST to /run: %v", err)) } else { vercelRunResponse := &vercelRunResponse{} if err := json.Unmarshal(resp, vercelRunResponse); err != nil { - errs = append(errs, err) + errs = append(errs, fmt.Errorf("Failed to unmarshal response: %v", err)) } else { + runID = vercelRunResponse.ID + } } } if runID != "" { rsm.postTaskSummaries(runID) - if donePayload, err := json.Marshal(newVercelDonePayload(rsm.RunSummary)); err == nil { - if _, err := rsm.apiClient.JSONPatch(runsURL, donePayload); err != nil { - errs = append(errs, err) + patchURL := fmt.Sprintf(runsPatchEndpoint, rsm.spaceID, runID) + if _, err := rsm.apiClient.JSONPatch(patchURL, donePayload); err != nil { + errs = append(errs, fmt.Errorf("Failed to post PATCH: %s", err)) } } } @@ -235,7 +241,8 @@ func (rsm *Meta) record() []error { return nil } -func (rsm *Meta) postTaskSummaries(runID string) { +func (rsm *Meta) postTaskSummaries(runID string) []error { + errs := []error{} // We make at most 8 requests at a time. maxParallelRequests := 8 taskSummaries := rsm.RunSummary.Tasks @@ -256,9 +263,11 @@ func (rsm *Meta) postTaskSummaries(runID string) { defer wg.Done() for index := range queue { task := taskSummaries[index] - if taskPayload, err := json.Marshal(task); err == nil { + payload := newVercelTaskPayload(task) + if taskPayload, err := json.Marshal(payload); err == nil { + if _, err := rsm.apiClient.JSONPost(taskURL, taskPayload); err != nil { - rsm.ui.Warn(fmt.Sprintf("Eror uploading summary of %s", task.TaskID)) + errs = append(errs, fmt.Errorf("Eror uploading summary of %s", task.TaskID)) } } } @@ -270,4 +279,10 @@ func (rsm *Meta) postTaskSummaries(runID string) { } close(queue) wg.Wait() + + if len(errs) > 0 { + return errs + } + + return nil } diff --git a/cli/internal/runsummary/vercel.go b/cli/internal/runsummary/vercel.go index d0533600e7d40..77bc39ad0a0b7 100644 --- a/cli/internal/runsummary/vercel.go +++ b/cli/internal/runsummary/vercel.go @@ -1,5 +1,13 @@ package runsummary +import ( + "fmt" + "strings" + + "github.com/vercel/turbo/cli/internal/ci" + "github.com/vercel/turbo/cli/internal/util" +) + type vercelRunResponse struct { ID string } @@ -22,28 +30,97 @@ type vercelRunPayload struct { // ExitCode is the exit code for the full run ExitCode int `json:"exitCode,omitempty"` + + // The command that kicked off the turbo run + Command string `json:"command,omitempty"` + + Context string `json:"context,omitempy"` + // TODO: we need to add these in // originationUser string // gitBranch string // gitSha string - // context string // command string } +type vercelCacheStatus struct { + Status string `json:"status,omitempty"` + Source string `json:"source,omitempty"` +} + +type vercelTask struct { + // id string + // log string + // TODO: add in command + + Key string `json:"key,omitempty"` + Name string `json:"name,omitempty"` + Workspace string `json:"workspace,omitempty"` + Hash string `json:"hash,omitempty"` + StartTime int `json:"startTime,omitempty"` + EndTime int `json:"endTime,omitempty"` + Cache vercelCacheStatus `json:"cache,omitempty"` + ExitCode int `json:"exitCode,omitempty"` + Dependencies []string `json:"dependencies,omitempty"` + Dependents []string `json:"dependents,omitempty"` +} + func newVercelRunCreatePayload(runsummary *RunSummary) *vercelRunPayload { - startTime := int(runsummary.ExecutionSummary.startedAt.UnixMilli()) + startTime := runsummary.ExecutionSummary.startedAt.UnixMilli() + taskNames := make(util.Set, len(runsummary.Tasks)) + for _, task := range runsummary.Tasks { + taskNames.Add(task.Task) + } return &vercelRunPayload{ - StartTime: startTime, - Status: "started", + StartTime: int(startTime), + Status: "running", + Command: fmt.Sprintf("turbo run %s", strings.Join(taskNames.UnsafeListOfStrings(), " ")), Type: "TURBO", + Context: getContext(), + } +} + +func getContext() string { + name := ci.Constant() + if name == "" { + return "LOCAL" } + + return name + } func newVercelDonePayload(runsummary *RunSummary) *vercelRunPayload { - endTime := int(runsummary.ExecutionSummary.endedAt.UnixMilli()) + endTime := runsummary.ExecutionSummary.endedAt.UnixMilli() return &vercelRunPayload{ Status: "completed", - EndTime: endTime, + EndTime: int(endTime), ExitCode: runsummary.ExecutionSummary.exitCode, } } + +func newVercelTaskPayload(taskSummary *TaskSummary) *vercelTask { + hit := taskSummary.CacheState.Local || taskSummary.CacheState.Remote + status := "MISS" + var source string + if hit { + source = "REMOTE" + status = "HIT" + } + + return &vercelTask{ + Key: taskSummary.TaskID, + Name: taskSummary.Task, + Workspace: taskSummary.Package, + Hash: taskSummary.Hash, + StartTime: int(taskSummary.Execution.startAt.UnixMilli()), + EndTime: int(taskSummary.Execution.startAt.Add(taskSummary.Execution.Duration).UnixMilli()), + Cache: vercelCacheStatus{ + Status: status, + Source: source, + }, + ExitCode: *taskSummary.Execution.exitCode, + Dependencies: taskSummary.Dependencies, + Dependents: taskSummary.Dependents, + } +}