Skip to content

Commit b1ed2cc

Browse files
CLI interface setup and main/root command
The root command `snowsaw` represents the main entry point of the application that - registers all subcommands and global flags. - initializes the application logging/printer (GH-59) verbosity level. - checks for existing application-wide configuration files to load and merges them with the given flag parameters. The `info` subcommand prints more detailed application information while the `--version` flag can be used to obtain the version number in a parsable format. The `main` function in the `main` package has been placed in the `main.go` in the repository root. It calls `Run()` of the `snowsaw` command to start the main application flow. Epic GH-33 Depends on GH-58 Resolves GH-61
1 parent 5aa483e commit b1ed2cc

File tree

5 files changed

+211
-1
lines changed

5 files changed

+211
-1
lines changed

cmd/snowsaw/info/info.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (C) 2017-present Arctic Ice Studio <[email protected]>
2+
// Copyright (C) 2017-present Sven Greb <[email protected]>
3+
//
4+
// Project: snowsaw
5+
// Repository: https://github.com/arcticicestudio/snowsaw
6+
// License: MIT
7+
8+
// Author: Arctic Ice Studio <[email protected]>
9+
// Author: Sven Greb <[email protected]>
10+
// Since: 0.4.0
11+
12+
// Package info provides the info command to print more detailed application information.
13+
package info
14+
15+
import (
16+
"fmt"
17+
18+
"github.com/fatih/color"
19+
"github.com/spf13/cobra"
20+
21+
"github.com/arcticicestudio/snowsaw/pkg/config"
22+
)
23+
24+
// NewInfoCmd creates and configures a new `info` command.
25+
func NewInfoCmd() *cobra.Command {
26+
infoCmd := &cobra.Command{
27+
Use: "info",
28+
Short: "Prints more detailed application information",
29+
Run: func(cmd *cobra.Command, args []string) {
30+
if config.BuildDateTime != "" {
31+
fmt.Println(fmt.Sprintf("%s %s (build %s)",
32+
color.CyanString(config.ProjectName),
33+
color.BlueString(config.Version),
34+
color.GreenString(config.BuildDateTime)))
35+
} else {
36+
fmt.Println(fmt.Sprintf("%s %s",
37+
color.CyanString(config.ProjectName),
38+
color.BlueString(config.Version)))
39+
}
40+
},
41+
}
42+
43+
return infoCmd
44+
}

cmd/snowsaw/snowsaw.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (C) 2017-present Arctic Ice Studio <[email protected]>
2+
// Copyright (C) 2017-present Sven Greb <[email protected]>
3+
//
4+
// Project: snowsaw
5+
// Repository: https://github.com/arcticicestudio/snowsaw
6+
// License: MIT
7+
8+
// Author: Arctic Ice Studio <[email protected]>
9+
// Author: Sven Greb <[email protected]>
10+
// Since: 0.4.0
11+
12+
// Package snowsaw provides the root command of the application and bootstraps the startup.
13+
package snowsaw
14+
15+
import (
16+
"os"
17+
"strings"
18+
19+
"github.com/fatih/color"
20+
"github.com/spf13/cobra"
21+
22+
"github.com/arcticicestudio/snowsaw/cmd/snowsaw/info"
23+
"github.com/arcticicestudio/snowsaw/pkg/config"
24+
"github.com/arcticicestudio/snowsaw/pkg/config/builder"
25+
"github.com/arcticicestudio/snowsaw/pkg/config/source/file"
26+
"github.com/arcticicestudio/snowsaw/pkg/prt"
27+
)
28+
29+
var (
30+
// debug indicates if the `debug` flag has been set to enable configure the logging for the debug scope.
31+
debug bool
32+
// explicitConfigFilePath stores the path to the application configuration file when the `config` flag is specified.
33+
explicitConfigFilePath string
34+
)
35+
36+
// rootCmd is the root command of the application.
37+
var rootCmd = &cobra.Command{
38+
Use: config.ProjectName,
39+
Short: "A lightweight, plugin-driven and dynamic dotfiles bootstrapper.",
40+
Run: func(cmd *cobra.Command, args []string) {
41+
if err := cmd.Help(); err != nil {
42+
prt.Errorf("Failed to run %s: %v", config.ProjectName, err)
43+
os.Exit(1)
44+
}
45+
},
46+
}
47+
48+
// Run is the main application function that adds all child commands to the root command and sets flags appropriately.
49+
// This is called by `main.main()` and only needs to be run once for the root command.
50+
func Run() {
51+
// Disable verbose errors to provide custom formatted CLI output via application-wide printer.
52+
rootCmd.SilenceErrors = true
53+
54+
// Run the application with the given commands, flags and arguments and exit on any (downstream) error.
55+
if err := rootCmd.Execute(); err != nil {
56+
prt.Errorf(err.Error())
57+
os.Exit(1)
58+
}
59+
}
60+
61+
func init() {
62+
// Specify the functions to be run before each command gets executed.
63+
cobra.OnInitialize(initDebugScope, initConfig, initPrinter)
64+
65+
// Define global application flags.
66+
rootCmd.PersistentFlags().StringVar(&explicitConfigFilePath, "config", "", "set the configuration file")
67+
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "enable debug information output")
68+
69+
// Set the app version information for the automatically generated `version` flag.
70+
rootCmd.Version = color.CyanString(config.Version)
71+
rootCmd.SetVersionTemplate(`{{printf "%s\n" .Version}}`)
72+
73+
// Create and register all subcommands.
74+
rootCmd.AddCommand(info.NewInfoCmd())
75+
}
76+
77+
// initConfig searches and loads either the default application configuration file paths or the explicit file at the
78+
// given path specified through the global `config` flag.
79+
func initConfig() {
80+
if explicitConfigFilePath != "" {
81+
if err := builder.Load(file.NewFile(explicitConfigFilePath)).Into(&config.AppConfig); err != nil {
82+
prt.Errorf("while loading custom application configuration file:\n%v", err)
83+
os.Exit(1)
84+
}
85+
} else {
86+
b := builder.Load(config.AppConfigPaths...)
87+
if len(b.Files) == 0 {
88+
prt.Debugf("No configuration files found, using default application configuration.")
89+
}
90+
if err := b.Into(&config.AppConfig); err != nil {
91+
prt.Errorf("while loading application configuration files:\n%v", err)
92+
os.Exit(1)
93+
}
94+
}
95+
}
96+
97+
// initDebugScope configures the application when run with debug scope.
98+
func initDebugScope() {
99+
if debug {
100+
prt.SetVerbosityLevel(prt.DebugVerbosity)
101+
}
102+
}
103+
104+
// setPrinterVerbosityLevel configures the global CLI printer like the verbosity level.
105+
func initPrinter() {
106+
lvl, err := prt.ParseVerbosityLevel(strings.ToUpper(config.AppConfig.LogLevel))
107+
if err != nil {
108+
prt.Debugf("Error while parsing log level from configuration: %v", err)
109+
prt.Debugf("Using default INFO level as fallback")
110+
prt.SetVerbosityLevel(prt.InfoVerbosity)
111+
} else {
112+
prt.Debugf("Using configured logger level: %s", strings.ToUpper(config.AppConfig.LogLevel))
113+
prt.SetVerbosityLevel(lvl)
114+
}
115+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ require (
88
github.com/imdario/mergo v0.3.7
99
github.com/mattn/go-colorable v0.1.2 // indirect
1010
github.com/mitchellh/go-homedir v1.1.0
11-
gopkg.in/yaml.v2 v2.2.2 // indirect
11+
github.com/spf13/cobra v0.0.5
1212
)

go.sum

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
1+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2+
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
3+
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
4+
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
5+
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
6+
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
7+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
29
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
10+
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
311
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
412
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
13+
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
514
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
615
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
16+
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
17+
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
18+
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
719
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
820
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
921
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
1022
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
1123
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
1224
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
25+
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
26+
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
27+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
28+
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
29+
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
30+
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
31+
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
32+
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
33+
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
34+
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
35+
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
36+
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
37+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
38+
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
39+
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
40+
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
41+
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1342
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
1443
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
44+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1545
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1646
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1747
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (C) 2017-present Arctic Ice Studio <[email protected]>
2+
// Copyright (C) 2017-present Sven Greb <[email protected]>
3+
//
4+
// Project: snowsaw
5+
// Repository: https://github.com/arcticicestudio/snowsaw
6+
// License: MIT
7+
8+
// Author: Arctic Ice Studio <[email protected]>
9+
// Author: Sven Greb <[email protected]>
10+
// Since: 0.4.0
11+
12+
// A lightweight, plugin-driven and dynamic dotfiles bootstrapper.
13+
package main
14+
15+
import (
16+
"github.com/arcticicestudio/snowsaw/cmd/snowsaw"
17+
)
18+
19+
func main() {
20+
snowsaw.Run()
21+
}

0 commit comments

Comments
 (0)