2022-02-10 14:33:27 +00:00
package cli
import (
"net/url"
"os"
"strings"
2022-03-22 19:17:50 +00:00
"github.com/charmbracelet/lipgloss"
2022-02-10 14:33:27 +00:00
"github.com/kirsle/configdir"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
2022-03-22 19:17:50 +00:00
"github.com/coder/coder/cli/cliui"
2022-02-10 14:33:27 +00:00
"github.com/coder/coder/cli/config"
"github.com/coder/coder/codersdk"
)
2022-02-21 18:47:08 +00:00
var (
2022-03-22 19:17:50 +00:00
caret = cliui . Styles . Prompt . String ( )
2022-02-21 18:47:08 +00:00
)
2022-02-10 14:33:27 +00:00
const (
varGlobalConfig = "global-config"
2022-02-21 17:10:05 +00:00
varNoOpen = "no-open"
fix: Run expect tests on Windows with conpty pseudo-terminal (#276)
This brings together a bunch of random, partially implemented packages for support of the new(ish) Windows [`conpty`](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) API - such that we can leverage the `expect` style of CLI tests, but in a way that works in Linux/OSX `pty`s and Windows `conpty`.
These include:
- Vendoring the `go-expect` library from Netflix w/ some tweaks to work cross-platform
- Vendoring the `pty` cross-platform implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/pty)
- Vendoring the `conpty` Windows-specific implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/conpty)
- Adjusting the `pty` interface to work with `go-expect` + the cross-plat version
There were several limitations with the current packages:
- `go-expect` requires the same `os.File` (TTY) for input / output, but `conhost` requires separate file handles
- `conpty` does not handle input, only output
- The cross-platform `pty` didn't expose the full set of primitives needed for `console`
Therefore, the following changes were made:
- Handling of `stdin` was added to the `conpty` interface
- We weren't using the full extent of the `go-expect` interface, so some portions were removed (ie, exec'ing a process) to simplify our implementation and make it easier to extend cross-platform
- Instead of `console` exposing just a `Tty`, it exposes an `InTty` and `OutTty`, to help encapsulate the difference on Windows (on Linux, these point to the same pipe)
Future improvements:
- The `isatty` implementation doesn't support accurate detection of `conhost` pty's without an associated process. In lieu of a more robust check, I've added a `--force-tty` flag intended for test case use - that forces the CLI to run in tty mode.
- It seems the windows implementation doesn't support setting a deadline. This is needed for the expect.Timeout API, but isn't used by us yet.
Fixes #241
2022-02-15 01:05:40 +00:00
varForceTty = "force-tty"
2022-02-10 14:33:27 +00:00
)
func Root ( ) * cobra . Command {
cmd := & cobra . Command {
2022-03-22 19:17:50 +00:00
Use : "coder" ,
SilenceUsage : true ,
2022-02-10 14:33:27 +00:00
Long : ` ▄ █ ▀ ▀ █ ▄
▄ ▄ ▀ ▀ ▀ █ ▌ █ █ ▀ ▀ █ ▄ ▐ █
▄ ▄ █ █ ▀ ▀ █ ▄ ▄ ▄ █ █ █ █ █ ▀ ▀ █ ▐ █ ▀ ▀ █ █ ▄ █ ▀ ▀ █ █ ▀ ▀
█ ▌ ▄ ▌ ▐ █ █ ▌ ▀ █ ▄ ▄ ▄ █ ▌ █ █ ▐ █ █ █ █ █ ▀ ▀ █
█ █ █ █ █ █ ▀ ▄ █ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀
2022-03-22 19:17:50 +00:00
` + lipgloss.NewStyle().Underline(true).Render("Self-hosted developer workspaces on your infra") + `
2022-02-10 14:33:27 +00:00
` ,
2022-03-22 19:17:50 +00:00
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. ` ) + `
2022-02-10 14:33:27 +00:00
2022-03-22 19:17:50 +00:00
` + cliui.Styles.Code.Render("$ coder start --dev") + `
` + cliui.Styles.Paragraph.Render("Get started by creating a project from an example.") + `
2022-02-10 14:33:27 +00:00
2022-03-22 19:17:50 +00:00
` + cliui . Styles . Code . Render ( "$ coder projects init" ) ,
2022-02-10 14:33:27 +00:00
}
// Customizes the color of headings to make subcommands
// more visually appealing.
2022-03-22 19:17:50 +00:00
header := cliui . Styles . Placeholder
2022-02-10 14:33:27 +00:00
cmd . SetUsageTemplate ( strings . NewReplacer (
2022-03-22 19:17:50 +00:00
` 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:" ) ,
2022-02-10 14:33:27 +00:00
) . Replace ( cmd . UsageTemplate ( ) ) )
2022-03-22 19:17:50 +00:00
cmd . AddCommand (
configSSH ( ) ,
start ( ) ,
login ( ) ,
parameters ( ) ,
projects ( ) ,
users ( ) ,
workspaces ( ) ,
2022-03-29 00:19:28 +00:00
ssh ( ) ,
2022-03-22 19:17:50 +00:00
workspaceTunnel ( ) ,
)
cmd . PersistentFlags ( ) . String ( varGlobalConfig , configdir . LocalConfig ( "coderv2" ) , "Path to the global `coder` config directory" )
fix: Run expect tests on Windows with conpty pseudo-terminal (#276)
This brings together a bunch of random, partially implemented packages for support of the new(ish) Windows [`conpty`](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) API - such that we can leverage the `expect` style of CLI tests, but in a way that works in Linux/OSX `pty`s and Windows `conpty`.
These include:
- Vendoring the `go-expect` library from Netflix w/ some tweaks to work cross-platform
- Vendoring the `pty` cross-platform implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/pty)
- Vendoring the `conpty` Windows-specific implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/conpty)
- Adjusting the `pty` interface to work with `go-expect` + the cross-plat version
There were several limitations with the current packages:
- `go-expect` requires the same `os.File` (TTY) for input / output, but `conhost` requires separate file handles
- `conpty` does not handle input, only output
- The cross-platform `pty` didn't expose the full set of primitives needed for `console`
Therefore, the following changes were made:
- Handling of `stdin` was added to the `conpty` interface
- We weren't using the full extent of the `go-expect` interface, so some portions were removed (ie, exec'ing a process) to simplify our implementation and make it easier to extend cross-platform
- Instead of `console` exposing just a `Tty`, it exposes an `InTty` and `OutTty`, to help encapsulate the difference on Windows (on Linux, these point to the same pipe)
Future improvements:
- The `isatty` implementation doesn't support accurate detection of `conhost` pty's without an associated process. In lieu of a more robust check, I've added a `--force-tty` flag intended for test case use - that forces the CLI to run in tty mode.
- It seems the windows implementation doesn't support setting a deadline. This is needed for the expect.Timeout API, but isn't used by us yet.
Fixes #241
2022-02-15 01:05:40 +00:00
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 )
}
2022-02-21 17:10:05 +00:00
cmd . PersistentFlags ( ) . Bool ( varNoOpen , false , "Block automatically opening URLs in the browser." )
err = cmd . PersistentFlags ( ) . MarkHidden ( varNoOpen )
if err != nil {
panic ( err )
}
2022-02-10 14:33:27 +00:00
return cmd
}
2022-02-12 19:34:04 +00:00
// createClient returns a new client from the command context.
// The configuration directory will be read from the global flag.
2022-02-10 14:33:27 +00:00
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 )
2022-02-12 19:34:04 +00:00
client . SessionToken = token
return client , nil
2022-02-10 14:33:27 +00:00
}
2022-02-12 19:34:04 +00:00
// currentOrganization returns the currently active organization for the authenticated user.
2022-03-22 19:17:50 +00:00
func currentOrganization ( cmd * cobra . Command , client * codersdk . Client ) ( codersdk . Organization , error ) {
2022-03-07 17:40:54 +00:00
orgs , err := client . OrganizationsByUser ( cmd . Context ( ) , "me" )
2022-02-10 14:33:27 +00:00
if err != nil {
2022-03-22 19:17:50 +00:00
return codersdk . Organization { } , nil
2022-02-10 14:33:27 +00:00
}
// For now, we won't use the config to set this.
// Eventually, we will support changing using "coder switch <org>"
return orgs [ 0 ] , nil
}
2022-02-12 19:34:04 +00:00
// createConfig consumes the global configuration flag to produce a config root.
2022-02-10 14:33:27 +00:00
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.
fix: Run expect tests on Windows with conpty pseudo-terminal (#276)
This brings together a bunch of random, partially implemented packages for support of the new(ish) Windows [`conpty`](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) API - such that we can leverage the `expect` style of CLI tests, but in a way that works in Linux/OSX `pty`s and Windows `conpty`.
These include:
- Vendoring the `go-expect` library from Netflix w/ some tweaks to work cross-platform
- Vendoring the `pty` cross-platform implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/pty)
- Vendoring the `conpty` Windows-specific implementation from [waypoint-plugin-sdk](https://github.com/hashicorp/waypoint-plugin-sdk/tree/b55c787a65ff9b7d2b32cfae80681b78f8f2275e/internal/pkg/conpty)
- Adjusting the `pty` interface to work with `go-expect` + the cross-plat version
There were several limitations with the current packages:
- `go-expect` requires the same `os.File` (TTY) for input / output, but `conhost` requires separate file handles
- `conpty` does not handle input, only output
- The cross-platform `pty` didn't expose the full set of primitives needed for `console`
Therefore, the following changes were made:
- Handling of `stdin` was added to the `conpty` interface
- We weren't using the full extent of the `go-expect` interface, so some portions were removed (ie, exec'ing a process) to simplify our implementation and make it easier to extend cross-platform
- Instead of `console` exposing just a `Tty`, it exposes an `InTty` and `OutTty`, to help encapsulate the difference on Windows (on Linux, these point to the same pipe)
Future improvements:
- The `isatty` implementation doesn't support accurate detection of `conhost` pty's without an associated process. In lieu of a more robust check, I've added a `--force-tty` flag intended for test case use - that forces the CLI to run in tty mode.
- It seems the windows implementation doesn't support setting a deadline. This is needed for the expect.Timeout API, but isn't used by us yet.
Fixes #241
2022-02-15 01:05:40 +00:00
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
}
2022-03-22 19:17:50 +00:00
file , ok := cmd . InOrStdin ( ) . ( * os . File )
2022-02-10 14:33:27 +00:00
if ! ok {
return false
}
return isatty . IsTerminal ( file . Fd ( ) )
}