feat: Add custom help text

pull/2/head
Karan Sharma 2020-12-13 21:03:44 +05:30
parent 40c62216ec
commit 9d2245f72e
7 changed files with 114 additions and 43 deletions

View File

@ -1,9 +1,8 @@
DOGGO-BIN := doggo.bin DOGGO-BIN := doggo.bin
HASH := $(shell git rev-parse --short HEAD) HASH := $(shell git rev-parse --short HEAD)
COMMIT_DATE := $(shell git show -s --format=%ci ${HASH})
BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S') BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S')
VERSION := ${HASH} (${COMMIT_DATE}) VERSION := ${HASH}
.PHONY: build .PHONY: build
build: ## Build the doggo binary build: ## Build the doggo binary

22
TODO.md
View File

@ -1,4 +1,4 @@
# doggo - v1.0 Milestone # doggo - Initial Release Milestone
## Resolver ## Resolver
- [x] Create a DNS Resolver struct - [x] Create a DNS Resolver struct
@ -6,20 +6,22 @@
- [x] Add a resolve method - [x] Add a resolve method
- [x] Make it separate from Hub - [x] Make it separate from Hub
- [x] Parse output into separate fields - [x] Parse output into separate fields
- [ ] Test IPv6 - [ ] Test IPv6/IPv4 only options
- [x] Add DOH support - [x] Add DOH support
- [x] Add DOT support - [x] Add DOT support
- [x] Add DNS protocol on TCP mode support. - [x] Add DNS protocol on TCP mode support.
- [ ] Error on NXDomain (Realted upstream [bug](https://github.com/miekg/dns/issues/1198)) - [ ] Error on NXDomain (Realted upstream [bug](https://github.com/miekg/dns/issues/1198))
- [ ] Support all DNS records?
- [x] Major records supported
## CLI Features ## CLI Features
- [ ] `digfile` - [ ] `digfile`
- [x] `ndots` support - [ ] `ndots` support
- [x] `search list` support - [x] `search list` support
- [x] JSON output - [x] JSON output
- [x] Colorized output - [x] Colorized output
- [x] Table output - [x] Table output
- [ ] Parsing options free-form - [x] Parsing options free-form
- [x] Remove urfave/cli in favour of `flag` - [x] Remove urfave/cli in favour of `flag`
## CLI Grunt ## CLI Grunt
@ -27,15 +29,27 @@
- [x] Neatly package them to load args in different functions - [x] Neatly package them to load args in different functions
- [x] Upper case is not mandatory for query type/classes - [x] Upper case is not mandatory for query type/classes
- [x] Output - [x] Output
- [ ] Custom Help Text
- [x] Add examples
- [ ] Colorize
- [ ] Add different commands
- [x] Add client transport options - [x] Add client transport options
- [ ] Fix an issue while loading free form args, where the same records are being added twice
## Tests ## Tests
## Documentation ## Documentation
- [ ] Mkdocs init project
- [ ] Custom Index (Landing Page)
## Release Checklist ## Release Checklist
- [ ] Goreleaser - [ ] Goreleaser
- [ ] Snap - [ ] Snap
- [ ] Homebrew - [ ] Homebrew
- [ ] ARM - [ ] ARM
## v1.0
- [ ] Support obscure protocal tweaks in `dig`

View File

@ -1,11 +1,11 @@
package main package main
import ( import (
"fmt"
"os" "os"
"github.com/knadh/koanf" "github.com/knadh/koanf"
"github.com/knadh/koanf/providers/posflag" "github.com/knadh/koanf/providers/posflag"
"github.com/sirupsen/logrus"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
@ -27,10 +27,7 @@ func main() {
// Configure Flags // Configure Flags
// Use the POSIX compliant pflag lib instead of Go's flag lib. // Use the POSIX compliant pflag lib instead of Go's flag lib.
f := flag.NewFlagSet("config", flag.ContinueOnError) f := flag.NewFlagSet("config", flag.ContinueOnError)
f.Usage = func() { f.Usage = renderCustomHelp
fmt.Println(f.FlagUsages())
os.Exit(0)
}
// Path to one or more config files to load into koanf along with some config params. // Path to one or more config files to load into koanf along with some config params.
f.StringSliceP("query", "q", []string{}, "Domain name to query") f.StringSliceP("query", "q", []string{}, "Domain name to query")
f.StringSliceP("type", "t", []string{}, "Type of DNS record to be queried (A, AAAA, MX etc)") f.StringSliceP("type", "t", []string{}, "Type of DNS record to be queried (A, AAAA, MX etc)")
@ -50,26 +47,32 @@ func main() {
// Output Options // Output Options
f.BoolP("json", "J", false, "Set the output format as JSON") f.BoolP("json", "J", false, "Set the output format as JSON")
f.Bool("time", false, "Display how long it took for the response to arrive") f.Bool("time", false, "Display how long it took for the response to arrive")
f.Bool("color", true, "Show colored output")
f.Bool("debug", false, "Enable debug mode") f.Bool("debug", false, "Enable debug mode")
// Parse and Load Flags // Parse and Load Flags
f.Parse(os.Args[1:]) f.Parse(os.Args[1:])
if err := k.Load(posflag.Provider(f, ".", k), nil); err != nil { if err := k.Load(posflag.Provider(f, ".", k), nil); err != nil {
hub.Logger.Fatalf("error loading flags: %v", err) hub.Logger.Errorf("error loading flags: %v", err)
fmt.Println(f.FlagUsages()) f.Usage()
os.Exit(0) hub.Logger.Exit(2)
}
// set log level
if k.Bool("debug") {
// Set logger level
hub.Logger.SetLevel(logrus.DebugLevel)
} else {
hub.Logger.SetLevel(logrus.InfoLevel)
} }
// Run the app. // Run the app.
hub.Logger.Debug("Starting doggo...") hub.Logger.Debug("Starting doggo 🐶")
// Parse Query Args // Parse Query Args
hub.loadQueryArgs() hub.loadQueryArgs()
// Start App // Start App
if len(hub.QueryFlags.QNames) == 0 { if len(hub.QueryFlags.QNames) == 0 {
fmt.Println(f.FlagUsages()) f.Usage()
os.Exit(0)
} }
hub.Lookup() hub.Lookup()

View File

@ -1,21 +1,42 @@
package main package main
import ( import (
"fmt" "html/template"
"io" "os"
) )
// Override Help Template // AppHelpTemplate is the text template to customise the Help output.
var helpTmpl = `NAME: // Uses text/template to render templates.
{{.Name}} var AppHelpTemplate = `NAME:
{{ range $key, $value := . }} {{.Name}} {{.Description}}
<li><strong>{{ $key }}</strong>: {{ $value }}</li>
{{ end }} USAGE:
{{.Name}} [OPTIONS] [--] <arguments>
VERSION:
{{.Version}} Built at {{.Date}}
EXAMPLES:
doggo mrkaran.dev Query a domain using defaults
doggo mrkaran.dev CNAME Looks up for a CNAME record.
doggo mrkaran.dev MX @9.9.9.9 Uses a custom DNS resolver.
doggo -q mrkaran.dev -t MX -n 1.1.1.1 Using named arguments
` `
func renderCustomHelp(w io.Writer, templ string, data interface{}) { func renderCustomHelp() {
var helpTmplVars = map[string]string{} helpTmplVars := map[string]string{
"Name": "doggo",
helpTmplVars["Name"] = "doggo" "Description": "DNS Client for Humans",
fmt.Fprintf(w, helpTmpl, helpTmplVars) "Version": buildVersion,
"Date": buildDate,
}
tmpl, err := template.New("test").Parse(AppHelpTemplate)
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, helpTmplVars)
if err != nil {
panic(err)
}
os.Exit(0)
} }

View File

@ -31,6 +31,7 @@ type QueryFlags struct {
ShowJSON bool `koanf:"json"` ShowJSON bool `koanf:"json"`
UseSearchList bool `koanf:"search"` UseSearchList bool `koanf:"search"`
Ndots int `koanf:"ndots"` Ndots int `koanf:"ndots"`
Color bool `koanf:"color"`
} }
// NewHub initializes an instance of Hub which holds app wide configuration. // NewHub initializes an instance of Hub which holds app wide configuration.

View File

@ -64,9 +64,15 @@ func (hub *Hub) outputJSON(out []Output, msgs []resolvers.Response) {
} }
func (hub *Hub) outputTerminal(out []Output) { func (hub *Hub) outputTerminal(out []Output) {
green := color.New(color.FgGreen).SprintFunc() green := color.New(color.FgGreen, color.Bold).SprintFunc()
blue := color.New(color.FgBlue).SprintFunc() blue := color.New(color.FgBlue, color.Bold).SprintFunc()
magenta := color.New(color.FgMagenta).SprintFunc() yellow := color.New(color.FgYellow, color.Bold).SprintFunc()
cyan := color.New(color.FgCyan, color.Bold).SprintFunc()
red := color.New(color.FgRed, color.Bold).SprintFunc()
if !hub.QueryFlags.Color {
color.NoColor = true // disables colorized output
}
table := tablewriter.NewWriter(os.Stdout) table := tablewriter.NewWriter(os.Stdout)
header := []string{"Name", "Type", "Class", "TTL", "Address", "Nameserver"} header := []string{"Name", "Type", "Class", "TTL", "Address", "Nameserver"}
@ -87,7 +93,24 @@ func (hub *Hub) outputTerminal(out []Output) {
table.SetNoWhiteSpace(true) table.SetNoWhiteSpace(true)
for _, o := range out { for _, o := range out {
output := []string{green(o.Name), blue(o.Type), o.Class, o.TTL, magenta(o.Address), o.Nameserver} var typOut string
switch typ := o.Type; typ {
case "A":
typOut = blue(o.Type)
case "AAAA":
typOut = blue(o.Type)
case "MX":
typOut = cyan(o.Type)
case "NS":
typOut = cyan(o.Type)
case "CNAME":
typOut = yellow(o.Type)
case "TXT":
typOut = yellow(o.Type)
default:
typOut = red(o.Type)
}
output := []string{green(o.Name), typOut, o.Class, o.TTL, o.Address, o.Nameserver}
// Print how long it took // Print how long it took
if hub.QueryFlags.DisplayTimeTaken { if hub.QueryFlags.DisplayTimeTaken {
output = append(output, o.TimeTaken) output = append(output, o.TimeTaken)
@ -125,10 +148,28 @@ func collectOutput(responses []resolvers.Response) []Output {
addr = t.AAAA.String() addr = t.AAAA.String()
case *dns.CNAME: case *dns.CNAME:
addr = t.Target addr = t.Target
case *dns.CAA:
addr = t.Tag + " " + t.Value
case *dns.HINFO:
addr = t.Cpu + " " + t.Os
// case *dns.LOC:
// addr = t.String()
case *dns.PTR:
addr = t.Ptr
case *dns.SRV:
addr = strconv.Itoa(int(t.Priority)) + " " +
strconv.Itoa(int(t.Weight)) + " " +
t.Target + ":" + strconv.Itoa(int(t.Port))
case *dns.TXT:
addr = t.String()
case *dns.NS:
addr = t.String()
case *dns.MX: case *dns.MX:
addr = strconv.Itoa(int(t.Preference)) + " " + t.Mx addr = strconv.Itoa(int(t.Preference)) + " " + t.Mx
case *dns.SOA: case *dns.SOA:
addr = t.String() addr = t.String()
case *dns.NAPTR:
addr = t.String()
} }
h := a.Header() h := a.Header()

View File

@ -5,17 +5,9 @@ import (
"strings" "strings"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/sirupsen/logrus"
) )
func (hub *Hub) loadQueryArgs() error { func (hub *Hub) loadQueryArgs() error {
// set log level
if k.Bool("debug") {
// Set logger level
hub.Logger.SetLevel(logrus.DebugLevel)
} else {
hub.Logger.SetLevel(logrus.InfoLevel)
}
err := hub.loadNamedArgs() err := hub.loadNamedArgs()