Ctrl K
ComunidadGuías…

cli/list.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cli

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/google/uuid"
	"golang.org/x/xerrors"

	"github.com/coder/coder/v2/cli/cliui"
	"github.com/coder/coder/v2/codersdk"
	"github.com/coder/pretty"
	"github.com/coder/serpent"
)

// workspaceListRow is the type provided to the OutputFormatter. This is a bit
// dodgy but it's the only way to do complex display code for one format vs. the
// other.
type WorkspaceListRow struct {
	// For JSON format:
	codersdk.Workspace `table:"-"`

	// For table format:
	Favorite         bool      `json:"-" table:"favorite"`
	WorkspaceName    string    `json:"-" table:"workspace,default_sort"`
	OrganizationID   uuid.UUID `json:"-" table:"organization id"`
	OrganizationName string    `json:"-" table:"organization name"`
	Template         string    `json:"-" table:"template"`
	Status           string    `json:"-" table:"status"`
	Healthy          string    `json:"-" table:"healthy"`
	LastBuilt        string    `json:"-" table:"last built"`
	CurrentVersion   string    `json:"-" table:"current version"`
	Outdated         bool      `json:"-" table:"outdated"`
	StartsAt         string    `json:"-" table:"starts at"`
	StartsNext       string    `json:"-" table:"starts next"`
	StopsAfter       string    `json:"-" table:"stops after"`
	StopsNext        string    `json:"-" table:"stops next"`
	DailyCost        string    `json:"-" table:"daily cost"`
}

func WorkspaceListRowFromWorkspace(now time.Time, workspace codersdk.Workspace) WorkspaceListRow {
	status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition)

	lastBuilt := now.UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second)
	schedRow := scheduleListRowFromWorkspace(now, workspace)

	healthy := ""
	if status == "Starting" || status == "Started" {
		healthy = strconv.FormatBool(workspace.Health.Healthy)
	}
	favIco := " "
	if workspace.Favorite {
		favIco = "★"
	}
	workspaceName := favIco + " " + workspace.OwnerName + "/" + workspace.Name
	return WorkspaceListRow{
		Favorite:         workspace.Favorite,
		Workspace:        workspace,
		WorkspaceName:    workspaceName,
		OrganizationID:   workspace.OrganizationID,
		OrganizationName: workspace.OrganizationName,
		Template:         workspace.TemplateName,
		Status:           status,
		Healthy:          healthy,
		LastBuilt:        durationDisplay(lastBuilt),
		CurrentVersion:   workspace.LatestBuild.TemplateVersionName,
		Outdated:         workspace.Outdated,
		StartsAt:         schedRow.StartsAt,
		StartsNext:       schedRow.StartsNext,
		StopsAfter:       schedRow.StopsAfter,
		StopsNext:        schedRow.StopsNext,
		DailyCost:        strconv.Itoa(int(workspace.LatestBuild.DailyCost)),
	}
}

func (r *RootCmd) list() *serpent.Command {
	var (
		filter    cliui.WorkspaceFilter
		formatter = cliui.NewOutputFormatter(
			cliui.TableFormat(
				[]WorkspaceListRow{},
				[]string{
					"workspace",
					"template",
					"status",
					"healthy",
					"last built",
					"current version",
					"outdated",
					"starts at",
					"stops after",
				},
			),
			cliui.JSONFormat(),
		)
		sharedWithMe bool
	)
	cmd := &serpent.Command{
		Annotations: workspaceCommand,
		Use:         "list",
		Short:       "List workspaces",
		Aliases:     []string{"ls"},
		Middleware: serpent.Chain(
			serpent.RequireNArgs(0),
		),
		Options: serpent.OptionSet{
			{
				Name:        "shared-with-me",
				Description: "Show workspaces shared with you.",
				Flag:        "shared-with-me",
				Value:       serpent.BoolOf(&sharedWithMe),
				Hidden:      true,
			},
		},
		Handler: func(inv *serpent.Invocation) error {
			client, err := r.InitClient(inv)
			if err != nil {
				return err
			}

			workspaceFilter := filter.Filter()
			if sharedWithMe {
				user, err := client.User(inv.Context(), codersdk.Me)
				if err != nil {
					return xerrors.Errorf("fetch current user: %w", err)
				}
				workspaceFilter.SharedWithUser = user.ID.String()

				// Unset the default query that conflicts with the --shared-with-me flag
				if workspaceFilter.FilterQuery == "user:me" {
					workspaceFilter.FilterQuery = ""
				}
			}

			res, err := QueryConvertWorkspaces(inv.Context(), client, workspaceFilter, WorkspaceListRowFromWorkspace)
			if err != nil {
				return err
			}

			out, err := formatter.Format(inv.Context(), res)
			if err != nil {
				return err
			}

			if out == "" {
				pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "No workspaces found! Create one:\n")
				_, _ = fmt.Fprintln(inv.Stderr)
				_, _ = fmt.Fprintln(inv.Stderr, "  "+pretty.Sprint(cliui.DefaultStyles.Code, "coder create <name>"))
				_, _ = fmt.Fprintln(inv.Stderr)
				return nil
			}

			_, err = fmt.Fprintln(inv.Stdout, out)
			return err
		},
	}
	filter.AttachOptions(&cmd.Options)
	formatter.AttachOptions(&cmd.Options)
	return cmd
}

// queryConvertWorkspaces is a helper function for converting
// codersdk.Workspaces to a different type.
// It's used by the list command to convert workspaces to
// WorkspaceListRow, and by the schedule command to
// convert workspaces to scheduleListRow.
func QueryConvertWorkspaces[T any](ctx context.Context, client *codersdk.Client, filter codersdk.WorkspaceFilter, convertF func(time.Time, codersdk.Workspace) T) ([]T, error) {
	var empty []T
	workspaces, err := client.Workspaces(ctx, filter)
	if err != nil {
		return empty, xerrors.Errorf("query workspaces: %w", err)
	}
	converted := make([]T, len(workspaces.Workspaces))
	for i, workspace := range workspaces.Workspaces {
		converted[i] = convertF(time.Now(), workspace)
	}
	return converted, nil
}