github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/hud/view.go (about) 1 package hud 2 3 import ( 4 "os" 5 "sort" 6 "sync" 7 8 "github.com/tilt-dev/tilt/internal/dockercompose" 9 "github.com/tilt-dev/tilt/internal/hud/view" 10 "github.com/tilt-dev/tilt/internal/k8s" 11 "github.com/tilt-dev/tilt/internal/ospath" 12 "github.com/tilt-dev/tilt/internal/store" 13 "github.com/tilt-dev/tilt/internal/store/k8sconv" 14 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 15 "github.com/tilt-dev/tilt/pkg/model" 16 "github.com/tilt-dev/tilt/pkg/model/logstore" 17 ) 18 19 func StateToTerminalView(s store.EngineState, mu *sync.RWMutex) view.View { 20 ret := view.View{} 21 22 for _, ms := range s.TiltfileStates { 23 ret.Resources = append(ret.Resources, tiltfileResourceView(ms)) 24 } 25 26 for _, name := range s.ManifestDefinitionOrder { 27 mt, ok := s.ManifestTargets[name] 28 if !ok { 29 continue 30 } 31 32 ms := mt.State 33 if ms.DisableState == v1alpha1.DisableStateDisabled { 34 // Don't show disabled resources in the terminal UI. 35 continue 36 } 37 38 var absWatchDirs []string 39 for i, p := range mt.Manifest.LocalPaths() { 40 if i > 50 { 41 // Bail out after 50 to avoid pathological performance issues. 42 break 43 } 44 fi, err := os.Stat(p) 45 46 // Treat this as a directory when there's an error. 47 if err != nil || fi.IsDir() { 48 absWatchDirs = append(absWatchDirs, p) 49 } 50 } 51 52 var pendingBuildEdits []string 53 for _, status := range ms.BuildStatuses { 54 for f := range status.PendingFileChanges { 55 pendingBuildEdits = append(pendingBuildEdits, f) 56 } 57 } 58 59 pendingBuildEdits = ospath.FileListDisplayNames(absWatchDirs, pendingBuildEdits) 60 61 buildHistory := append([]model.BuildRecord{}, ms.BuildHistory...) 62 for i, build := range buildHistory { 63 build.Edits = ospath.FileListDisplayNames(absWatchDirs, build.Edits) 64 buildHistory[i] = build 65 } 66 67 currentBuild := ms.EarliestCurrentBuild() 68 currentBuild.Edits = ospath.FileListDisplayNames(absWatchDirs, currentBuild.Edits) 69 70 // Sort the strings to make the outputs deterministic. 71 sort.Strings(pendingBuildEdits) 72 73 endpoints := store.ManifestTargetEndpoints(mt) 74 75 // NOTE(nick): Right now, the UX is designed to show the output exactly one 76 // pod. A better UI might summarize the pods in other ways (e.g., show the 77 // "most interesting" pod that's crash looping, or show logs from all pods 78 // at once). 79 _, pendingBuildSince := ms.HasPendingChanges() 80 r := view.Resource{ 81 Name: name, 82 LastDeployTime: ms.LastSuccessfulDeployTime, 83 TriggerMode: mt.Manifest.TriggerMode, 84 BuildHistory: buildHistory, 85 PendingBuildEdits: pendingBuildEdits, 86 PendingBuildSince: pendingBuildSince, 87 PendingBuildReason: mt.NextBuildReason(), 88 CurrentBuild: currentBuild, 89 Endpoints: model.LinksToURLStrings(endpoints), // hud can't handle link names, just send URLs 90 ResourceInfo: resourceInfoView(mt), 91 } 92 93 ret.Resources = append(ret.Resources, r) 94 } 95 96 ret.LogReader = logstore.NewReader(mu, s.LogStore) 97 ret.FatalError = s.FatalError 98 99 return ret 100 } 101 102 const MainTiltfileManifestName = model.MainTiltfileManifestName 103 104 func tiltfileResourceView(ms *store.ManifestState) view.Resource { 105 currentBuild := ms.EarliestCurrentBuild() 106 tr := view.Resource{ 107 Name: MainTiltfileManifestName, 108 IsTiltfile: true, 109 CurrentBuild: currentBuild, 110 BuildHistory: ms.BuildHistory, 111 ResourceInfo: view.TiltfileResourceInfo{}, 112 } 113 if !currentBuild.Empty() { 114 tr.PendingBuildSince = currentBuild.StartTime 115 } else { 116 tr.LastDeployTime = ms.LastBuild().FinishTime 117 } 118 return tr 119 } 120 121 func resourceInfoView(mt *store.ManifestTarget) view.ResourceInfoView { 122 runStatus := mt.RuntimeStatus() 123 switch state := mt.State.RuntimeState.(type) { 124 case dockercompose.State: 125 return view.NewDCResourceInfo( 126 state.ContainerState.Status, state.ContainerID, state.SpanID, state.ContainerState.StartedAt.Time, runStatus) 127 case store.K8sRuntimeState: 128 if mt.Manifest.PodReadinessMode() == model.PodReadinessIgnore { 129 return view.YAMLResourceInfo{ 130 K8sDisplayNames: state.EntityDisplayNames(), 131 } 132 } 133 pod := state.MostRecentPod() 134 podID := k8s.PodID(pod.Name) 135 return view.K8sResourceInfo{ 136 PodName: pod.Name, 137 PodCreationTime: pod.CreatedAt.Time, 138 PodUpdateStartTime: state.UpdateStartTime[podID], 139 PodStatus: pod.Status, 140 PodRestarts: int(state.VisiblePodContainerRestarts(podID)), 141 SpanID: k8sconv.SpanIDForPod(mt.Manifest.Name, podID), 142 RunStatus: runStatus, 143 DisplayNames: state.EntityDisplayNames(), 144 } 145 case store.LocalRuntimeState: 146 return view.NewLocalResourceInfo(runStatus, state.PID, state.SpanID) 147 default: 148 // This is silly but it was the old behavior. 149 return view.K8sResourceInfo{} 150 } 151 }