mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
* feat: Add stage to build logs This adds a stage property to logs, and refactors the job logs cliui. It also adds tests to the cliui for build logs! * feat: Add stage to build logs This adds a stage property to logs, and refactors the job logs cliui. It also adds tests to the cliui for build logs! * feat: Add config-ssh and tests for resiliency * Rename "Echo" test to "ImmediateExit" * Fix Terraform resource agent association * Fix logs post-cancel * Fix select on Windows * Remove terraform init logs * Move timer into it's own loop * Fix race condition in provisioner jobs * Fix requested changes
145 lines
4.5 KiB
Go
145 lines
4.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/kirsle/configdir"
|
|
"github.com/mattn/go-isatty"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/coder/coder/cli/cliui"
|
|
"github.com/coder/coder/cli/config"
|
|
"github.com/coder/coder/codersdk"
|
|
)
|
|
|
|
var (
|
|
caret = cliui.Styles.Prompt.String()
|
|
)
|
|
|
|
const (
|
|
varGlobalConfig = "global-config"
|
|
varNoOpen = "no-open"
|
|
varForceTty = "force-tty"
|
|
)
|
|
|
|
func Root() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "coder",
|
|
SilenceUsage: true,
|
|
Long: ` ▄█▀ ▀█▄
|
|
▄▄ ▀▀▀ █▌ ██▀▀█▄ ▐█
|
|
▄▄██▀▀█▄▄▄ ██ ██ █▀▀█ ▐█▀▀██ ▄█▀▀█ █▀▀
|
|
█▌ ▄▌ ▐█ █▌ ▀█▄▄▄█▌ █ █ ▐█ ██ ██▀▀ █
|
|
██████▀▄█ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀
|
|
` + lipgloss.NewStyle().Underline(true).Render("Self-hosted developer workspaces on your infra") + `
|
|
|
|
`,
|
|
Example: cliui.Styles.Paragraph.Render(`Start Coder in "dev" mode. This dev-mode requires no further setup, and your local `+cliui.Styles.Code.Render("coder")+` CLI will be authenticated to talk to it. This makes it easy to experiment with Coder.`) + `
|
|
|
|
` + cliui.Styles.Code.Render("$ coder start --dev") + `
|
|
` + cliui.Styles.Paragraph.Render("Get started by creating a project from an example.") + `
|
|
|
|
` + cliui.Styles.Code.Render("$ coder projects init"),
|
|
}
|
|
// Customizes the color of headings to make subcommands
|
|
// more visually appealing.
|
|
header := cliui.Styles.Placeholder
|
|
cmd.SetUsageTemplate(strings.NewReplacer(
|
|
`Usage:`, header.Render("Usage:"),
|
|
`Examples:`, header.Render("Examples:"),
|
|
`Available Commands:`, header.Render("Commands:"),
|
|
`Global Flags:`, header.Render("Global Flags:"),
|
|
`Flags:`, header.Render("Flags:"),
|
|
`Additional help topics:`, header.Render("Additional help:"),
|
|
).Replace(cmd.UsageTemplate()))
|
|
|
|
cmd.AddCommand(
|
|
configSSH(),
|
|
start(),
|
|
login(),
|
|
parameters(),
|
|
projects(),
|
|
users(),
|
|
workspaces(),
|
|
ssh(),
|
|
workspaceTunnel(),
|
|
)
|
|
|
|
cmd.PersistentFlags().String(varGlobalConfig, configdir.LocalConfig("coderv2"), "Path to the global `coder` config directory")
|
|
cmd.PersistentFlags().Bool(varForceTty, false, "Force the `coder` command to run as if connected to a TTY")
|
|
err := cmd.PersistentFlags().MarkHidden(varForceTty)
|
|
if err != nil {
|
|
// This should never return an error, because we just added the `--force-tty`` flag prior to calling MarkHidden.
|
|
panic(err)
|
|
}
|
|
cmd.PersistentFlags().Bool(varNoOpen, false, "Block automatically opening URLs in the browser.")
|
|
err = cmd.PersistentFlags().MarkHidden(varNoOpen)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// createClient returns a new client from the command context.
|
|
// The configuration directory will be read from the global flag.
|
|
func createClient(cmd *cobra.Command) (*codersdk.Client, error) {
|
|
root := createConfig(cmd)
|
|
rawURL, err := root.URL().Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serverURL, err := url.Parse(rawURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
token, err := root.Session().Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := codersdk.New(serverURL)
|
|
client.SessionToken = token
|
|
return client, nil
|
|
}
|
|
|
|
// currentOrganization returns the currently active organization for the authenticated user.
|
|
func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (codersdk.Organization, error) {
|
|
orgs, err := client.OrganizationsByUser(cmd.Context(), "me")
|
|
if err != nil {
|
|
return codersdk.Organization{}, nil
|
|
}
|
|
// For now, we won't use the config to set this.
|
|
// Eventually, we will support changing using "coder switch <org>"
|
|
return orgs[0], nil
|
|
}
|
|
|
|
// createConfig consumes the global configuration flag to produce a config root.
|
|
func createConfig(cmd *cobra.Command) config.Root {
|
|
globalRoot, err := cmd.Flags().GetString(varGlobalConfig)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return config.Root(globalRoot)
|
|
}
|
|
|
|
// isTTY returns whether the passed reader is a TTY or not.
|
|
// This accepts a reader to work with Cobra's "InOrStdin"
|
|
// function for simple testing.
|
|
func isTTY(cmd *cobra.Command) bool {
|
|
// If the `--force-tty` command is available, and set,
|
|
// assume we're in a tty. This is primarily for cases on Windows
|
|
// where we may not be able to reliably detect this automatically (ie, tests)
|
|
forceTty, err := cmd.Flags().GetBool(varForceTty)
|
|
if forceTty && err == nil {
|
|
return true
|
|
}
|
|
file, ok := cmd.InOrStdin().(*os.File)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return isatty.IsTerminal(file.Fd())
|
|
}
|