Are you still coding go cli handling by hand?
Tags: go, cli, code
Published: 2020-10-15 14:10:25

---
title: Are you still coding go cli handling by hand?
published: true
description: Time to stop coding your go command line handling by hand
tags: go,golang,cli,code
cover_image: https://i.stack.imgur.com/J6mvE.jpg
---

Are you still coding your go cli handling by hand? Time to stop and rethink better ways to do it now. 

Take a look at this go command line handling file:

{% gist https://gist.github.com/suntong/7c23ea49c48ea08b5736088994971f44 file=OpenSesame_cliDef.go %}

The help text _"path to serve files from"_ shows up three times:

```go
$ grep -1 'path to serve' OpenSesame_cliDef.go
        Port string // listening port
        Path string // path to serve files from
        Help bool   // show usage help
--
        flag.StringVar(&Opts.Path, "path", "./",
                "path to serve files from")
        flag.BoolVar(&Opts.Help, "help", false,
--

const usageSummary = "  -port\tlistening port (OPENSESAME_PORT)\n  -path\tpath to serve files from (OPENSESAME_PATH)\n  -help\tshow usage help (OPENSESAME_HELP)\n\nDetails:\n\n"
```

If coding by hand, most probably the first occurrence will be omitted. But that's kind of pity, as the help text is helpful in explaining what exactly the `Path string` variable is for:

```go
        Path string // path to serve files from
```

Also, if coding by hand, the second and third occurrences will very easily get out of sync if not be taken care of all the time. 

All these violate the DRY (Don't Repeat Yourself) principle. How to avoid it? The answer is this file:

{% gist https://gist.github.com/suntong/7c23ea49c48ea08b5736088994971f44 file=OpenSesame_cli.yaml %}

from which we can see that the help text _"path to serve files from"_ is defined only once but get automatically put into three different proper places.

![success](https://i.stack.imgur.com/BrotW.jpg)

This is the go cli handling code auto generation we are talking about. Look at the above two files, which one is more easier to maintain? With cli handling code auto generation feature out there, don't code it manually next time when you do cli handling again. 

What if you already have a manually written cli handling code? Well, converting it to automatically generated is not that hard at all. Here are just two simple steps,

- [introduce config.go](https://github.com/go-ego/gpy/pull/19/commits/a7066d8aac389ff8e2525175620450e505bc7a15), which ***ports*** the existing cli handling from existing code into auto-generated `config.go` file.

- [implemented -d, -c, & -help](https://github.com/go-ego/gpy/pull/19/commits/530ac85adc94068dc17f3d375e33a973bd5d609f), which ***adds*** more business logic handling for the newly added command line flags/parameters.

Very simple, no sweat at all. All thanks to the auto-generated `config.go` file.

![success](https://i.stack.imgur.com/vmJhi.jpg)

OK, now comes to the question, [how is the `config.go` file auto-generated](https://github.com/suntong/gpy/commit/e3190cbaa3924d5edcfa60a830a7177085859dc3)? Let me answer it with another example, that starts everything from the beginning, with [all the following explanation borrowed from here](https://github.com/go-easygen/wireframe/wiki/Command-line-flag-handling-code-auto-generation#opensesame)

This section will work through every steps how the program is created. 

1. [Initialize an empty project](https://github.com/suntong/OpenSesame/commit/70ef318ab9ad0fdacb131a84b4be7bfecc64d189).  
   The `_proj.yaml` file will later be used to 
   * [Create Repository in Github](https://github.com/go-easygen/wireframe#github-create-repo---create-repository-in-github)
   * [Create Release in Github](https://github.com/go-easygen/wireframe#github-create-release---create-release-in-github)
   * [Create Binary releases using CI/CD](https://github.com/go-easygen/wireframe#binary-releases)
1. [Initialize project wireframe](https://github.com/suntong/OpenSesame/commit/71be01c711a6110d5873323c3eb38985ffe9af90) using the standard Go `flag` package, by
   1. Edit the `_cli.yaml` cli arguments definition file
   1. Then do `go generate` (enabled [like this](https://github.com/suntong/OpenSesame/commit/af07c1acebf4002f79e57290244d1351685469ab)), or directly invoke the `_cliGen.sh` script
   1. Finally do `go build`
1. [Provide the initial OpenSesame functionality](https://github.com/suntong/OpenSesame/commit/45c16f97da8f3aa0614a69261c347c695c13cc1b)
1. [Update OpenSesame code to make use of the auto-generated cli wireframe](https://github.com/suntong/OpenSesame/commit/4e640c90f756d4f4c1169c9602f37a7846208d42)
1. 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](https://github.com/suntong/OpenSesame/commit/b5e96447f7d85a5e54763b8c5108eac1f5760836) of `port` value
   * [Update usage doc](https://github.com/suntong/OpenSesame/commit/177e34b3224abf84f1f27397b5d7dcce161d8aac)
   * [Enable `UsageSummary`](https://github.com/suntong/OpenSesame/commit/fec5406dacce680c8e7e802531d0b4aa13ed486b)
   * etc.

This finished a full `Initialize` -> `Make use` -> `More changes to cli definitions` cycle. 

![success](https://i.stack.imgur.com/DR4wz.jpg)

Make sure to check out the rest of the above quoted wiki, [Go command line flag handling code auto generation](https://github.com/go-easygen/wireframe/wiki/Command-line-flag-handling-code-auto-generation), from which you can see that using the same `.yaml` driven file, not only the go standard `flag` command line handling code can be automatically generated, but `viper` and `cobra` as well, and all the way to the full-featured cli code that can [set the argument values at three different levels](https://github.com/go-easygen/wireframe/wiki/Command-line-flag-handling-code-auto-generation#priorities):

So the priority of setting the `Host` value is, from lowest priority to the highest:

1. self-config file
1. environment variable
1. command line

Three different levels.