├── .github ├── dependabot.yml ├── screenshot.png └── workflows │ └── test.yml ├── .gitignore ├── .golangci.yml ├── LICENSE ├── Makefile ├── README.md ├── ccli.go ├── example └── main.go ├── go.mod └── go.sum /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "11:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saschagrunert/ccli/db6918527da3eb18292f7ddbb019cc9b06818590/.github/screenshot.png -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | env: 8 | GO_VERSION: '1.21' 9 | jobs: 10 | run: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-go@v4 15 | with: 16 | go-version: ${{ env.GO_VERSION }} 17 | - run: | 18 | go run example/main.go 19 | go run example/main.go -h 20 | 21 | lint: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: actions/setup-go@v4 26 | with: 27 | go-version: ${{ env.GO_VERSION }} 28 | - run: make lint 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | *.sw* 26 | build 27 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | run: 3 | concurrency: 6 4 | deadline: 5m 5 | linters: 6 | disable-all: true 7 | enable: 8 | - asasalint 9 | - asciicheck 10 | - bidichk 11 | - bodyclose 12 | - containedctx 13 | - contextcheck 14 | - cyclop 15 | - decorder 16 | - dogsled 17 | - dupl 18 | - dupword 19 | - durationcheck 20 | - errcheck 21 | - errchkjson 22 | - errname 23 | - errorlint 24 | - execinquery 25 | - exhaustive 26 | - exportloopref 27 | - forcetypeassert 28 | - gci 29 | - ginkgolinter 30 | - gocheckcompilerdirectives 31 | - gochecknoglobals 32 | - gochecknoinits 33 | - gocognit 34 | - goconst 35 | - gocritic 36 | - gocyclo 37 | - godot 38 | - godox 39 | - goerr113 40 | - gofmt 41 | - gofumpt 42 | - goheader 43 | - goimports 44 | - gomnd 45 | - gomoddirectives 46 | - gomodguard 47 | - goprintffuncname 48 | - gosec 49 | - gosimple 50 | - gosmopolitan 51 | - govet 52 | - grouper 53 | - importas 54 | - ineffassign 55 | - interfacebloat 56 | - ireturn 57 | - loggercheck 58 | - maintidx 59 | - makezero 60 | - mirror 61 | - misspell 62 | - musttag 63 | - nakedret 64 | - nestif 65 | - nilerr 66 | - nilnil 67 | - nlreturn 68 | - noctx 69 | - nolintlint 70 | - nonamedreturns 71 | - nosprintfhostport 72 | - paralleltest 73 | - prealloc 74 | - predeclared 75 | - promlinter 76 | - reassign 77 | - revive 78 | - rowserrcheck 79 | - sqlclosecheck 80 | - staticcheck 81 | - stylecheck 82 | - tagalign 83 | - tagliatelle 84 | - tenv 85 | - testableexamples 86 | - testpackage 87 | - thelper 88 | - tparallel 89 | - unconvert 90 | - unparam 91 | - unused 92 | - usestdlibvars 93 | - varnamelen 94 | - wastedassign 95 | - whitespace 96 | - wrapcheck 97 | - zerologlint 98 | # - depguard 99 | # - exhaustruct 100 | # - forbidigo 101 | # - funlen 102 | # - lll 103 | # - wsl 104 | linters-settings: 105 | errcheck: 106 | check-type-assertions: true 107 | check-blank: true 108 | gocritic: 109 | enabled-checks: 110 | - appendAssign 111 | - appendCombine 112 | - argOrder 113 | - assignOp 114 | - badCall 115 | - badCond 116 | - badLock 117 | - badRegexp 118 | - badSorting 119 | - boolExprSimplify 120 | - builtinShadow 121 | - builtinShadowDecl 122 | - captLocal 123 | - caseOrder 124 | - codegenComment 125 | - commentFormatting 126 | - commentedOutCode 127 | - commentedOutImport 128 | - defaultCaseOrder 129 | - deferInLoop 130 | - deferUnlambda 131 | - deprecatedComment 132 | - docStub 133 | - dupArg 134 | - dupBranchBody 135 | - dupCase 136 | - dupImport 137 | - dupSubExpr 138 | - dynamicFmtString 139 | - elseif 140 | - emptyDecl 141 | - emptyFallthrough 142 | - emptyStringTest 143 | - equalFold 144 | - evalOrder 145 | - exitAfterDefer 146 | - exposedSyncMutex 147 | - externalErrorReassign 148 | - filepathJoin 149 | - flagDeref 150 | - flagName 151 | - hexLiteral 152 | - httpNoBody 153 | - hugeParam 154 | - ifElseChain 155 | - importShadow 156 | - indexAlloc 157 | - initClause 158 | - mapKey 159 | - methodExprCall 160 | - nestingReduce 161 | - newDeref 162 | - nilValReturn 163 | - octalLiteral 164 | - offBy1 165 | - paramTypeCombine 166 | - preferDecodeRune 167 | - preferFilepathJoin 168 | - preferFprint 169 | - preferStringWriter 170 | - preferWriteByte 171 | - ptrToRefParam 172 | - rangeExprCopy 173 | - rangeValCopy 174 | - redundantSprint 175 | - regexpMust 176 | - regexpPattern 177 | - regexpSimplify 178 | - returnAfterHttpError 179 | - ruleguard 180 | - singleCaseSwitch 181 | - sliceClear 182 | - sloppyLen 183 | - sloppyReassign 184 | - sloppyTestFuncName 185 | - sloppyTypeAssert 186 | - sortSlice 187 | - sprintfQuotedString 188 | - sqlQuery 189 | - stringConcatSimplify 190 | - stringXbytes 191 | - stringsCompare 192 | - switchTrue 193 | - syncMapLoadAndDelete 194 | - timeCmpSimplify 195 | - timeExprSimplify 196 | - todoCommentWithoutDetail 197 | - tooManyResultsChecker 198 | - truncateCmp 199 | - typeAssertChain 200 | - typeDefFirst 201 | - typeSwitchVar 202 | - typeUnparen 203 | - uncheckedInlineErr 204 | - underef 205 | - unlabelStmt 206 | - unlambda 207 | - unnamedResult 208 | - unnecessaryBlock 209 | - unnecessaryDefer 210 | - unslice 211 | - valSwap 212 | - weakCond 213 | - whyNoLint 214 | - wrapperFunc 215 | - yodaStyleExpr 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sascha Grunert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO ?= go 2 | 3 | BUILD_PATH := $(shell pwd)/build 4 | BUILD_BIN_PATH := ${BUILD_PATH}/bin 5 | GOLANGCI_LINT := ${BUILD_BIN_PATH}/golangci-lint 6 | 7 | ${GOLANGCI_LINT}: 8 | export \ 9 | VERSION=v1.54.2 \ 10 | URL=https://raw.githubusercontent.com/golangci/golangci-lint \ 11 | BINDIR=${BUILD_BIN_PATH} && \ 12 | curl -sfL $$URL/$$VERSION/install.sh | sh -s $$VERSION 13 | 14 | .PHONY: lint 15 | lint: ${GOLANGCI_LINT} 16 | GL_DEBUG=gocritic ${GOLANGCI_LINT} run 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ccli 🌈 2 | 3 | [![Build Status](https://travis-ci.org/saschagrunert/ccli.svg)](https://travis-ci.org/saschagrunert/ccli) [![godoc ccli](https://img.shields.io/badge/godoc-ccli-blue.svg)](https://godoc.org/gopkg.in/saschagrunert/ccli.v1) [![godoc ccli](https://img.shields.io/badge/gopkg-ccli-green.svg)](http://gopkg.in/saschagrunert/ccli.v1) 4 | 5 | ## Command line parsing in go, with coloring support 6 | 7 | This project uses the already existing go package [cli](https://github.com/urfave/cli) and adds additional coloring 8 | support to it. Some strong defaults are provided as well. 9 | 10 | ![screenshot](.github/screenshot.png) 11 | 12 | ## Usage 13 | 14 | Install the package with: 15 | 16 | ```shell 17 | go get github.com/saschagrunert/ccli 18 | ``` 19 | 20 | Afterwards it can be used like the `cli` package: 21 | 22 | ```go 23 | package main 24 | 25 | import ( 26 | "fmt" 27 | "os" 28 | "time" 29 | 30 | "github.com/saschagrunert/ccli" 31 | "github.com/urfave/cli/v2" 32 | ) 33 | 34 | func main() { 35 | app := ccli.NewApp() 36 | app.Name = "AppName" 37 | app.Usage = "App usage..." 38 | app.Version = "0.1.0" 39 | app.Description = "Application description" 40 | app.Copyright = fmt.Sprintf("© %d Some Company", time.Now().Year()) 41 | app.Authors = []cli.Author{{Name: "Name", Email: "e@mail.com"}} 42 | app.Flags = []cli.Flag{ 43 | cli.StringFlag{ 44 | Name: "lang", 45 | Value: "english", 46 | Usage: "language for the greeting", 47 | }, 48 | } 49 | app.Action = func(c *cli.Context) error { 50 | fmt.Println("boom! I say!") 51 | return nil 52 | } 53 | if err := app.Run(os.Args); err != nil { 54 | os.Exit(1) 55 | } 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /ccli.go: -------------------------------------------------------------------------------- 1 | package ccli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/fatih/color" 7 | "github.com/urfave/cli/v2" 8 | ) 9 | 10 | // NewApp creates a new applications with the given settings. 11 | func NewApp() *cli.App { 12 | app := cli.NewApp() 13 | app.Writer = color.Output 14 | setAppTemplates() 15 | 16 | return app 17 | } 18 | 19 | func setAppTemplates() { 20 | // Set the colors 21 | blue := color.New(color.FgBlue).SprintFunc() 22 | cyan := color.New(color.FgCyan).SprintFunc() 23 | green := color.New(color.FgGreen).SprintFunc() 24 | red := color.New(color.FgRed).SprintFunc() 25 | yellow := color.New(color.FgYellow).SprintFunc() 26 | 27 | // Set the application help template 28 | cli.AppHelpTemplate = fmt.Sprintf(`%s {{if .Version}}{{if not .HideVersion}}{{.Version}}{{end}}{{end}} 29 | {{if .Usage}}{{.Usage}}{{end}} 30 | 31 | %s 32 | %s {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} 33 | 34 | %s 35 | {{.Description}}{{end}}{{if len .Authors}} 36 | 37 | %s{{with $length := len .Authors}}{{if ne 1 $length}}%s{{end}}{{end}}%s 38 | {{range $index, $author := .Authors}}{{if $index}} 39 | {{end}}%s{{end}}{{end}}{{if .VisibleCommands}} 40 | 41 | %s{{range .VisibleCategories}}{{if .Name}} 42 | {{.Name}}:{{end}}{{range .VisibleCommands}} 43 | %s{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 44 | 45 | %s 46 | {{range $index, $option := .VisibleFlags}}{{if $index}} 47 | {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} 48 | 49 | %s{{end}} 50 | `, green("{{.Name}}"), 51 | yellow("USAGE:"), 52 | cyan("{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}"), 53 | yellow("DESCRIPTION:"), 54 | yellow("AUTHOR"), 55 | yellow("S"), 56 | yellow(":"), 57 | blue("{{$author}}"), 58 | yellow("COMMANDS:"), 59 | green(`{{join .Names ", "}}`), 60 | yellow("GLOBAL OPTIONS:"), 61 | red("{{.Copyright}}")) 62 | 63 | // Set the command help template 64 | cli.CommandHelpTemplate = fmt.Sprintf(`%s 65 | {{.HelpName}} - {{.Usage}} 66 | 67 | %s 68 | {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}} 69 | 70 | %s 71 | {{.Category}}{{end}}{{if .Description}} 72 | 73 | %s 74 | {{.Description}}{{end}}{{if .VisibleFlags}} 75 | 76 | %s 77 | {{range .VisibleFlags}}{{.}} 78 | {{end}}{{end}} 79 | `, yellow("NAME:"), 80 | yellow("USAGE:"), 81 | yellow("CATEGORY:"), 82 | yellow("DESCRIPTION:"), 83 | yellow("OPTIONS:")) 84 | 85 | // Set the subcommand help template 86 | cli.SubcommandHelpTemplate = fmt.Sprintf(`%s 87 | {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} 88 | 89 | %s 90 | {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} 91 | 92 | %s{{range .VisibleCategories}}{{if .Name}} 93 | {{.Name}}:{{end}}{{range .VisibleCommands}} 94 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} 95 | {{end}}{{if .VisibleFlags}} 96 | %s 97 | {{range .VisibleFlags}}{{.}} 98 | {{end}}{{end}} 99 | `, yellow("NAME:"), 100 | yellow("USAGE:"), 101 | yellow("COMMANDS:"), 102 | yellow("OPTIONS:")) 103 | } 104 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/saschagrunert/ccli/v2" 9 | "github.com/urfave/cli/v2" 10 | ) 11 | 12 | func main() { 13 | app := ccli.NewApp() 14 | app.Name = "AppName" 15 | app.Usage = "App usage..." 16 | app.Version = "0.1.0" 17 | app.Description = "Application description" 18 | app.Copyright = fmt.Sprintf("© %d Some Company", time.Now().Year()) 19 | app.Authors = []*cli.Author{{Name: "Name", Email: "e@mail.com"}} 20 | app.Flags = []cli.Flag{ 21 | &cli.StringFlag{ 22 | Name: "lang", 23 | Value: "english", 24 | Usage: "language for the greeting", 25 | }, 26 | } 27 | app.Action = func(c *cli.Context) error { 28 | fmt.Println("boom! I say!") 29 | 30 | return nil 31 | } 32 | if err := app.Run(os.Args); err != nil { 33 | os.Exit(1) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/saschagrunert/ccli/v2 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/fatih/color v1.18.0 7 | github.com/urfave/cli/v2 v2.27.6 8 | ) 9 | 10 | require ( 11 | github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect 12 | github.com/mattn/go-colorable v0.1.13 // indirect 13 | github.com/mattn/go-isatty v0.0.20 // indirect 14 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 15 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 16 | golang.org/x/sys v0.25.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 3 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 4 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 5 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 6 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 7 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 8 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 9 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 10 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 11 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 12 | github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= 13 | github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= 14 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= 15 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= 16 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 17 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 18 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 19 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 20 | --------------------------------------------------------------------------------