Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update data sent to vercel for runs #4495

Merged
merged 10 commits into from
Apr 11, 2023
6 changes: 5 additions & 1 deletion cli/internal/runsummary/execution_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ type TaskExecutionSummary struct {
exitCode *int // pointer so we can distinguish between 0 and unknown.
}

func (ts *TaskExecutionSummary) endTime() time.Time {
return ts.startAt.Add(ts.Duration)
}

// MarshalJSON munges the TaskExecutionSummary into a format we want
// We'll use an anonmyous, private struct for this, so it's not confusingly duplicated
func (ts *TaskExecutionSummary) MarshalJSON() ([]byte, error) {
Expand All @@ -82,7 +86,7 @@ func (ts *TaskExecutionSummary) MarshalJSON() ([]byte, error) {
ExitCode *int `json:"exitCode"`
}{
Start: ts.startAt.UnixMilli(),
End: ts.startAt.Add(ts.Duration).UnixMilli(),
End: ts.endTime().UnixMilli(),
Err: ts.err,
ExitCode: ts.exitCode,
}
Expand Down
36 changes: 30 additions & 6 deletions cli/internal/runsummary/run_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
"sync"
"time"

Expand All @@ -26,6 +27,7 @@ const MissingFrameworkLabel = "<NO FRAMEWORK DETECTED>"

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
Expand Down Expand Up @@ -144,8 +146,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))
}
}
}
}
Expand Down Expand Up @@ -176,6 +181,16 @@ func (summary *RunSummary) TrackTask(taskID string) (func(outcome executionEvent
return summary.ExecutionSummary.run(taskID)
}

// command returns a best guess command for the entire Run.
// TODO: we should thread this through from the entry point rather than make it up
func (summary *RunSummary) command() string {
taskNames := make(util.Set, len(summary.Tasks))
for _, task := range summary.Tasks {
taskNames.Add(task.Task)
}
return fmt.Sprintf("turbo run %s", strings.Join(taskNames.UnsafeListOfStrings(), " "))
mehulkar marked this conversation as resolved.
Show resolved Hide resolved
}

// Save saves the run summary to a file
func (rsm *Meta) save() error {
json, err := rsm.FormatJSON()
Expand Down Expand Up @@ -222,7 +237,8 @@ func (rsm *Meta) record() []error {
rsm.postTaskSummaries(runID)

if donePayload, err := json.Marshal(newVercelDonePayload(rsm.RunSummary)); err == nil {
if _, err := rsm.apiClient.JSONPatch(runsURL, donePayload); err != nil {
patchURL := fmt.Sprintf(runsPatchEndpoint, rsm.spaceID, runID)
if _, err := rsm.apiClient.JSONPatch(patchURL, donePayload); err != nil {
errs = append(errs, err)
}
}
Expand All @@ -235,7 +251,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
Expand All @@ -256,9 +273,10 @@ 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))
}
}
}
Expand All @@ -270,4 +288,10 @@ func (rsm *Meta) postTaskSummaries(runID string) {
}
close(queue)
wg.Wait()

if len(errs) > 0 {
return errs
}

return nil
}
68 changes: 65 additions & 3 deletions cli/internal/runsummary/vercel.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package runsummary

import (
"github.com/vercel/turbo/cli/internal/ci"
)

type vercelRunResponse struct {
ID string
}
Expand All @@ -22,20 +26,49 @@ 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 is the host on which this Run was executed (e.g. Vercel)
Context string `json:"context,omitempty"`

// 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 {
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())
var context = "LOCAL"
if name := ci.Constant(); name != "" {
context = name
}
return &vercelRunPayload{
StartTime: startTime,
Status: "started",
Status: "running",
Command: runsummary.command(),
Type: "TURBO",
Context: context,
}
}

Expand All @@ -47,3 +80,32 @@ func newVercelDonePayload(runsummary *RunSummary) *vercelRunPayload {
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"
}
mehulkar marked this conversation as resolved.
Show resolved Hide resolved

startTime := int(taskSummary.Execution.startAt.UnixMilli())
endTime := int(taskSummary.Execution.endTime().UnixMilli())

return &vercelTask{
Key: taskSummary.TaskID,
Name: taskSummary.Task,
Workspace: taskSummary.Package,
Hash: taskSummary.Hash,
StartTime: startTime,
EndTime: endTime,
Cache: vercelCacheStatus{
Status: status,
Source: source,
},
ExitCode: *taskSummary.Execution.exitCode,
Copy link
Contributor

@gaspar09 gaspar09 Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm finding some exitCodes for tasks are not being set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they won't be set for failed tasks. that will be covered in #4524.

Dependencies: taskSummary.Dependencies,
Dependents: taskSummary.Dependents,
}
}