-
Notifications
You must be signed in to change notification settings - Fork 2
Command line flag handling code auto generation
Summary on Providing Options for Go Applications
Entrées before we get into wireframing:
-
Accessing Go command line parameters
which starts withArgs
-
Passing options to Go from command line
which focuses on the built-in command line parameters handling option, the built-inflag
package. -
TOML -- Go De Facto config file
which covers TOML, and also on overcoming its drawbacks -
Beyond TOML, the Go's De Facto config file
which introduce a package less well-known but get the job done nice and easily -
Viper, Go configuration with fangs
which, of course, touches uponviper
and with a working example that works withcobra
together, which you can't find elsewhere easily -
EasyGen -- Universal code/text generator that is easy to use the highlight of the mini series --- comparing to
easygen
, all above manual coding is too arduous and tedious. - Easygen is now coding itself this an appendix to the mini series on how to provide options for Go applications, and doing it easily.
-
Showcasing the power of easygen with ffcvt another example of easygen, to auto generate command line parameters handling for
ffcvt
-
Easygen for viper and as a universal code/text generator, let
easygen
code your viper command line parameters handling for you.
First of all, this is what auto-generated help looks like. The program does nothing but to showcase what the wire framing can do:
$ wireframe
wire framing
Version 0.1.0 built on 2018-09-18
Tool to showcase wire-framing command line app fast prototype
Options:
-h, --help display help information
-c, --config config file [=wireframe_cfg.json]
-H, --host host addr [=$HOST]
-p, --port listening port
--long Now can use the \n to arrange parameters in groups
Just like what is showing here, even with extreme long usage text that can spread across multiple lines [=$Demo]
-D, --daemonize daemonize the service
-v, --verbose Verbose mode (Multiple -v options increase the verbosity)
Commands:
put Upload into service
get Get from the service
This gives full help at root level.
There are also two sub-commands, which are:
$ wireframe put
Upload into service
Usage:
wireframe put -i /tmp/f
Options:
-h, --help display help information
-c, --config config file [=wireframe_cfg.json]
-H, --host host addr [=$HOST]
-p, --port listening port
--long Now can use the \n to arrange parameters in groups
Just like what is showing here, even with extreme long usage text that can spread across multiple lines [=$Demo]
-D, --daemonize daemonize the service
-v, --verbose Verbose mode (Multiple -v options increase the verbosity)
-i, --input *The file to upload from (mandatory)
The above gives sub-command put
level help, and the next is for get
:
$ wireframe get
Get from the service
Usage:
wireframe get -o /tmp/f some more args
Options:
-h, --help display help information
-c, --config config file [=wireframe_cfg.json]
-H, --host host addr [=$HOST]
-p, --port listening port
--long Now can use the \n to arrange parameters in groups
Just like what is showing here, even with extreme long usage text that can spread across multiple lines [=$Demo]
-D, --daemonize daemonize the service
-v, --verbose Verbose mode (Multiple -v options increase the verbosity)
-i, --input *The file to upload from (mandatory)
-o, --output The output file (default: some file)
The above gives sub-command get
level help.
Before we see how it runs, let's take a look at how to define and get all the above. Here is the single source of CLI definition for all above:
wireframe_cli.yaml
# program name, name for the executable
ProgramName: wireframe
Authors: Myself <[email protected]>
PackageName: main
Name: wireframe
Desc: "wire framing"
Text: Tool to showcase wire-framing command line app fast prototype
#NumOption: cli.AtLeast(1)
Style: cli.DenseNormalStyle
NumArg: cli.AtLeast(1)
Global: true
# this (Self) means that root option is using the self-config .json file
Self: true
#UsageLead: "Usage:\\n wireframe [Options] dir [dirs...]"
Options:
- Name: Self
Type: '*rootT'
Flag: c,config
Usage: config file\n
#Value: "$__EXEC_FILENAME.json"
Value: wireframe_cfg.json
- Name: Host
Type: string
Flag: H,host
Usage: host addr
Value: '$HOST'
- Name: Port
Type: int
Flag: p,port
Usage: listening port\n
- Name: Demo
Type: string
Flag: long
Usage: Now can use the \\n to arrange parameters in groups\n\t\t\tJust like what is showing here, even with extreme long usage text that can spread across multiple lines\n
Value: '$Demo'
- Name: Daemonize
Type: bool
Flag: D,daemonize
Usage: daemonize the service
- Name: Verbose
Type: cli.Counter
Flag: v,verbose
Usage: Verbose mode (Multiple -v options increase the verbosity)\n
Command:
- Name: put
Desc: "Upload into service"
Text: 'Usage:\n wireframe put -i /tmp/f'
#NumArg: cli.AtLeast(1)
NumOption: cli.AtLeast(1)
Options:
- Name: Filei
Type: '*clix.Reader'
Flag: '*i,input'
Usage: The file to upload from (mandatory)
- Name: get
Desc: Get from the service
Text: 'Usage:\n wireframe get -o /tmp/f mandatory some more args'
NumArg: cli.AtLeast(1)
#NumOption: cli.AtLeast(1)
Options:
- Name: Filei
Type: '*clix.Reader'
Flag: '*i,input'
Usage: The file to upload from (mandatory)
- Name: Fileo
Type: '*clix.Writer'
Flag: o,output
Usage: 'The output file (default: some file)'
The above yaml
definition is all it takes to get a wire-framed Go code to start with. Click and expand to see the details.
Before we jump into the generate code itself, just take a look now what we will get out of the box first:
$ touch /tmp/f; wireframe put -i /tmp/f
[put]:
&{Helper:{Help:false} Self:0xc420010240 Host:127.0.0.1 Port:8080 Daemonize:false Verbose:{value:0}}
&{Filei:0xc4200d86c0}
[]
wireframe v 0.1.0. Upload into service
Copyright (C) 2018, Myself <[email protected]>
This shows getting everything from the self-config file.
Note the value of Host
, it is read from the wireframe_cfg.json
self-config file.
$ HOST=10.0.0.1 wireframe put -i /tmp/f
[put]:
&{Helper:{Help:false} Self:0xc42008c1c0 Host:10.0.0.1 Port:8080 Daemonize:false Verbose:{value:0}}
&{Filei:0xc4200f2660}
[]
wireframe v 0.1.0. Upload into service
Copyright (C) 2018, Myself <[email protected]>
This shows overriding settings from the self-config file using the environment variables.
Note the value of Host
now is taken from the environment variable, instead from the wireframe_cfg.json
self-config file.
$ HOST=10.0.0.1 wireframe put -i /tmp/f -H 168.0.0.1
[put]:
&{Helper:{Help:false} Self:0xc4200901c0 Host:168.0.0.1 Port:8080 Daemonize:false Verbose:{value:0}}
&{Filei:0xc4200f6680}
[]
wireframe v 0.1.0. Upload into service
Copyright (C) 2018, Myself <[email protected]>
This shows overriding settings on the command line.
Note the value of Host
now is taken from the command line.
So the priority of setting the Host
value is, from lowest priority to the highest:
- self-config file
- environment variable
- command line
Three different levels.
$ HOST=10.0.0.1 wireframe get -o /tmp/f some more args
[get]:
&{Helper:{Help:false} Self:0xc420090180 Host:10.0.0.1 Port:8080 Daemonize:false Verbose:{value:0}}
&{Fileo:0xc4200f8680}
[some more args]
wireframe v 0.1.0. Get from the service
Copyright (C) 2018, Myself <[email protected]>
This just shows how to make use of the extra arguments passed from the command line.
Note the setting is a bit different between put
and get
regarding what is mandatory on the command line. I.e., for get
, there much be some extra command line arguments.
Basically, the above functionalities are what we can get out of the box from the above single source of CLI definition file automatically, without a single line of customization code.
With the existing setup, to modify Command line arguments, there are only three steps involved:
- First edit the
_cli.yaml
cli arguments definition file - Then do
go generate
- Finally do
go build
The modification from the _cli.yaml
file will now manifest in the newly built executable.
As explained before, the above wireframe
program does nothing but to showcase what the wire framing can do.
Here are CLI handling auto-generation real life examples.
OpenSesame
is for sharing files temporarily. If you have some big files that you don't want to host anywhere else, you can spin up OpenSesame
to serve those shared files directly from your box using a random path/url, then close it down after the other party have grabbed them. The url is only good for one-time, and will become useless even if it leaked later.
This section will work through every steps how the program is created.
-
Initialize an empty project.
The_proj.yaml
file will later be used to -
Initialize project wireframe using the standard Go
flag
package, by- Edit the
_cli.yaml
cli arguments definition file - Then do
go generate
(enabled like this), or directly invoke the_cliGen.sh
script - Finally do
go build
- Edit the
- Provide the initial OpenSesame functionality
- Update OpenSesame code to make use of the auto-generated cli wireframe
- The auto-generated cli wireframe code are not set in stones, they can later be further amended, just like the real-world cases. For e.g.
-
Update cli definition of
port
value - Update usage doc
- Enable
UsageSummary
- etc.
-
Update cli definition of
This finished a full Initialize
-> Make use
-> More changes to cli definitions
cycle.
The project also showcases the CI/CD features like,
- Add github CI/CD
-
Add travis with CI/CD to build into bintray bin & Debian repo
In case you need further tweaking the.travis.yml
file, here are more examples:
-
FFCvt
is likeOpenSesame
, which uses nothing but the standard Goflag
package for CLI handling. - It uses the same
easygen
template asOpenSesame
, just - Defining CLI arguments in a massive scale. For any program that has so many CLI arguments, imagine that you need to build the CLI handling manually one by one.
For further details, refer to Showcasing the power of easygen with ffcvt that auto generate command line parameters handling for ffcvt
Refer to
- Command line flag handling code auto-generation, especially,
- the cli based command line flag handling code auto-generation.
- for the four different
UsageStyles
that can be used to control theusage()
output, check out the UsageStyle of package cli wiki.