error on unexpected arguments, test out logger, add .milpa
courtesy of the department of departamental recursiveness
This commit is contained in:
parent
7fab6d66d8
commit
725347ec48
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
coverage/*
|
6
.milpa/commands/dev/ci.sh
Normal file
6
.milpa/commands/dev/ci.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
|
||||||
|
milpa dev lint || @milpa.fail "linter has errors"
|
||||||
|
milpa dev test unit || @milpa.fail "tests failed"
|
3
.milpa/commands/dev/ci.yaml
Normal file
3
.milpa/commands/dev/ci.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
summary: Lints and tests milpa
|
||||||
|
description: |
|
||||||
|
runs `milpa dev lint {shell,go}` and `milpa dev test {unit,integration}`
|
10
.milpa/commands/dev/lint.sh
Normal file
10
.milpa/commands/dev/lint.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
|
||||||
|
cd "$(dirname "$MILPA_COMMAND_REPO")" || @milpa.fail "could not cd into base dir"
|
||||||
|
|
||||||
|
@milpa.log info "Linting go files"
|
||||||
|
golangci-lint run || exit 2
|
||||||
|
@milpa.log complete "Go files are up to spec"
|
||||||
|
|
3
.milpa/commands/dev/lint.yaml
Normal file
3
.milpa/commands/dev/lint.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
summary: Runs linter on go files
|
||||||
|
description: |
|
||||||
|
basically golangci-lint
|
12
.milpa/commands/dev/test.sh
Normal file
12
.milpa/commands/dev/test.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
if [[ "$MILPA_OPT_COVERAGE" ]]; then
|
||||||
|
rm -rf coverage
|
||||||
|
milpa dev test unit --coverage
|
||||||
|
milpa dev test coverage-report
|
||||||
|
else
|
||||||
|
milpa dev test unit
|
||||||
|
fi
|
7
.milpa/commands/dev/test.yaml
Normal file
7
.milpa/commands/dev/test.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
summary: Runs unit and integration tests
|
||||||
|
description: |
|
||||||
|
a wrapper for milpa dev test *
|
||||||
|
options:
|
||||||
|
coverage:
|
||||||
|
type: bool
|
||||||
|
description: if provided, will output coverage reports
|
18
.milpa/commands/dev/test/coverage-report.sh
Normal file
18
.milpa/commands/dev/test/coverage-report.sh
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
|
||||||
|
runs=()
|
||||||
|
while IFS= read -r -d $'\0'; do
|
||||||
|
runs+=("$REPLY")
|
||||||
|
done < <(find coverage -type d -maxdepth 1 -mindepth 1 -print0)
|
||||||
|
packages="$(IFS=, ; echo "${runs[*]}")"
|
||||||
|
|
||||||
|
|
||||||
|
@milpa.log info "Building coverage report from runs: ${runs[*]}"
|
||||||
|
go tool covdata textfmt -i="$packages" -o coverage/coverage.cov || @milpa.fail "could not merge runs"
|
||||||
|
go tool cover -html=coverage/coverage.cov -o coverage/coverage.html || @milpa.fail "could not build reports"
|
||||||
|
|
||||||
|
@milpa.log complete "Coverage report built"
|
||||||
|
go tool covdata percent -i="$packages"
|
||||||
|
go tool cover -func=coverage/coverage.cov | tail -n 1
|
5
.milpa/commands/dev/test/coverage-report.yaml
Normal file
5
.milpa/commands/dev/test/coverage-report.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
summary: Creates a coverage report from previous test runs
|
||||||
|
description: |
|
||||||
|
looks at test/coverage/* for output
|
||||||
|
|
||||||
|
see: https://go.dev/testing/coverage/
|
17
.milpa/commands/dev/test/unit.sh
Normal file
17
.milpa/commands/dev/test/unit.sh
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
|
||||||
|
root="$(dirname "$MILPA_COMMAND_REPO")"
|
||||||
|
cd "$root" || @milpa.fail "could not cd into $root"
|
||||||
|
@milpa.log info "Running unit tests"
|
||||||
|
args=()
|
||||||
|
|
||||||
|
if [[ "${MILPA_OPT_COVERAGE}" ]]; then
|
||||||
|
cover_dir="$root/coverage/unit"
|
||||||
|
rm -rf "$cover_dir"
|
||||||
|
mkdir -p "$cover_dir"
|
||||||
|
args=( -test.gocoverdir="$cover_dir" --coverpkg=./... )
|
||||||
|
fi
|
||||||
|
gotestsum --format short -- ./... "${args[@]}" || exit 2
|
||||||
|
@milpa.log complete "Unit tests passed"
|
7
.milpa/commands/dev/test/unit.yaml
Normal file
7
.milpa/commands/dev/test/unit.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
summary: Runs unit tests
|
||||||
|
description: |
|
||||||
|
Runs unit tests using gotestsum
|
||||||
|
options:
|
||||||
|
coverage:
|
||||||
|
type: bool
|
||||||
|
description: if provided, will output coverage reports
|
46
.milpa/commands/release/create.sh
Normal file
46
.milpa/commands/release/create.sh
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
@milpa.load_util user-input
|
||||||
|
|
||||||
|
current_branch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
[[ "$current_branch" != "main" ]] && @milpa.fail "Refusing to release on branch <$current_branch>"
|
||||||
|
[[ -n "$(git status --porcelain)" ]] && @milpa.fail "Git tree is messy, won't continue"
|
||||||
|
|
||||||
|
function next_semver() {
|
||||||
|
local components
|
||||||
|
IFS="." read -r -a components <<< "${2}"
|
||||||
|
following=""
|
||||||
|
case "$1" in
|
||||||
|
major ) following="$((components[0]+1)).0.0" ;;
|
||||||
|
minor ) following="${components[0]}.$((components[1]+1)).0" ;;
|
||||||
|
patch ) following="${components[0]}.${components[1]}.$((components[2]+1))" ;;
|
||||||
|
*) @milpa.fail "unknown increment type: <$1>"
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "$following"
|
||||||
|
}
|
||||||
|
|
||||||
|
increment="$MILPA_ARG_INCREMENT"
|
||||||
|
# get the latest tag, ignoring any pre-releases
|
||||||
|
# by default current version is 0.0.-1, and must initially release a patch
|
||||||
|
current="$(git describe --abbrev=0 --exclude='*-*' --tags 2>/dev/null || echo "0.0.-1")"
|
||||||
|
|
||||||
|
next=$(next_semver "$increment" "$current")
|
||||||
|
|
||||||
|
if [[ "$MILPA_OPT_PRE" ]]; then
|
||||||
|
# pre releases might update previous ones, look for them
|
||||||
|
pre_current=$(git describe --abbrev=0 --match="$next-$MILPA_OPT_PRE.*" --tags 2>/dev/null || echo "$current-$MILPA_OPT_PRE.-1")
|
||||||
|
build=${pre_current##*.}
|
||||||
|
next="$next-$MILPA_OPT_PRE.$(( build + 1 ))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
@milpa.log info "Creating release with version $(@milpa.fmt inverted "$next")"
|
||||||
|
@milpa.confirm "Proceed with release?" || @milpa.fail "Refusing to continue, got <$REPLY>"
|
||||||
|
@milpa.log success "Continuing with release"
|
||||||
|
|
||||||
|
@milpa.log info "Creating tag and pushing"
|
||||||
|
git tag "$next" || @milpa.fail "Could not create tag $next"
|
||||||
|
git push origin "$next" || @milpa.fail "Could not push tag $next"
|
||||||
|
|
||||||
|
@milpa.log complete "Release created and pushed to origin!"
|
15
.milpa/commands/release/create.yaml
Normal file
15
.milpa/commands/release/create.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
summary: Creates a new tag and updates the changelog
|
||||||
|
description: |
|
||||||
|
Automation might trigger a release if github is in a good mood
|
||||||
|
arguments:
|
||||||
|
- name: increment
|
||||||
|
description: "The kind of semver increment"
|
||||||
|
default: patch
|
||||||
|
values:
|
||||||
|
static: [major, minor, patch]
|
||||||
|
required: true
|
||||||
|
options:
|
||||||
|
pre:
|
||||||
|
values:
|
||||||
|
static: [alpha, beta, rc]
|
||||||
|
description: create a pre-release
|
@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright © 2021 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,7 +14,8 @@ var GenerateCompletions = &cobra.Command{
|
|||||||
Hidden: true,
|
Hidden: true,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
|
ValidArgs: []string{"bash", "fish", "zsh"},
|
||||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "bash":
|
case "bash":
|
||||||
|
@ -24,6 +24,7 @@ func newCobraRoot(root *command.Command) *cobra.Command {
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
|
// This tricks cobra into erroring without a subcommand
|
||||||
ValidArgs: []string{""},
|
ValidArgs: []string{""},
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := cobra.OnlyValidArgs(cmd, args); err != nil {
|
if err := cobra.OnlyValidArgs(cmd, args); err != nil {
|
||||||
@ -73,7 +74,9 @@ func ToCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
|
|||||||
Args: func(cc *cobra.Command, supplied []string) error {
|
Args: func(cc *cobra.Command, supplied []string) error {
|
||||||
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
||||||
if !skipValidation && runtime.ValidationEnabled() {
|
if !skipValidation && runtime.ValidationEnabled() {
|
||||||
cmd.Arguments.Parse(supplied)
|
if err := cmd.Arguments.Parse(supplied); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return cmd.Arguments.AreValid()
|
return cmd.Arguments.AreValid()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -91,7 +91,9 @@ func Execute(version string) error {
|
|||||||
if idx == len(cmd.Path)-1 {
|
if idx == len(cmd.Path)-1 {
|
||||||
leaf := ToCobra(cmd, cmdRoot.Options)
|
leaf := ToCobra(cmd, cmdRoot.Options)
|
||||||
container.AddCommand(leaf)
|
container.AddCommand(leaf)
|
||||||
|
if container != ccRoot {
|
||||||
container.ValidArgs = append(container.ValidArgs, leaf.Name())
|
container.ValidArgs = append(container.ValidArgs, leaf.Name())
|
||||||
|
}
|
||||||
log.Tracef("cobra: %s => %s", leaf.Name(), container.CommandPath())
|
log.Tracef("cobra: %s => %s", leaf.Name(), container.CommandPath())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ func anySliceToStringSlice(src any) []string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args *Arguments) Parse(supplied []string) {
|
func (args *Arguments) Parse(supplied []string) error {
|
||||||
|
parsed := []string{}
|
||||||
for idx, arg := range *args {
|
for idx, arg := range *args {
|
||||||
argumentProvided := idx < len(supplied)
|
argumentProvided := idx < len(supplied)
|
||||||
|
|
||||||
@ -76,7 +77,13 @@ func (args *Arguments) Parse(supplied []string) {
|
|||||||
} else {
|
} else {
|
||||||
arg.SetValue([]string{supplied[idx]})
|
arg.SetValue([]string{supplied[idx]})
|
||||||
}
|
}
|
||||||
|
parsed = append(parsed, *arg.provided...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(parsed) != len(supplied) {
|
||||||
|
return errors.BadArguments{Msg: fmt.Sprintf("Unexpected arguments provided: %s", supplied)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args *Arguments) AreValid() error {
|
func (args *Arguments) AreValid() error {
|
||||||
@ -106,7 +113,9 @@ func (args *Arguments) CompletionFunction(cc *cobra.Command, provided []string,
|
|||||||
lastArg := (*args)[len(*args)-1]
|
lastArg := (*args)[len(*args)-1]
|
||||||
hasVariadicArg := expectedArgLen > 0 && lastArg.Variadic
|
hasVariadicArg := expectedArgLen > 0 && lastArg.Variadic
|
||||||
lastArg.Command.Options.Parse(cc.Flags())
|
lastArg.Command.Options.Parse(cc.Flags())
|
||||||
args.Parse(provided)
|
if err := args.Parse(provided); err != nil {
|
||||||
|
return []string{err.Error()}, cobra.ShellCompDirectiveDefault
|
||||||
|
}
|
||||||
|
|
||||||
directive = cobra.ShellCompDirectiveDefault
|
directive = cobra.ShellCompDirectiveDefault
|
||||||
if argsCompleted < expectedArgLen || hasVariadicArg {
|
if argsCompleted < expectedArgLen || hasVariadicArg {
|
||||||
|
@ -39,7 +39,7 @@ func testCommand() *Command {
|
|||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
cmd := testCommand()
|
cmd := testCommand()
|
||||||
cmd.Arguments.Parse([]string{"asdf", "one", "two", "three"})
|
cmd.Arguments.Parse([]string{"asdf", "one", "two", "three"}) // nolint: errcheck
|
||||||
known := cmd.Arguments.AllKnown()
|
known := cmd.Arguments.AllKnown()
|
||||||
|
|
||||||
if !cmd.Arguments[0].IsKnown() {
|
if !cmd.Arguments[0].IsKnown() {
|
||||||
@ -67,7 +67,7 @@ func TestParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd = testCommand()
|
cmd = testCommand()
|
||||||
cmd.Arguments.Parse([]string{"asdf"})
|
cmd.Arguments.Parse([]string{"asdf"}) // nolint: errcheck
|
||||||
known = cmd.Arguments.AllKnown()
|
known = cmd.Arguments.AllKnown()
|
||||||
|
|
||||||
if !cmd.Arguments[0].IsKnown() {
|
if !cmd.Arguments[0].IsKnown() {
|
||||||
@ -209,7 +209,7 @@ func TestArgumentsValidate(t *testing.T) {
|
|||||||
cmd.Arguments[1] = staticArgument("second", "", []string{"one", "two", "three"}, true)
|
cmd.Arguments[1] = staticArgument("second", "", []string{"one", "two", "three"}, true)
|
||||||
cmd.SetBindings()
|
cmd.SetBindings()
|
||||||
|
|
||||||
cmd.Arguments.Parse([]string{"first", "one", "three", "two"})
|
cmd.Arguments.Parse([]string{"first", "one", "three", "two"}) // nolint: errcheck
|
||||||
|
|
||||||
err := cmd.Arguments.AreValid()
|
err := cmd.Arguments.AreValid()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -219,7 +219,7 @@ func TestArgumentsValidate(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.Command.FullName(), func(t *testing.T) {
|
t.Run(c.Command.FullName(), func(t *testing.T) {
|
||||||
c.Command.Arguments.Parse(c.Args)
|
c.Command.Arguments.Parse(c.Args) // nolint: errcheck
|
||||||
|
|
||||||
err := c.Command.Arguments.AreValid()
|
err := c.Command.Arguments.AreValid()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -232,144 +232,6 @@ func TestArgumentsValidate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestArgumentsToEnv(t *testing.T) {
|
|
||||||
// cases := []struct {
|
|
||||||
// Command *Command
|
|
||||||
// Args []string
|
|
||||||
// Expect []string
|
|
||||||
// Env []string
|
|
||||||
// }{
|
|
||||||
// {
|
|
||||||
// Args: []string{"something"},
|
|
||||||
// Expect: []string{"export MILPA_ARG_FIRST=something"},
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "required", "present"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Required: true,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Args: []string{},
|
|
||||||
// Expect: []string{"export MILPA_ARG_FIRST=default"},
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "default", "present"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Default: "default",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Args: []string{"zero", "one", "two", "three"},
|
|
||||||
// Expect: []string{
|
|
||||||
// "export MILPA_ARG_FIRST=zero",
|
|
||||||
// "declare -a MILPA_ARG_VARIADIC='( one two three )'",
|
|
||||||
// },
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "variadic"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Default: "default",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Name: "variadic",
|
|
||||||
// Variadic: true,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Args: []string{},
|
|
||||||
// Expect: []string{"export MILPA_ARG_FIRST=default"},
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "static", "default"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Default: "default",
|
|
||||||
// Values: &ValueSource{
|
|
||||||
// Static: &[]string{
|
|
||||||
// "default",
|
|
||||||
// "good",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Args: []string{"good"},
|
|
||||||
// Expect: []string{"export MILPA_ARG_FIRST=good"},
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "static", "good"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Default: "default",
|
|
||||||
// Values: &ValueSource{
|
|
||||||
// Static: &[]string{
|
|
||||||
// "default",
|
|
||||||
// "good",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Args: []string{"good"},
|
|
||||||
// Expect: []string{"export MILPA_ARG_FIRST=good"},
|
|
||||||
// Command: &Command{
|
|
||||||
// // Name: []string{"test", "script", "good"},
|
|
||||||
// Arguments: []*Argument{
|
|
||||||
// {
|
|
||||||
// Name: "first",
|
|
||||||
// Default: "default",
|
|
||||||
// Values: &ValueSource{
|
|
||||||
// Script: "echo good; echo default",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for _, c := range cases {
|
|
||||||
// t.Run(c.Command.FullName(), func(t *testing.T) {
|
|
||||||
// dst := []string{}
|
|
||||||
// c.Command.SetBindings()
|
|
||||||
// c.Command.Arguments.Parse(c.Args)
|
|
||||||
// c.Command.Arguments.ToEnv(c.Command, &dst, "export ")
|
|
||||||
|
|
||||||
// err := c.Command.Arguments.AreValid()
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatalf("Unexpected failure validating: %s", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for _, expected := range c.Expect {
|
|
||||||
// found := false
|
|
||||||
// for _, actual := range dst {
|
|
||||||
// if strings.HasPrefix(actual, expected) {
|
|
||||||
// found = true
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !found {
|
|
||||||
// t.Fatalf("Expected line %v not found in %v", expected, dst)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
func TestArgumentToDesc(t *testing.T) {
|
func TestArgumentToDesc(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Arg *Argument
|
Arg *Argument
|
||||||
|
@ -119,7 +119,9 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *Command) ParseInput(cc *cobra.Command, args []string) error {
|
func (cmd *Command) ParseInput(cc *cobra.Command, args []string) error {
|
||||||
cmd.Arguments.Parse(args)
|
if err := cmd.Arguments.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
||||||
cmd.Options.Parse(cc.Flags())
|
cmd.Options.Parse(cc.Flags())
|
||||||
if !skipValidation {
|
if !skipValidation {
|
||||||
|
@ -165,7 +165,9 @@ func (opt *Option) CompletionFunction(cmd *cobra.Command, args []string, toCompl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opt.Command.Arguments.Parse(args)
|
if err := opt.Command.Arguments.Parse(args); err != nil {
|
||||||
|
return []string{err.Error()}, cobra.ShellCompDirectiveDefault
|
||||||
|
}
|
||||||
opt.Command.Options.Parse(cmd.Flags())
|
opt.Command.Options.Parse(cmd.Flags())
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -311,7 +311,7 @@ func (vs *ValueSource) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
|
|
||||||
var customCompleters = map[string]CompletionFunc{}
|
var customCompleters = map[string]CompletionFunc{}
|
||||||
|
|
||||||
// Registers a completion function for the given command.ValueType key name
|
// Registers a completion function for the given command.ValueType key name.
|
||||||
func RegisterValueSource(key string, completion CompletionFunc) {
|
func RegisterValueSource(key string, completion CompletionFunc) {
|
||||||
customCompleters[key] = completion
|
customCompleters[key] = completion
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ func TestResolveTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).SetBindings()
|
}).SetBindings()
|
||||||
cmd.Arguments.Parse(test.Args)
|
cmd.Arguments.Parse(test.Args) // nolint: errcheck
|
||||||
cmd.Options.Parse(test.Flags)
|
cmd.Options.Parse(test.Flags)
|
||||||
res, err := cmd.ResolveTemplate(test.Tpl, "")
|
res, err := cmd.ResolveTemplate(test.Tpl, "")
|
||||||
|
|
||||||
|
@ -1,14 +1,38 @@
|
|||||||
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var bold *color.Color
|
||||||
|
var boldRedBG *color.Color
|
||||||
|
var boldRed *color.Color
|
||||||
|
var boldYellowBG *color.Color
|
||||||
|
var boldYellow *color.Color
|
||||||
|
var dimmed *color.Color
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bold = color.New(color.Bold)
|
||||||
|
bold.EnableColor()
|
||||||
|
boldRedBG = color.New(color.Bold, color.BgRed)
|
||||||
|
boldRedBG.EnableColor()
|
||||||
|
boldRed = color.New(color.Bold, color.FgHiRed)
|
||||||
|
boldRed.EnableColor()
|
||||||
|
boldYellowBG = color.New(color.Bold, color.BgYellow, color.FgBlack)
|
||||||
|
boldYellowBG.EnableColor()
|
||||||
|
boldYellow = color.New(color.Bold, color.FgHiYellow)
|
||||||
|
boldYellow.EnableColor()
|
||||||
|
dimmed = color.New(color.Faint)
|
||||||
|
dimmed.EnableColor()
|
||||||
|
}
|
||||||
|
|
||||||
type Formatter struct {
|
type Formatter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +40,8 @@ func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|||||||
prefix := ""
|
prefix := ""
|
||||||
colorEnabled := runtime.ColorEnabled()
|
colorEnabled := runtime.ColorEnabled()
|
||||||
message := entry.Message
|
message := entry.Message
|
||||||
if runtime.VerboseEnabled() {
|
switch {
|
||||||
|
case runtime.VerboseEnabled():
|
||||||
date := strings.Replace(entry.Time.Local().Format(time.DateTime), " ", "T", 1)
|
date := strings.Replace(entry.Time.Local().Format(time.DateTime), " ", "T", 1)
|
||||||
component := ""
|
component := ""
|
||||||
if c, ok := entry.Data[componentKey]; ok {
|
if c, ok := entry.Data[componentKey]; ok {
|
||||||
@ -24,36 +49,40 @@ func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
level := entry.Level.String()
|
level := entry.Level.String()
|
||||||
if colorEnabled {
|
if colorEnabled {
|
||||||
if entry.Level <= logrus.ErrorLevel {
|
switch {
|
||||||
level = "\033[31m\033[1m" + level + "\033[0m"
|
case entry.Level <= logrus.ErrorLevel:
|
||||||
} else if entry.Level == logrus.WarnLevel {
|
level = boldRed.Sprint(level)
|
||||||
level = "\033[33m\033[1m" + level + "\033[0m"
|
case entry.Level == logrus.WarnLevel:
|
||||||
} else if entry.Level >= logrus.DebugLevel && colorEnabled {
|
level = boldYellow.Sprint(level)
|
||||||
message = "\033[2m" + message + "\033[0m"
|
case entry.Level >= logrus.DebugLevel:
|
||||||
|
level = dimmed.Sprint(level)
|
||||||
|
message = dimmed.Sprint(message)
|
||||||
|
default:
|
||||||
|
level = dimmed.Sprint(level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix = fmt.Sprintf("\033[2m%s %s%s\033[0m\t", date, level, component)
|
prefix = dimmed.Sprint(date) + " " + level + dimmed.Sprint(component) + "\t"
|
||||||
} else if entry.Level == logrus.ErrorLevel {
|
case entry.Level == logrus.ErrorLevel:
|
||||||
if colorEnabled {
|
if colorEnabled {
|
||||||
prefix = "\033[41m\033[1m ERROR \033[0m "
|
prefix = boldRedBG.Sprint(" ERROR ") + " "
|
||||||
} else {
|
} else {
|
||||||
prefix = "ERROR: "
|
prefix = "ERROR: "
|
||||||
}
|
}
|
||||||
} else if entry.Level == logrus.WarnLevel {
|
case entry.Level == logrus.WarnLevel:
|
||||||
if colorEnabled {
|
if colorEnabled {
|
||||||
prefix = "\033[43m\033[31m warning \033[0m "
|
prefix = boldYellowBG.Sprint(" WARNING ") + " "
|
||||||
message = "\033[33m" + message + "\033[0m"
|
|
||||||
} else {
|
} else {
|
||||||
prefix = "WARNING: "
|
prefix = "WARNING: "
|
||||||
}
|
}
|
||||||
} else if entry.Level >= logrus.DebugLevel {
|
case entry.Level >= logrus.DebugLevel:
|
||||||
if colorEnabled {
|
if colorEnabled {
|
||||||
prefix = "\033[2m" + entry.Level.String() + ":\033[0m "
|
prefix = dimmed.Sprintf("%s: ", strings.ToUpper(entry.Level.String()))
|
||||||
message = "\033[2m" + message + "\033[0m"
|
message = dimmed.Sprint(message)
|
||||||
} else {
|
} else {
|
||||||
prefix = strings.ToUpper(entry.Level.String()) + ": "
|
prefix = strings.ToUpper(entry.Level.String()) + ": "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []byte(prefix + message + "\n"), nil
|
return []byte(prefix + message + "\n"), nil
|
||||||
}
|
}
|
||||||
|
187
pkg/logger/log_test.go
Normal file
187
pkg/logger/log_test.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
package logger_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "git.rob.mx/nidito/chinampa/pkg/logger"
|
||||||
|
rt "git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func withEnv(t *testing.T, env map[string]string) {
|
||||||
|
prevEnv := os.Environ()
|
||||||
|
for _, entry := range prevEnv {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
os.Unsetenv(parts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
os.Setenv(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
rt.ResetParsedFlags()
|
||||||
|
|
||||||
|
for k := range env {
|
||||||
|
os.Unsetenv(k)
|
||||||
|
}
|
||||||
|
for _, entry := range prevEnv {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
os.Setenv(parts[0], parts[1])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatter(t *testing.T) {
|
||||||
|
now := strings.Replace(time.Now().Local().Format(time.DateTime), " ", "T", 1)
|
||||||
|
cases := []struct {
|
||||||
|
Color bool
|
||||||
|
Verbose bool
|
||||||
|
Call func(args ...any)
|
||||||
|
Expects string
|
||||||
|
Level logrus.Level
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Color: true,
|
||||||
|
Call: Info,
|
||||||
|
Expects: "message",
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Color: true,
|
||||||
|
Verbose: true,
|
||||||
|
Call: Info,
|
||||||
|
Expects: fmt.Sprintf("\033[2m%s\033[0m \033[2minfo\033[0m\033[2m\033[0m message", now),
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Color: true,
|
||||||
|
Call: Debug,
|
||||||
|
Expects: "",
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Debug,
|
||||||
|
Expects: "DEBUG: message",
|
||||||
|
Level: logrus.DebugLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Color: true,
|
||||||
|
Call: Debug,
|
||||||
|
Expects: "\033[2mDEBUG: \033[0m\033[2mmessage\033[0m",
|
||||||
|
Level: logrus.DebugLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Color: true,
|
||||||
|
Verbose: true,
|
||||||
|
Call: Debug,
|
||||||
|
Expects: fmt.Sprintf("\033[2m%s\033[0m \033[2mdebug\033[0m\033[2m\033[0m\t\033[2mmessage\033[0m",
|
||||||
|
now),
|
||||||
|
Level: logrus.DebugLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Trace,
|
||||||
|
Expects: "",
|
||||||
|
Level: logrus.DebugLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Trace,
|
||||||
|
Expects: "TRACE: message",
|
||||||
|
Level: logrus.TraceLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Warn,
|
||||||
|
Expects: "",
|
||||||
|
Level: logrus.ErrorLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Warn,
|
||||||
|
Expects: "WARNING: message",
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Warn,
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
Color: true,
|
||||||
|
Verbose: true,
|
||||||
|
Expects: fmt.Sprintf("\033[2m%s\033[0m \033[1;93mwarning\033[0m\033[2m\033[0m\tmessage", now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Warn,
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
Color: true,
|
||||||
|
Expects: "\033[1;43;30m WARNING \033[0m message",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Error,
|
||||||
|
Expects: "ERROR: message",
|
||||||
|
Level: logrus.ErrorLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Error,
|
||||||
|
Expects: "ERROR: message",
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Error,
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
Color: true,
|
||||||
|
Verbose: true,
|
||||||
|
Expects: fmt.Sprintf("\033[2m%s\033[0m \033[1;91merror\033[0m\033[2m\033[0m\tmessage", now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Call: Error,
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
Color: true,
|
||||||
|
Expects: "\033[1;41m ERROR \033[0m message",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
fname := runtime.FuncForPC(reflect.ValueOf(c.Call).Pointer()).Name()
|
||||||
|
comps := []string{fname, c.Level.String()}
|
||||||
|
if c.Color {
|
||||||
|
comps = append(comps, "color")
|
||||||
|
}
|
||||||
|
if c.Verbose {
|
||||||
|
comps = append(comps, "verbose")
|
||||||
|
}
|
||||||
|
name := strings.Join(comps, "/")
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
env := map[string]string{
|
||||||
|
"COLOR": "",
|
||||||
|
"VERBOSE": "",
|
||||||
|
}
|
||||||
|
if c.Color {
|
||||||
|
env["COLOR"] = "always"
|
||||||
|
} else {
|
||||||
|
env["NO_COLOR"] = "1"
|
||||||
|
}
|
||||||
|
if c.Verbose {
|
||||||
|
env["VERBOSE"] = "1"
|
||||||
|
}
|
||||||
|
withEnv(t, env)
|
||||||
|
data := bytes.Buffer{}
|
||||||
|
logrus.SetLevel(c.Level)
|
||||||
|
logrus.SetOutput(&data)
|
||||||
|
c.Call("message")
|
||||||
|
expected := c.Expects
|
||||||
|
if c.Expects != "" {
|
||||||
|
expected = c.Expects + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if res := data.String(); res != expected {
|
||||||
|
t.Fatalf("%s:\ngot : %s\nwanted: %v", name, res, expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -52,39 +52,72 @@ func isTrueIsh(val string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _flags map[string]bool
|
||||||
|
|
||||||
|
func ResetParsedFlags() {
|
||||||
|
_flags = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagInArgs(name string) bool {
|
||||||
|
if _flags == nil {
|
||||||
|
_flags = map[string]bool{}
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
switch arg {
|
||||||
|
case "--verbose":
|
||||||
|
_flags["verbose"] = true
|
||||||
|
delete(_flags, "silent")
|
||||||
|
case "--silent":
|
||||||
|
_flags["silent"] = true
|
||||||
|
delete(_flags, "verbose")
|
||||||
|
case "--color":
|
||||||
|
_flags["color"] = true
|
||||||
|
delete(_flags, "no-color")
|
||||||
|
case "--no-color":
|
||||||
|
_flags["no-color"] = true
|
||||||
|
delete(_flags, "color")
|
||||||
|
case "--skip-validation":
|
||||||
|
_flags["skip-validation"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := _flags[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func DebugEnabled() bool {
|
func DebugEnabled() bool {
|
||||||
return isTrueIsh(os.Getenv(env.Debug))
|
return isTrueIsh(os.Getenv(env.Debug))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidationEnabled() bool {
|
func ValidationEnabled() bool {
|
||||||
return isFalseIsh(os.Getenv(env.ValidationDisabled))
|
return !flagInArgs("skip-validation") && isFalseIsh(os.Getenv(env.ValidationDisabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerboseEnabled() bool {
|
func VerboseEnabled() bool {
|
||||||
for _, arg := range os.Args {
|
if flagInArgs("silent") {
|
||||||
if arg == "--verbose" {
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
return isTrueIsh(os.Getenv(env.Verbose)) || flagInArgs("verbose")
|
||||||
return isTrueIsh(os.Getenv(env.Verbose))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SilenceEnabled() bool {
|
func SilenceEnabled() bool {
|
||||||
for _, arg := range os.Args {
|
if flagInArgs("verbose") {
|
||||||
if arg == "--silent" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if VerboseEnabled() {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if flagInArgs("silent") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return isTrueIsh(os.Getenv(env.Silent))
|
return isTrueIsh(os.Getenv(env.Silent)) || flagInArgs("silent")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ColorEnabled() bool {
|
func ColorEnabled() bool {
|
||||||
return isFalseIsh(os.Getenv(env.NoColor)) && !UnstyledHelpEnabled()
|
if flagInArgs("color") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're talking to ttys, we want color unless NO_COLOR/--no-color
|
||||||
|
return !(isTrueIsh(os.Getenv(env.NoColor)) || UnstyledHelpEnabled() || flagInArgs("no-color"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnstyledHelpEnabled() bool {
|
func UnstyledHelpEnabled() bool {
|
||||||
|
@ -8,15 +8,146 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||||
. "git.rob.mx/nidito/chinampa/pkg/runtime"
|
. "git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnabled(t *testing.T) {
|
func withEnv(t *testing.T, env map[string]string) {
|
||||||
defer func() { os.Setenv(env.Verbose, "") }()
|
prevEnv := os.Environ()
|
||||||
|
for _, entry := range prevEnv {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
os.Unsetenv(parts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range env {
|
||||||
|
os.Setenv(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
ResetParsedFlags()
|
||||||
|
|
||||||
|
for k := range env {
|
||||||
|
os.Unsetenv(k)
|
||||||
|
}
|
||||||
|
for _, entry := range prevEnv {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
os.Setenv(parts[0], parts[1])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCombinations(t *testing.T) {
|
||||||
|
args := append([]string{}, os.Args...)
|
||||||
|
t.Cleanup(func() { os.Args = args })
|
||||||
|
cases := []struct {
|
||||||
|
Env map[string]string
|
||||||
|
Args []string
|
||||||
|
Func func() bool
|
||||||
|
Expects bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Env: map[string]string{},
|
||||||
|
Args: []string{},
|
||||||
|
Func: VerboseEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Verbose: "1"},
|
||||||
|
Args: []string{"--silent"},
|
||||||
|
Func: VerboseEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Verbose: "1"},
|
||||||
|
Args: []string{},
|
||||||
|
Func: VerboseEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Silent: "1"},
|
||||||
|
Args: []string{},
|
||||||
|
Func: VerboseEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{},
|
||||||
|
Args: []string{},
|
||||||
|
Func: SilenceEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Silent: "1"},
|
||||||
|
Args: []string{},
|
||||||
|
Func: SilenceEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Silent: "1"},
|
||||||
|
Args: []string{"--verbose"},
|
||||||
|
Func: SilenceEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.Verbose: "1"},
|
||||||
|
Args: []string{"--silent"},
|
||||||
|
Func: SilenceEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.ForceColor: "1"},
|
||||||
|
Args: []string{"--no-color"},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{},
|
||||||
|
Args: []string{},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.ForceColor: "1"},
|
||||||
|
Args: []string{},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.ForceColor: "1"},
|
||||||
|
Args: []string{"--no-color"},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.NoColor: "1"},
|
||||||
|
Args: []string{},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: map[string]string{env.NoColor: "1"},
|
||||||
|
Args: []string{"--color"},
|
||||||
|
Func: ColorEnabled,
|
||||||
|
Expects: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
fname := runtime.FuncForPC(reflect.ValueOf(c.Func).Pointer()).Name()
|
||||||
|
name := fmt.Sprintf("%v/%v/%s", fname, c.Env, c.Args)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
withEnv(t, c.Env)
|
||||||
|
os.Args = c.Args
|
||||||
|
if res := c.Func(); res != c.Expects {
|
||||||
|
t.Fatalf("%s got %v wanted: %v", name, res, c.Expects)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnabled(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Func func() bool
|
Func func() bool
|
||||||
@ -27,6 +158,7 @@ func TestEnabled(t *testing.T) {
|
|||||||
Func: VerboseEnabled,
|
Func: VerboseEnabled,
|
||||||
Expects: true,
|
Expects: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Name: env.Silent,
|
Name: env.Silent,
|
||||||
Func: SilenceEnabled,
|
Func: SilenceEnabled,
|
||||||
@ -64,7 +196,7 @@ func TestEnabled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, val := range enabled {
|
for _, val := range enabled {
|
||||||
t.Run("enabled-"+val, func(t *testing.T) {
|
t.Run("enabled-"+val, func(t *testing.T) {
|
||||||
os.Setenv(c.Name, val)
|
withEnv(t, map[string]string{c.Name: val})
|
||||||
if c.Func() != c.Expects {
|
if c.Func() != c.Expects {
|
||||||
t.Fatalf("%s wasn't enabled with a valid value: %s", name, val)
|
t.Fatalf("%s wasn't enabled with a valid value: %s", name, val)
|
||||||
}
|
}
|
||||||
@ -74,7 +206,7 @@ func TestEnabled(t *testing.T) {
|
|||||||
disabled := []string{"", "no", "false", "0", "disabled"}
|
disabled := []string{"", "no", "false", "0", "disabled"}
|
||||||
for _, val := range disabled {
|
for _, val := range disabled {
|
||||||
t.Run("disabled-"+val, func(t *testing.T) {
|
t.Run("disabled-"+val, func(t *testing.T) {
|
||||||
os.Setenv(c.Name, val)
|
withEnv(t, map[string]string{c.Name: val})
|
||||||
if c.Func() == c.Expects {
|
if c.Func() == c.Expects {
|
||||||
t.Fatalf("%s was enabled with falsy value: %s", name, val)
|
t.Fatalf("%s was enabled with falsy value: %s", name, val)
|
||||||
}
|
}
|
||||||
@ -84,40 +216,46 @@ func TestEnabled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSilent(t *testing.T) {
|
func TestSilent(t *testing.T) {
|
||||||
origArgs := os.Args
|
args := append([]string{}, os.Args...)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() { os.Args = args })
|
||||||
os.Args = origArgs
|
|
||||||
})
|
|
||||||
t.Run("SILENT = silence", func(t *testing.T) {
|
t.Run("SILENT = silence", func(t *testing.T) {
|
||||||
t.Setenv(env.Silent, "1")
|
withEnv(t, map[string]string{
|
||||||
t.Setenv(env.Verbose, "")
|
env.Silent: "1",
|
||||||
|
env.Verbose: "",
|
||||||
|
})
|
||||||
os.Args = []string{}
|
os.Args = []string{}
|
||||||
if !SilenceEnabled() {
|
if !SilenceEnabled() {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("SILENT + VERBOSE = silence", func(t *testing.T) {
|
t.Run("SILENT+VERBOSE=silence", func(t *testing.T) {
|
||||||
t.Setenv(env.Silent, "1")
|
withEnv(t, map[string]string{
|
||||||
t.Setenv(env.Verbose, "1")
|
env.Silent: "1",
|
||||||
|
env.Verbose: "1",
|
||||||
|
})
|
||||||
os.Args = []string{}
|
os.Args = []string{}
|
||||||
if SilenceEnabled() {
|
if !SilenceEnabled() {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("VERBOSE + --silent = silent", func(t *testing.T) {
|
t.Run("VERBOSE+--silent=silent", func(t *testing.T) {
|
||||||
t.Setenv(env.Silent, "")
|
withEnv(t, map[string]string{
|
||||||
t.Setenv(env.Verbose, "1")
|
env.Silent: "0",
|
||||||
|
env.Verbose: "1",
|
||||||
|
})
|
||||||
os.Args = []string{"some", "random", "--silent", "args"}
|
os.Args = []string{"some", "random", "--silent", "args"}
|
||||||
if !SilenceEnabled() {
|
if !SilenceEnabled() {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("--silent = silent", func(t *testing.T) {
|
t.Run("--silent=silent", func(t *testing.T) {
|
||||||
t.Setenv(env.Silent, "")
|
withEnv(t, map[string]string{
|
||||||
t.Setenv(env.Verbose, "")
|
env.Silent: "",
|
||||||
|
env.Verbose: "",
|
||||||
|
})
|
||||||
os.Args = []string{"some", "random", "--silent", "args"}
|
os.Args = []string{"some", "random", "--silent", "args"}
|
||||||
if !SilenceEnabled() {
|
if !SilenceEnabled() {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@ -125,20 +263,27 @@ func TestSilent(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("nothing = nothing", func(t *testing.T) {
|
t.Run("nothing = nothing", func(t *testing.T) {
|
||||||
t.Setenv(env.Silent, "")
|
withEnv(t, map[string]string{
|
||||||
t.Setenv(env.Verbose, "")
|
env.Silent: "",
|
||||||
|
env.Verbose: "",
|
||||||
|
})
|
||||||
os.Args = []string{"some", "random", "args"}
|
os.Args = []string{"some", "random", "args"}
|
||||||
if SilenceEnabled() {
|
if SilenceEnabled() {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
if VerboseEnabled() {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnvironmentMapEnabled(t *testing.T) {
|
func TestEnvironmentMapEnabled(t *testing.T) {
|
||||||
trueString := strconv.FormatBool(true)
|
trueString := strconv.FormatBool(true)
|
||||||
os.Setenv(env.ForceColor, trueString)
|
withEnv(t, map[string]string{
|
||||||
os.Setenv(env.Debug, trueString)
|
env.ForceColor: trueString,
|
||||||
os.Setenv(env.Verbose, trueString)
|
env.Debug: trueString,
|
||||||
|
env.Verbose: trueString,
|
||||||
|
})
|
||||||
|
|
||||||
res := EnvironmentMap()
|
res := EnvironmentMap()
|
||||||
if res == nil {
|
if res == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user