Skip to content

Commit

Permalink
Implement --quiet flag (#690)
Browse files Browse the repository at this point in the history
* add quiet flag

* fix linter concern

* prevent update message in quiet mode

* check for mutually exclusive flags

* update quiet flag description

* check for quiet mode in Run()

* wrap code in auto-yes

* tweaks

* fix tests
  • Loading branch information
Integralist authored Oct 26, 2022
1 parent fcd188f commit d31860f
Show file tree
Hide file tree
Showing 16 changed files with 88 additions and 44 deletions.
18 changes: 13 additions & 5 deletions pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func Run(opts RunOpts) error {
var md manifest.Data
md.File.SetErrLog(opts.ErrLog)
md.File.SetOutput(opts.Stdout)
md.File.Read(manifest.Filename)
_ = md.File.Read(manifest.Filename)

// The globals will hold generally-applicable configuration parameters
// from a variety of sources, and is provided to each concrete command.
Expand Down Expand Up @@ -102,6 +102,7 @@ func Run(opts RunOpts) error {
app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&globals.Flag.Endpoint)
app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&globals.Flag.NonInteractive)
app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&globals.Flag.Profile)
app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&globals.Flag.Quiet)
app.Flag("token", tokenHelp).Short('t').StringVar(&globals.Flag.Token)
app.Flag("verbose", "Verbose logging").Short('v').BoolVar(&globals.Flag.Verbose)

Expand All @@ -123,6 +124,10 @@ func Run(opts RunOpts) error {
return nil
}

if globals.Flag.Quiet {
md.File.SetQuiet(true)
}

token, source := globals.Token()

if globals.Verbose() {
Expand All @@ -146,10 +151,12 @@ func Run(opts RunOpts) error {
if source == config.SourceFile && (len(segs) > 0 && segs[0] != "profile") {
if fi, err := os.Stat(config.FilePath); err == nil {
if mode := fi.Mode().Perm(); mode > config.FilePermissions {
text.Warning(opts.Stdout, "Unprotected configuration file.")
fmt.Fprintf(opts.Stdout, "Permissions for '%s' are too open\n", config.FilePath)
fmt.Fprintf(opts.Stdout, "It is recommended that your configuration file is NOT accessible by others.\n")
fmt.Fprintln(opts.Stdout)
if !globals.Flag.Quiet {
text.Warning(opts.Stdout, "Unprotected configuration file.")
fmt.Fprintf(opts.Stdout, "Permissions for '%s' are too open\n", config.FilePath)
fmt.Fprintf(opts.Stdout, "It is recommended that your configuration file is NOT accessible by others.\n")
fmt.Fprintln(opts.Stdout)
}
}
}
}
Expand Down Expand Up @@ -185,6 +192,7 @@ func Run(opts RunOpts) error {
ctx,
revision.AppVersion,
opts.Versioners.CLI,
globals.Flag.Quiet,
)
defer f(opts.Stdout) // ...and the printing function second, so we hit the timeout
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/app/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -149,6 +150,7 @@ var globalFlags = map[string]bool{
"help": true,
"non-interactive": true,
"profile": true,
"quiet": true,
"token": true,
"verbose": true,
}
Expand Down Expand Up @@ -246,6 +248,13 @@ func processCommandInput(
// But it's useful to have it implemented so it's ready to roll when we do.
var vars map[string]any

if cmd.IsVerboseAndQuiet(opts.Args) {
return command, cmdName, fsterr.RemediationError{
Inner: errors.New("--verbose and --quiet flag provided"),
Remediation: "Either remove both --verbose and --quiet flags, or one of them.",
}
}

if cmd.IsHelpFlagOnly(opts.Args) && len(opts.Args) == 1 {
return command, cmdName, fsterr.SkipExitError{
Skip: true,
Expand Down
12 changes: 12 additions & 0 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ func IsHelpFlagOnly(args []string) bool {
return len(args) > 0 && args[0] == "--help"
}

// IsVerboseAndQuiet indicates if the user called `fastly --verbose --quiet`.
// These flags are mutually exclusive.
func IsVerboseAndQuiet(args []string) bool {
matches := []bool{}
for _, a := range args {
if a == "--verbose" || a == "-v" || a == "--quiet" || a == "-q" {
matches = append(matches, true)
}
}
return len(matches) > 1
}

// IsGlobalFlagsOnly indicates if the user called the binary with any
// permutation order of the globally defined flags.
//
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/authtoken/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error {
)

if err = c.customerID.Parse(); err == nil {
if !c.customerID.WasSet {
if !c.customerID.WasSet && !c.Globals.Flag.Quiet {
text.Info(out, "Listing customer tokens for the FASTLY_CUSTOMER_ID environment variable")
text.Break(out)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/commands/compute/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
}

mf := c.manifest.File
if c.Globals.Flag.Quiet {
mf.SetQuiet(true)
}
if c.dir == "" && !mf.Exists() {
fmt.Fprintf(progress, "--directory not specified, using current directory\n\n")
c.dir = wd
Expand Down
2 changes: 2 additions & 0 deletions pkg/commands/ip/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error {
return err
}

// TODO: Implement --json support.

text.Break(out)
fmt.Fprintf(out, "%s\n", text.Bold("IPv4"))
for _, ip := range ipv4 {
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/objectstore/getkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func NewGetKeyCommand(parent cmd.Registerer, globals *config.Data, data manifest
c.manifest = data
c.CmdClause = parent.Command("get", "Get Fastly edge config store key")
c.CmdClause.Flag("id", "ID of object store").Required().StringVar(&c.Input.ID)
// FIXME: This should be `--key` with a short `-k` flag.
c.CmdClause.Flag("k", "Key to fetch").Required().StringVar(&c.Input.Key)
return &c
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/commands/objectstore/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ func NewInsertKeyCommand(parent cmd.Registerer, globals *config.Data, data manif
c.manifest = data
c.CmdClause = parent.Command("insert", "Insert key/value pair into a Fastly edge config store")
c.CmdClause.Flag("id", "Name of Object Store").Short('n').Required().StringVar(&c.Input.ID)
// FIXME: This should be `--key` with a short `-k` flag.
c.CmdClause.Flag("k", "Key to insert").Required().StringVar(&c.Input.Key)
// FIXME: This should be `--value`.
c.CmdClause.Flag("v", "Value to insert").Required().StringVar(&c.Input.Value)

return &c
Expand Down
9 changes: 0 additions & 9 deletions pkg/commands/service/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,6 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error {

c.input.ServiceID = serviceID

// TODO(integralist):
// Validation such as this should become redundant once Go-Fastly is
// consistently implementing validation (which itself should be redundant
// once each backend API is 100% confirmed as validating client inputs).
//
// As it stands we have multiple clients duplicating logic which should exist
// (and thus be relied upon) at the API layer.
//
// If neither arguments are provided, error with useful message.
if !c.name.WasSet && !c.comment.WasSet {
return fmt.Errorf("error parsing arguments: must provide either --name or --comment to update service")
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/commands/update/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func CheckAsync(
ctx context.Context,
currentVersion string,
cliVersioner Versioner,
quietMode bool,
) (printResults func(io.Writer)) {
results := make(chan checkResult, 1)
go func() {
Expand All @@ -63,7 +64,7 @@ func CheckAsync(

return func(w io.Writer) {
result := <-results
if result.shouldUpdate {
if result.shouldUpdate && !quietMode {
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "A new version of the Fastly CLI is available.\n")
fmt.Fprintf(w, "Current version: %s\n", result.current)
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/update/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func TestCheckAsync(t *testing.T) {
ctx,
testcase.currentVersion,
testcase.cliVersioner,
false,
)
f(&buf)

Expand Down
27 changes: 15 additions & 12 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,19 +355,21 @@ func (f *File) Read(

text.Break(out)

replacement := "Replace it with a valid version? (any existing email/token data will be lost) [y/N] "
label := fmt.Sprintf("Your configuration file (%s) is invalid. %s", path, replacement)
cont, err := text.AskYesNo(out, label, in)
if err != nil {
return fmt.Errorf("error reading input: %w", err)
}
if !cont {
err := fsterr.RemediationError{
Inner: fmt.Errorf("%v: %v", ErrInvalidConfig, unmarshalErr),
Remediation: RemediationManualFix,
if !f.autoYes {
replacement := "Replace it with a valid version? (any existing email/token data will be lost) [y/N] "
label := fmt.Sprintf("Your configuration file (%s) is invalid. %s", path, replacement)
cont, err := text.AskYesNo(out, label, in)
if err != nil {
return fmt.Errorf("error reading input: %w", err)
}
if !cont {
err := fsterr.RemediationError{
Inner: fmt.Errorf("%v: %v", ErrInvalidConfig, unmarshalErr),
Remediation: RemediationManualFix,
}
errLog.Add(err)
return err
}
errLog.Add(err)
return err
}
f = &staticConfig
}
Expand Down Expand Up @@ -527,6 +529,7 @@ type Flag struct {
Endpoint string
NonInteractive bool
Profile string
Quiet bool
Token string
Verbose bool
}
Expand Down
16 changes: 12 additions & 4 deletions pkg/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,18 @@ type File struct {
ServiceID string `toml:"service_id"`
Setup Setup `toml:"setup,omitempty"`

quiet bool
errLog fsterr.LogInterface
exists bool
output io.Writer
readError error
}

// SetQuiet sets the associated flag value.
func (f *File) SetQuiet(v bool) {
f.quiet = v
}

// Scripts represents build configuration.
type Scripts struct {
Build string `toml:"build,omitempty"`
Expand Down Expand Up @@ -467,10 +473,12 @@ func (f *File) Read(path string) (err error) {
if f.ManifestVersion == 0 {
f.ManifestVersion = ManifestLatestVersion

text.Warning(f.output, fmt.Sprintf("The fastly.toml was missing a `manifest_version` field. A default schema version of `%d` will be used.", ManifestLatestVersion))
text.Break(f.output)
text.Output(f.output, fmt.Sprintf("Refer to the fastly.toml package manifest format: %s", SpecURL))
text.Break(f.output)
if !f.quiet {
text.Warning(f.output, fmt.Sprintf("The fastly.toml was missing a `manifest_version` field. A default schema version of `%d` will be used.", ManifestLatestVersion))
text.Break(f.output)
text.Output(f.output, fmt.Sprintf("Refer to the fastly.toml package manifest format: %s", SpecURL))
text.Break(f.output)
}
err = f.Write(path)
if err != nil {
f.errLog.Add(err)
Expand Down
19 changes: 11 additions & 8 deletions pkg/profile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,18 @@ func Init(token string, data *manifest.Data, globals *config.Data, in io.Reader,
msg = fmt.Sprintf("%s%s. ", bytes.ToUpper([]byte(msg[:1])), msg[1:])

msg = fmt.Sprintf("%sThe default profile '%s' (%s) will be used.", msg, name, p.Email)
text.Warning(out, msg)

label := "\nWould you like to continue? [y/N] "
cont, err := text.AskYesNo(out, label, in)
if err != nil {
return token, err
}
if !cont {
return token, errors.New("command execution cancelled")
if !globals.Flag.AutoYes {
text.Warning(out, msg)

label := "\nWould you like to continue? [y/N] "
cont, err := text.AskYesNo(out, label, in)
if err != nil {
return token, err
}
if !cont {
return token, errors.New("command execution cancelled")
}
}

text.Break(out)
Expand Down
6 changes: 3 additions & 3 deletions pkg/text/objectstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ func PrintObjectStoreKeys(out io.Writer, prefix string, keys []string) {
}
}

// PrintObjectStoreKeyValue pretty prints a value from an object store to a given
// Consumers can provide a prefix string which
// will be used as a prefix to each line, useful for indentation.
// PrintObjectStoreKeyValue pretty prints a value from an object store to a
// given io.Writer. Consumers can provide a prefix string which will be used as
// a prefix to each line, useful for indentation.
func PrintObjectStoreKeyValue(out io.Writer, prefix string, key, value string) {
out = textio.NewPrefixWriter(out, prefix)

Expand Down
2 changes: 1 addition & 1 deletion scripts/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ function append() {
for k in ${kits[@]}; do
curl -s "https://raw.githubusercontent.com/fastly/$k/main/fastly.toml" -o "$k.toml"

append ''
append "[[starter-kits.$(parse language)]]"
append "description = \"$(parse description)\""
append "name = \"$(parse name)\""
append "path = \"https://github.com/fastly/$k\""
append ''

rm "$k.toml"
done

0 comments on commit d31860f

Please sign in to comment.