Skip to content

Commit

Permalink
Initial sketch:
Browse files Browse the repository at this point in the history
The objective was to play out the use of cobra without any inits. This
work was heavily inspired from stripe-cli.
  • Loading branch information
cyx committed Sep 1, 2020
1 parent d8b69c8 commit dc566de
Show file tree
Hide file tree
Showing 13 changed files with 1,647 additions and 0 deletions.
196 changes: 196 additions & 0 deletions internal/ansi/ansi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ansi

import (
"fmt"
"io"
"os"
"runtime"
"time"

"github.com/briandowns/spinner"
"github.com/logrusorgru/aurora"
"github.com/tidwall/pretty"
"golang.org/x/crypto/ssh/terminal"
)

var darkTerminalStyle = &pretty.Style{
Key: [2]string{"\x1B[34m", "\x1B[0m"},
String: [2]string{"\x1B[30m", "\x1B[0m"},
Number: [2]string{"\x1B[94m", "\x1B[0m"},
True: [2]string{"\x1B[35m", "\x1B[0m"},
False: [2]string{"\x1B[35m", "\x1B[0m"},
Null: [2]string{"\x1B[31m", "\x1B[0m"},
}

// ForceColors forces the use of colors and other ANSI sequences.
var ForceColors = false

// DisableColors disables all colors and other ANSI sequences.
var DisableColors = false

// EnvironmentOverrideColors overs coloring based on `CLICOLOR` and
// `CLICOLOR_FORCE`. Cf. https://bixense.com/clicolors/
var EnvironmentOverrideColors = true

// Bold returns bolded text if the writer supports colors
func Bold(text string) string {
color := Color(os.Stdout)
return color.Sprintf(color.Bold(text))
}

// Color returns an aurora.Aurora instance with colors enabled or disabled
// depending on whether the writer supports colors.
func Color(w io.Writer) aurora.Aurora {
return aurora.NewAurora(shouldUseColors(w))
}

// ColorizeJSON returns a colorized version of the input JSON, if the writer
// supports colors.
func ColorizeJSON(json string, darkStyle bool, w io.Writer) string {
if !shouldUseColors(w) {
return json
}

style := (*pretty.Style)(nil)
if darkStyle {
style = darkTerminalStyle
}

return string(pretty.Color([]byte(json), style))
}

// ColorizeStatus returns a colorized number for HTTP status code
func ColorizeStatus(status int) aurora.Value {
color := Color(os.Stdout)

switch {
case status >= 500:
return color.Red(status).Bold()
case status >= 300:
return color.Yellow(status).Bold()
default:
return color.Green(status).Bold()
}
}

// Faint returns slightly offset color text if the writer supports it
func Faint(text string) string {
color := Color(os.Stdout)
return color.Sprintf(color.Faint(text))
}

// Italic returns italicized text if the writer supports it.
func Italic(text string) string {
color := Color(os.Stdout)
return color.Sprintf(color.Italic(text))
}

// Linkify returns an ANSI escape sequence with an hyperlink, if the writer
// supports colors.
func Linkify(text, url string, w io.Writer) string {
if !shouldUseColors(w) {
return text
}

// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
// for more information about this escape sequence.
return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", url, text)
}

type charset = []string

func getCharset() charset {
// See https://github.com/briandowns/spinner#available-character-sets for
// list of available charsets
if runtime.GOOS == "windows" {
// Less fancy, but uses ASCII characters so works with Windows default
// console.
return spinner.CharSets[8]
}
return spinner.CharSets[11]
}

const duration = time.Duration(100) * time.Millisecond

// StartNewSpinner starts a new spinner with the given message. If the writer is not
// a terminal or doesn't support colors, it simply prints the message.
func StartNewSpinner(msg string, w io.Writer) *spinner.Spinner {
if !isTerminal(w) || !shouldUseColors(w) {
fmt.Fprintln(w, msg)
return nil
}

s := spinner.New(getCharset(), duration)
s.Writer = w

if msg != "" {
s.Suffix = " " + msg
}

s.Start()

return s
}

// StartSpinner updates an existing spinner's message, and starts it if it was stopped
func StartSpinner(s *spinner.Spinner, msg string, w io.Writer) {
if s == nil {
fmt.Fprintln(w, msg)
return
}
if msg != "" {
s.Suffix = " " + msg
}
if !s.Active() {
s.Start()
}
}

// StopSpinner stops a spinner with the given message. If the writer is not
// a terminal or doesn't support colors, it simply prints the message.
func StopSpinner(s *spinner.Spinner, msg string, w io.Writer) {
if !isTerminal(w) || !shouldUseColors(w) {
fmt.Fprintln(w, msg)
return
}

if msg != "" {
s.FinalMSG = "> " + msg + "\n"
}

s.Stop()
}

// StrikeThrough returns struck though text if the writer supports colors
func StrikeThrough(text string) string {
color := Color(os.Stdout)
return color.Sprintf(color.StrikeThrough(text))
}

func isTerminal(w io.Writer) bool {
switch v := w.(type) {
case *os.File:
return terminal.IsTerminal(int(v.Fd()))
default:
return false
}
}

func shouldUseColors(w io.Writer) bool {
useColors := ForceColors || isTerminal(w)

if EnvironmentOverrideColors {
force, ok := os.LookupEnv("CLICOLOR_FORCE")

switch {
case ok && force != "0":
useColors = true
case ok && force == "0":
useColors = false
case os.Getenv("CLICOLOR") == "0":
useColors = false
}
}

return useColors && !DisableColors
}
143 changes: 143 additions & 0 deletions internal/cmd/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package cmd

import (
"fmt"

"github.com/auth0/auth0-cli/internal/config"
"github.com/spf13/cobra"
)

func actionsCmd(cfg *config.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "actions",
Short: "manage resources for actions.",
}

cmd.SetUsageTemplate(resourceUsageTemplate())
cmd.AddCommand(listActionsCmd(cfg))
cmd.AddCommand(createActionCmd(cfg))
cmd.AddCommand(renameActionCmd(cfg))
cmd.AddCommand(deleteActionCmd(cfg))
cmd.AddCommand(deployActionCmd(cfg))

return cmd
}

func listActionsCmd(cfg *config.Config) *cobra.Command {
var flags struct {
trigger string
}

cmd := &cobra.Command{
Use: "list",
Short: "List existing actions",
Long: `List all actions within a tenant.`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("list called")
return nil
},
}

cmd.Flags().StringVarP(&flags.trigger,
"trigger", "t", "", "Only list actions within this trigger.",
)

return cmd
}

func createActionCmd(cfg *config.Config) *cobra.Command {
var flags struct {
trigger string
}

cmd := &cobra.Command{
Use: "create <name>",
Short: "Create an action.",
Long: `Creates an action, and generates a few files for working with actions:
- code.js - function signature.
- testdata.json - sample payload for testing the action.
`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("create called")
return nil
},
}

cmd.Flags().StringVarP(&flags.trigger,
"trigger", "t", "", "Supported trigger for the action.",
)

return cmd
}

func deployActionCmd(cfg *config.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "deploy <name>",
Short: "Deploy an action.",
Long: `Deploy an action. This creates a new version.
The deploy lifecycle is as follows:
1. Build the code artifact. Produces a new version.
2. Route production traffic at it.
3. Bind it to the associated trigger (if not already bound).
`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("deploy called")
return nil
},
}

return cmd
}

func renameActionCmd(cfg *config.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "rename <name>",
Short: "Rename an existing action.",
Long: `Renames an action. If any generated files are found those files are also renamed.:
The following generated files will be moved:
- code.js
- testdata.json
`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("rename called")
return nil
},
}

return cmd
}

func deleteActionCmd(cfg *config.Config) *cobra.Command {
var flags struct {
confirm string
}

cmd := &cobra.Command{
Use: "delete <name>",
Short: "Delete an existing action.",
Long: `Deletes an existing action. Only actions not bound to triggers can be deleted.
To delete an action already bound, you have to:
1. Remove it from the trigger.
2. Delete the action after.
Note that all code artifacts will also be deleted.
`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("delete called")
return nil
},
}

cmd.Flags().StringVarP(&flags.confirm,
"confirm", "c", "", "Confirm the action name to be deleted.",
)

return cmd
}
Loading

0 comments on commit dc566de

Please sign in to comment.