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  }