diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5214412..f662725 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,13 @@ on: branches: [master, develop] pull_request: branches: [master] + workflow_dispatch: + inputs: + force_run: + description: 'Force workflow run' + required: true + type: choice + options: [yes, no] permissions: actions: read @@ -15,9 +22,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - SRC_DIR: src/github.com/${{ github.repository }} - jobs: Go: name: Go @@ -25,29 +29,33 @@ jobs: strategy: matrix: - go: [ '1.19.x', '1.20.x' ] + go: [ '1.21.x', '1.22.x' ] steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - - name: Checkout - uses: actions/checkout@v3 - with: - path: ${{env.SRC_DIR}} - - name: Download dependencies - working-directory: ${{env.SRC_DIR}} run: make deps - name: Build binary - working-directory: ${{env.SRC_DIR}} run: make all - - name: Do tests - working-directory: ${{env.SRC_DIR}} - run: | - ./knfgen testdata/test1.source | sha1sum | grep -q 'dbfffea813d04de4d838f1877b54fc53cee73616' - ./knfgen testdata/test2.source | sha1sum | grep -q 'dbfffea813d04de4d838f1877b54fc53cee73616' + Typos: + name: Typos + runs-on: ubuntu-latest + + needs: Go + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check spelling + continue-on-error: true + uses: crate-ci/typos@master diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bf5ecc9..bfc4df5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,14 +20,14 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: go - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..55aead8 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,2 @@ +[files] +extend-exclude = ["go.sum"] diff --git a/README.md b/README.md index cb63108..e351de0 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ #### From source -To build the `knfgen` from scratch, make sure you have a working Go 1.19+ workspace ([instructions](https://golang.org/doc/install)), then: +To build the `knfgen` from scratch, make sure you have a working Go 1.21+ workspace ([instructions](https://go.dev/doc/install)), then: ``` -go install github.com/essentialkaos/knfgen +go install github.com/essentialkaos/knfgen@latest ``` #### Prebuilt binaries @@ -41,7 +41,8 @@ Usage: knfgen {options} file Options - --separators, -s Add new lines between sections + --separators, -S Add new lines between sections + --united, -U Generate code for united configuration --no-color, -nc Disable colors in output --help, -h Show this help message --version, -v Show version diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..5b47d11 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,317 @@ +package cli + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "os" + "strconv" + "strings" + + "github.com/essentialkaos/ek/v12/fmtc" + "github.com/essentialkaos/ek/v12/knf" + "github.com/essentialkaos/ek/v12/mathutil" + "github.com/essentialkaos/ek/v12/options" + "github.com/essentialkaos/ek/v12/pager" + "github.com/essentialkaos/ek/v12/terminal/tty" + "github.com/essentialkaos/ek/v12/usage" + "github.com/essentialkaos/ek/v12/usage/completion/bash" + "github.com/essentialkaos/ek/v12/usage/completion/fish" + "github.com/essentialkaos/ek/v12/usage/completion/zsh" + "github.com/essentialkaos/ek/v12/usage/man" + "github.com/essentialkaos/ek/v12/usage/update" + + "github.com/essentialkaos/knfgen/support" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +const ( + APP = "knfgen" + VER = "1.0.0" + DESC = "Utility for generating Golang const code for KNF configuration files" +) + +const ( + OPT_SEPARATORS = "S:separators" + OPT_UNITED = "U:united" + OPT_NO_COLOR = "nc:no-color" + OPT_HELP = "h:help" + OPT_VER = "v:version" + + OPT_VERB_VER = "vv:verbose-version" + OPT_COMPLETION = "completion" + OPT_GENERATE_MAN = "generate-man" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +var optMap = options.Map{ + OPT_SEPARATORS: {Type: options.BOOL}, + OPT_UNITED: {Type: options.BOOL}, + OPT_NO_COLOR: {Type: options.BOOL}, + OPT_HELP: {Type: options.BOOL}, + OPT_VER: {Type: options.MIXED}, + + OPT_VERB_VER: {Type: options.BOOL}, + OPT_COMPLETION: {}, + OPT_GENERATE_MAN: {Type: options.BOOL}, +} + +// color tags for app name and version +var colorTagApp, colorTagVer string + +// tabSymbol contains tab symbol +var tabSymbol = "\t" + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Run is main utility function +func Run(gitRev string, gomod []byte) { + preConfigureUI() + + args, errs := options.Parse(optMap) + + if len(errs) != 0 { + for _, err := range errs { + printError(err.Error()) + } + + os.Exit(1) + } + + configureUI() + + switch { + case options.Has(OPT_COMPLETION): + os.Exit(printCompletion()) + case options.Has(OPT_GENERATE_MAN): + printMan() + os.Exit(0) + case options.GetB(OPT_VER): + genAbout(gitRev).Print(options.GetS(OPT_VER)) + os.Exit(0) + case options.GetB(OPT_VERB_VER): + support.Print(APP, VER, gitRev, gomod) + os.Exit(0) + case options.GetB(OPT_HELP) || len(args) == 0: + genUsage().Print() + os.Exit(0) + } + + process(args.Get(0).Clean().String()) +} + +// preConfigureUI preconfigures UI based on information about user terminal +func preConfigureUI() { + if !tty.IsTTY() { + fmtc.DisableColors = true + } + + switch { + case fmtc.IsTrueColorSupported(): + colorTagApp, colorTagVer = "{*}{#784FFF}", "{#784FFF}" + case fmtc.Is256ColorsSupported(): + colorTagApp, colorTagVer = "{*}{#99}", "{#99}" + default: + colorTagApp, colorTagVer = "{*}{c}", "{c}" + } +} + +// configureUI configures user interface +func configureUI() { + if options.GetB(OPT_NO_COLOR) { + fmtc.DisableColors = true + } + + isTmux, _ := tty.IsTMUX() + + if isTmux { + tabSymbol = " " + } +} + +// process starts config processing +func process(file string) { + config, err := knf.Read(file) + + if err != nil { + printError(err.Error()) + os.Exit(1) + } + + if pager.Setup() == nil { + defer pager.Complete() + } + + renderConfig(config) + + if options.GetB(OPT_UNITED) { + renderUnitedConfig(config) + } +} + +// renderConfig renders config data +func renderConfig(config *knf.Config) { + var maxPropSize int + + for _, section := range config.Sections() { + for _, prop := range config.Props(section) { + maxPropSize = mathutil.Max(maxPropSize, len(formatConstName(section, prop))) + } + } + + formatString := getFormatString(maxPropSize) + sectionsTotal := len(config.Sections()) + + printSeparator() + + fmtc.Println("{*}const{!} (") + + for sectionIndex, section := range config.Sections() { + for _, prop := range config.Props(section) { + fmtc.Printf( + formatString, + formatConstName(section, prop), + section, prop, + ) + } + + if options.GetB(OPT_SEPARATORS) && sectionIndex < sectionsTotal-1 { + fmtc.NewLine() + } + } + + fmt.Println(")") + + printSeparator() +} + +func renderUnitedConfig(config *knf.Config) { + fmtc.Println(`{s-}// addExtraOptions adds extra options{!}`) + fmtc.Println(`{*}func{!} {b}addExtraOptions{!}({&}m{!} {*}options.Map{!}) {`) + + for _, section := range config.Sections() { + for _, prop := range config.Props(section) { + fmtc.Printf( + tabSymbol+"m.{r*}Set{!}({r*}knfu.O{!}(%s), &options.{*}V{!}{s}{}{!})\n", + formatConstName(section, prop), + ) + } + } + + fmt.Println("}\n") + + fmtc.Println(`{s-}// combineConfigs combines knf configuration with options and environment variables{!}`) + fmtc.Println(`{*}func{!} {b}combineConfigs{!}() {`) + fmtc.Println(tabSymbol + "knfu.{r*}Combine{!}(") + + for _, section := range config.Sections() { + for _, prop := range config.Props(section) { + fmtc.Printf( + tabSymbol+tabSymbol+"knfu.{r*}Simple{!}(%s),\n", + formatConstName(section, prop), + ) + } + } + + fmt.Println(tabSymbol + ")") + fmt.Println("}") + + printSeparator() +} + +// formatConstName returns const name +func formatConstName(section, prop string) string { + fs := strings.ToUpper(section) + fp := strings.ToUpper(prop) + + fs = strings.ReplaceAll(fs, "-", "_") + fp = strings.ReplaceAll(fp, "-", "_") + + return fs + "_" + fp +} + +// printSeparator prints separator +func printSeparator() { + fmtc.Printf("\n{s-}// %s //{!}\n\n", strings.Repeat("/", 72)) +} + +// getFormatString returns format string +func getFormatString(maxSize int) string { + return tabSymbol + "%-" + strconv.Itoa(maxSize) + "s = {y}\"%s:%s\"{!}\n" +} + +// printError prints error message to console +func printError(f string, a ...interface{}) { + fmtc.Fprintf(os.Stderr, "{r}"+f+"{!}\n", a...) +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// printCompletion prints completion for given shell +func printCompletion() int { + info := genUsage() + + switch options.GetS(OPT_COMPLETION) { + case "bash": + fmt.Print(bash.Generate(info, "atlassian-cloud-backuper")) + case "fish": + fmt.Print(fish.Generate(info, "atlassian-cloud-backuper")) + case "zsh": + fmt.Print(zsh.Generate(info, optMap, "atlassian-cloud-backuper")) + default: + return 1 + } + + return 0 +} + +// printMan prints man page +func printMan() { + fmt.Println(man.Generate(genUsage(), genAbout(""))) +} + +// genUsage generates usage info +func genUsage() *usage.Info { + info := usage.NewInfo("", "file") + + info.AddOption(OPT_SEPARATORS, "Add new lines between sections") + info.AddOption(OPT_UNITED, "Generate code for united configuration") + info.AddOption(OPT_NO_COLOR, "Disable colors in output") + info.AddOption(OPT_HELP, "Show this help message") + info.AddOption(OPT_VER, "Show version") + + info.AddExample("app.knf", "Generate copy-paste code for app.knf") + + return info +} + +// genAbout generates info about version +func genAbout(gitRev string) *usage.About { + about := &usage.About{ + App: APP, + Version: VER, + Desc: DESC, + Year: 2006, + Owner: "ESSENTIAL KAOS", + + AppNameColorTag: colorTagApp, + VersionColorTag: colorTagVer, + DescSeparator: "{s}—{!}", + + License: "Apache License, Version 2.0 ", + UpdateChecker: usage.UpdateChecker{"essentialkaos/knfgen", update.GitHubChecker}, + } + + if gitRev != "" { + about.Build = "git:" + gitRev + } + + return about +} diff --git a/go.mod b/go.mod index 0074ad2..f2d53b7 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,9 @@ module github.com/essentialkaos/knfgen go 1.19 -require github.com/essentialkaos/ek/v12 v12.60.0 +require ( + github.com/essentialkaos/depsy v1.1.0 + github.com/essentialkaos/ek/v12 v12.107.0 +) -require golang.org/x/sys v0.5.0 // indirect +require golang.org/x/sys v0.18.0 // indirect diff --git a/go.sum b/go.sum index 59aeae2..ba221c0 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,10 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/essentialkaos/check v1.3.0 h1:ria+8o22RCLdt2D/1SHQsEH5Mmy5S+iWHaGHrrbPUc0= -github.com/essentialkaos/check v1.3.0/go.mod h1:PhxzfJWlf5L/skuyhzBLIvjMB5Xu9TIyDIsqpY5MvB8= -github.com/essentialkaos/ek/v12 v12.60.0 h1:Z0wGjnSAyJLHkbhlO27E/GfRqNFD11zPotEha7ygOzg= -github.com/essentialkaos/ek/v12 v12.60.0/go.mod h1:QFEIBoGPE5ezTV08JYWlWLL5t8fwcdOe3/e7bhTJNW0= -github.com/essentialkaos/go-linenoise/v3 v3.4.0/go.mod h1:t1kNLY2bSMQCy1JXOefD2BDLs/TTPMtTv3DFNV5uDSI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/essentialkaos/check v1.4.0 h1:kWdFxu9odCxUqo1NNFNJmguGrDHgwi3A8daXX1nkuKk= +github.com/essentialkaos/depsy v1.1.0 h1:U6dp687UkQwXlZU17Hg2KMxbp3nfZAoZ8duaeUFYvJI= +github.com/essentialkaos/depsy v1.1.0/go.mod h1:kpiTAV17dyByVnrbNaMcZt2jRwvuXClUYOzpyJQwtG8= +github.com/essentialkaos/ek/v12 v12.107.0 h1:wNUuFXJRPb6iGJEBDvU8pOV7l3a5yzUj5gt7ZR29vew= +github.com/essentialkaos/ek/v12 v12.107.0/go.mod h1:exBTL3OE3dm4vjHihE4ZhQ3onJq7C8q2r+OTZmpCO6s= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/knfgen.go b/knfgen.go index 49c3233..856fb86 100644 --- a/knfgen.go +++ b/knfgen.go @@ -2,217 +2,27 @@ package main // ////////////////////////////////////////////////////////////////////////////////// // // // -// Copyright (c) 2023 ESSENTIAL KAOS // +// Copyright (c) 2024 ESSENTIAL KAOS // // Apache License, Version 2.0 // // // // ////////////////////////////////////////////////////////////////////////////////// // import ( - "os" - "strconv" - "strings" + _ "embed" - "github.com/essentialkaos/ek/v12/env" - "github.com/essentialkaos/ek/v12/fmtc" - "github.com/essentialkaos/ek/v12/fmtutil" - "github.com/essentialkaos/ek/v12/fsutil" - "github.com/essentialkaos/ek/v12/knf" - "github.com/essentialkaos/ek/v12/mathutil" - "github.com/essentialkaos/ek/v12/options" - "github.com/essentialkaos/ek/v12/usage" - "github.com/essentialkaos/ek/v12/usage/update" + CLI "github.com/essentialkaos/knfgen/cli" ) // ////////////////////////////////////////////////////////////////////////////////// // -const ( - APP = "knfgen" - VER = "0.7.4" - DESC = "Utility for generating Golang const code for KNF configuration files" -) - -const ( - OPT_SEPARATORS = "s:separators" - OPT_NO_COLOR = "nc:no-color" - OPT_HELP = "h:help" - OPT_VER = "v:version" -) - -// ////////////////////////////////////////////////////////////////////////////////// // - -var optMap = options.Map{ - OPT_SEPARATORS: {Type: options.BOOL}, - OPT_NO_COLOR: {Type: options.BOOL}, - OPT_HELP: {Type: options.BOOL, Alias: "u:usage"}, - OPT_VER: {Type: options.BOOL, Alias: "ver"}, -} +//go:embed go.mod +var gomod []byte -var rawOutput = false +// gitrev is short hash of the latest git commit +var gitrev string // ////////////////////////////////////////////////////////////////////////////////// // func main() { - args, errs := options.Parse(optMap) - - if len(errs) != 0 { - for _, err := range errs { - printError(err.Error()) - } - - os.Exit(1) - } - - configureUI() - - if options.GetB(OPT_VER) { - showAbout() - return - } - - if options.GetB(OPT_HELP) || len(args) == 0 { - showUsage() - return - } - - process(args.Get(0).Clean().String()) -} - -// configureUI configures user interface -func configureUI() { - envVars := env.Get() - term := envVars.GetS("TERM") - - fmtc.DisableColors = true - rawOutput = true - - if term != "" { - switch { - case strings.Contains(term, "xterm"), - strings.Contains(term, "color"), - term == "screen": - fmtc.DisableColors = false - rawOutput = false - } - } - - if options.GetB(OPT_NO_COLOR) { - fmtc.DisableColors = true - } - - if !fsutil.IsCharacterDevice("/dev/stdout") && envVars.GetS("FAKETTY") == "" { - fmtc.DisableColors = true - rawOutput = true - } -} - -// process starts config processing -func process(file string) { - config, err := knf.Read(file) - - if err != nil { - if !rawOutput { - printError(err.Error()) - } - - os.Exit(1) - } - - renderConfig(config) -} - -// renderConfig renders config data -func renderConfig(config *knf.Config) { - if !rawOutput { - fmtutil.Separator(false) - } - - var maxPropSize int - - for _, section := range config.Sections() { - for _, prop := range config.Props(section) { - maxPropSize = mathutil.Max(maxPropSize, len(formatConstName(section, prop))) - } - } - - formatString := getFormatString(maxPropSize) - sectionsTotal := len(config.Sections()) - - fmtc.Println("{*}const ({!}") - - for sectionIndex, section := range config.Sections() { - for _, prop := range config.Props(section) { - fmtc.Printf( - formatString, - formatConstName(section, prop), - section, prop, - ) - } - - if options.GetB(OPT_SEPARATORS) && sectionIndex < sectionsTotal-1 { - fmtc.NewLine() - } - } - - fmtc.Println("{*}){!}") - - if !rawOutput { - fmtutil.Separator(false) - } -} - -// formatConstName returns const name -func formatConstName(section, prop string) string { - fs := strings.ToUpper(section) - fp := strings.ToUpper(prop) - - fs = strings.Replace(fs, "-", "_", -1) - fp = strings.Replace(fp, "-", "_", -1) - - return fs + "_" + fp -} - -// getFormatString returns format string -func getFormatString(maxSize int) string { - return "\t%-" + strconv.Itoa(maxSize) + "s = {y}\"%s:%s\"{!}\n" -} - -// printError prints error message to console -func printError(f string, a ...interface{}) { - fmtc.Fprintf(os.Stderr, "{r}"+f+"{!}\n", a...) -} - -// printError prints warning message to console -func printWarn(f string, a ...interface{}) { - fmtc.Fprintf(os.Stderr, "{y}"+f+"{!}\n", a...) -} - -// ////////////////////////////////////////////////////////////////////////////////// // - -// showUsage shows usage info -func showUsage() { - info := usage.NewInfo("", "file") - - info.AddOption(OPT_SEPARATORS, "Add new lines between sections") - info.AddOption(OPT_NO_COLOR, "Disable colors in output") - info.AddOption(OPT_HELP, "Show this help message") - info.AddOption(OPT_VER, "Show version") - - info.AddExample("app.knf", "Generate copy-paste code for app.knf") - - info.Render() -} - -// showAbout shows info about version -func showAbout() { - about := &usage.About{ - App: APP, - Version: VER, - Desc: DESC, - Year: 2006, - Owner: "ESSENTIAL KAOS", - License: "Apache License, Version 2.0 ", - UpdateChecker: usage.UpdateChecker{"essentialkaos/knfgen", update.GitHubChecker}, - } - - about.Render() + CLI.Run(gitrev, gomod) } diff --git a/support/support.go b/support/support.go new file mode 100644 index 0000000..c68b987 --- /dev/null +++ b/support/support.go @@ -0,0 +1,162 @@ +package support + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "os" + "os/exec" + "runtime" + "runtime/debug" + "strings" + + "github.com/essentialkaos/ek/v12/fmtc" + "github.com/essentialkaos/ek/v12/fmtutil" + "github.com/essentialkaos/ek/v12/hash" + "github.com/essentialkaos/ek/v12/strutil" + + "github.com/essentialkaos/depsy" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Print prints verbose info about application, system, dependencies and +// important environment +func Print(app, ver, gitRev string, gomod []byte) { + fmtutil.SeparatorTitleColorTag = "{s-}" + fmtutil.SeparatorFullscreen = false + fmtutil.SeparatorColorTag = "{s-}" + fmtutil.SeparatorSize = 80 + + showApplicationInfo(app, ver, gitRev) + showOSInfo() + showEnvInfo() + showDepsInfo(gomod) + + fmtutil.Separator(false) +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// showApplicationInfo shows verbose information about application +func showApplicationInfo(app, ver, gitRev string) { + fmtutil.Separator(false, "APPLICATION INFO") + + printInfo(7, "Name", app) + printInfo(7, "Version", ver) + + printInfo(7, "Go", fmtc.Sprintf( + "%s {s}(%s/%s){!}", + strings.TrimLeft(runtime.Version(), "go"), + runtime.GOOS, runtime.GOARCH, + )) + + if gitRev == "" { + gitRev = extractGitRevFromBuildInfo() + } + + if gitRev != "" { + if !fmtc.DisableColors && fmtc.IsTrueColorSupported() { + printInfo(7, "Git SHA", gitRev+getHashColorBullet(gitRev)) + } else { + printInfo(7, "Git SHA", gitRev) + } + } + + bin, _ := os.Executable() + binSHA := hash.FileHash(bin) + + if binSHA != "" { + binSHA = strutil.Head(binSHA, 7) + if !fmtc.DisableColors && fmtc.IsTrueColorSupported() { + printInfo(7, "Bin SHA", binSHA+getHashColorBullet(binSHA)) + } else { + printInfo(7, "Bin SHA", binSHA) + } + } +} + +// showEnvInfo shows info about environment +func showEnvInfo() { + fmtutil.Separator(false, "ENVIRONMENT") + + cmd := exec.Command("go", "version") + out, err := cmd.Output() + + if err != nil { + printInfo(2, "Go", "") + return + } + + goVer := string(out) + goVer = strutil.ReadField(goVer, 2, false, ' ') + goVer = strutil.Exclude(goVer, "go") + + printInfo(2, "Go", goVer) +} + +// showDepsInfo shows information about all dependencies +func showDepsInfo(gomod []byte) { + deps := depsy.Extract(gomod, false) + + if len(deps) == 0 { + return + } + + fmtutil.Separator(false, "DEPENDENCIES") + + for _, dep := range deps { + if dep.Extra == "" { + fmtc.Printf(" {s}%8s{!} %s\n", dep.Version, dep.Path) + } else { + fmtc.Printf(" {s}%8s{!} %s {s-}(%s){!}\n", dep.Version, dep.Path, dep.Extra) + } + } +} + +// extractGitRevFromBuildInfo extracts git SHA from embedded build info +func extractGitRevFromBuildInfo() string { + info, ok := debug.ReadBuildInfo() + + if !ok { + return "" + } + + for _, s := range info.Settings { + if s.Key == "vcs.revision" && len(s.Value) > 7 { + return s.Value[:7] + } + } + + return "" +} + +// getHashColorBullet return bullet with color from hash +func getHashColorBullet(v string) string { + if len(v) > 6 { + v = strutil.Head(v, 6) + } + + return fmtc.Sprintf(" {#" + strutil.Head(v, 6) + "}● {!}") +} + +// printInfo formats and prints info record +func printInfo(size int, name, value string) { + name += ":" + size++ + + if value == "" { + fm := fmt.Sprintf(" {*}%%-%ds{!} {s-}—{!}\n", size) + fmtc.Printf(fm, name) + } else { + fm := fmt.Sprintf(" {*}%%-%ds{!} %%s\n", size) + fmtc.Printf(fm, name, value) + } +} + +// ////////////////////////////////////////////////////////////////////////////////// // diff --git a/support/support_darwin.go b/support/support_darwin.go new file mode 100644 index 0000000..4647acb --- /dev/null +++ b/support/support_darwin.go @@ -0,0 +1,40 @@ +package support + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "github.com/essentialkaos/ek/v12/fmtutil" + "github.com/essentialkaos/ek/v12/system" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// showOSInfo shows verbose information about system +func showOSInfo() { + systemInfo, err := system.GetSystemInfo() + + if err != nil { + return + } + + osInfo, err := system.GetOSInfo() + + if err == nil { + fmtutil.Separator(false, "OS INFO") + + printInfo(12, "Name", osInfo.Name) + printInfo(12, "Version", osInfo.VersionID) + printInfo(12, "Build", osInfo.Build) + } + + fmtutil.Separator(false, "SYSTEM INFO") + + printInfo(7, "Name", systemInfo.OS) + printInfo(7, "Arch", systemInfo.Arch) + printInfo(7, "Kernel", systemInfo.Kernel) +} diff --git a/support/support_linux.go b/support/support_linux.go new file mode 100644 index 0000000..e1f5719 --- /dev/null +++ b/support/support_linux.go @@ -0,0 +1,63 @@ +package support + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "github.com/essentialkaos/ek/v12/fmtc" + "github.com/essentialkaos/ek/v12/fmtutil" + "github.com/essentialkaos/ek/v12/system" + "github.com/essentialkaos/ek/v12/system/container" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// showOSInfo shows verbose information about system +func showOSInfo() { + osInfo, err := system.GetOSInfo() + + if err == nil { + fmtutil.Separator(false, "OS INFO") + + printInfo(12, "Name", osInfo.ColoredName()) + printInfo(12, "Pretty Name", osInfo.ColoredPrettyName()) + printInfo(12, "Version", osInfo.Version) + printInfo(12, "ID", osInfo.ID) + printInfo(12, "ID Like", osInfo.IDLike) + printInfo(12, "Version ID", osInfo.VersionID) + printInfo(12, "Version Code", osInfo.VersionCodename) + printInfo(12, "Platform ID", osInfo.PlatformID) + printInfo(12, "CPE", osInfo.CPEName) + } + + systemInfo, err := system.GetSystemInfo() + + if err != nil { + return + } else if osInfo == nil { + fmtutil.Separator(false, "SYSTEM INFO") + printInfo(12, "Name", systemInfo.OS) + } + + printInfo(12, "Arch", systemInfo.Arch) + printInfo(12, "Kernel", systemInfo.Kernel) + + containerEngine := "No" + + switch container.GetEngine() { + case container.DOCKER: + containerEngine = "Yes (Docker)" + case container.PODMAN: + containerEngine = "Yes (Podman)" + case container.LXC: + containerEngine = "Yes (LXC)" + } + + fmtc.NewLine() + + printInfo(12, "Container", containerEngine) +} diff --git a/testdata/test1.source b/testdata/test1.source index 5abeec4..2e20e8d 100644 --- a/testdata/test1.source +++ b/testdata/test1.source @@ -2,7 +2,7 @@ [main] - # Path to writable temorary directory + # Path to writable temporary directory tmp-dir: /tmp [storage] diff --git a/testdata/test2.source b/testdata/test2.source index c34b6da..87189ac 100644 --- a/testdata/test2.source +++ b/testdata/test2.source @@ -2,7 +2,7 @@ [main] - # Path to writable temorary directory + # Path to writable temporary directory tmp-dir: /tmp [storage]