diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc6f8db..f662725 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,22 @@ on: branches: [master, develop] pull_request: branches: [master] - -env: - SRC_DIR: src/github.com/${{ github.repository }} + workflow_dispatch: + inputs: + force_run: + description: 'Force workflow run' + required: true + type: choice + options: [yes, no] + +permissions: + actions: read + contents: read + statuses: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: Go: @@ -16,23 +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 + + 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/Makefile b/Makefile index 8f32113..87011c8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ ################################################################################ -# This Makefile generated by GoMakeGen 2.2.0 using next command: +# This Makefile generated by GoMakeGen 2.3.0 using next command: # gomakegen --mod . # # More info: https://kaos.sh/gomakegen @@ -13,6 +13,7 @@ ifdef VERBOSE ## Print verbose information (Flag) VERBOSE_FLAG = -v endif +COMPAT ?= 1.18 MAKEDIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) GITREV ?= $(shell test -s $(MAKEDIR)/.git && git rev-parse --short HEAD) @@ -50,7 +51,7 @@ else endif ifdef COMPAT ## Compatible Go version (String) - go mod tidy $(VERBOSE_FLAG) -compat=$(COMPAT) + go mod tidy $(VERBOSE_FLAG) -compat=$(COMPAT) -go=$(COMPAT) else go mod tidy $(VERBOSE_FLAG) endif @@ -94,6 +95,6 @@ help: ## Show this info | sed 's/ifdef //' \ | awk 'BEGIN {FS = " .*?## "}; {printf " \033[32m%-14s\033[0m %s\n", $$1, $$2}' @echo -e '' - @echo -e '\033[90mGenerated by GoMakeGen 2.2.0\033[0m\n' + @echo -e '\033[90mGenerated by GoMakeGen 2.3.0\033[0m\n' ################################################################################ diff --git a/README.md b/README.md index 97cd22e..753866c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ #### From source -To build the MDToc from scratch, make sure you have a working Go 1.18+ workspace ([instructions](https://golang.org/doc/install)), then: +To build the MDToc from scratch, make sure you have a working Go 1.18+ workspace ([instructions](https://go.dev/doc/install)), then: ``` go install github.com/essentialkaos/mdtoc diff --git a/cli/cli.go b/cli/cli.go index f67be37..ae29a94 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -20,14 +20,15 @@ import ( "github.com/essentialkaos/ek/v12/mathutil" "github.com/essentialkaos/ek/v12/options" "github.com/essentialkaos/ek/v12/strutil" + "github.com/essentialkaos/ek/v12/support" + "github.com/essentialkaos/ek/v12/support/deps" + "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/mdtoc/cli/support" ) // ////////////////////////////////////////////////////////////////////////////////// // @@ -35,7 +36,7 @@ import ( // App info const ( APP = "MDToc" - VER = "1.2.5" + VER = "1.2.6" DESC = "Utility for generating table of contents for markdown files" ) @@ -72,7 +73,7 @@ var optMap = options.Map{ OPT_HTML: {Type: options.BOOL}, OPT_NO_COLOR: {Type: options.BOOL}, OPT_HELP: {Type: options.BOOL}, - OPT_VER: {Type: options.BOOL}, + OPT_VER: {Type: options.MIXED}, OPT_VERB_VER: {Type: options.BOOL}, OPT_COMPLETION: {}, @@ -86,6 +87,8 @@ var badgeRegExp = regexp.MustCompile(`\[!\[[^\]]*\]\((.*?)\s*("(?:.*[^"])")?\s*\ // Init is main function func Init(gitRev string, gomod []byte) { + preConfigureUI() + args, errs := options.Parse(optMap) if len(errs) != 0 { @@ -100,17 +103,21 @@ func Init(gitRev string, gomod []byte) { switch { case options.Has(OPT_COMPLETION): - os.Exit(genCompletion()) + os.Exit(printCompletion()) case options.Has(OPT_GENERATE_MAN): - os.Exit(genMan()) + printMan() + os.Exit(0) case options.GetB(OPT_VER): - showAbout(gitRev) + genAbout(gitRev).Print(options.GetS(OPT_VER)) os.Exit(0) case options.GetB(OPT_VERB_VER): - support.ShowSupportInfo(APP, VER, gitRev, gomod) + support.Collect(APP, VER). + WithRevision(gitRev). + WithDeps(deps.Extract(gomod)). + Print() os.Exit(0) case options.GetB(OPT_HELP): - showUsage() + genUsage().Print() os.Exit(0) } @@ -120,7 +127,7 @@ func Init(gitRev string, gomod []byte) { file = findProperReadme() if file == "" { - showUsage() + genUsage().Print() os.Exit(0) } } else { @@ -131,6 +138,13 @@ func Init(gitRev string, gomod []byte) { process(file) } +// preConfigureUI preconfigures UI based on information about user terminal +func preConfigureUI() { + if !tty.IsTTY() { + fmtc.DisableColors = true + } +} + // configureUI configures user interface func configureUI() { if options.GetB(OPT_NO_COLOR) { @@ -150,20 +164,10 @@ func findProperReadme() string { // checkFile checks markdown file before processing func checkFile(file string) { - if !fsutil.IsExist(file) { - printErrorAndExit("Can't read file %s - file does not exist", file) - } - - if !fsutil.IsRegular(file) { - printErrorAndExit("Can't read file %s - is not a file", file) - } + err := fsutil.ValidatePerms("FRS", file) - if !fsutil.IsReadable(file) { - printErrorAndExit("Can't read file %s - file is not readable", file) - } - - if !fsutil.IsNonEmpty(file) { - printErrorAndExit("Can't read file %s - file is empty", file) + if err != nil { + printErrorAndExit(err.Error()) } } @@ -316,7 +320,7 @@ func makeLink(text string) string { result := text result = strings.TrimLeft(result, "# ") - result = strings.Replace(result, " ", "-", -1) + result = strings.ReplaceAll(result, " ", "-") result = strings.ToLower(result) result = strings.Join(anchorRegExp.FindAllString(result, -1), "") @@ -337,7 +341,7 @@ func parseHeaderText(text string) (string, int) { func removeMarkdownTags(header string) string { for _, r := range "`_*~" { if strings.Count(header, string(r))%2 == 0 { - header = strings.Replace(header, string(r), "", -1) + header = strings.ReplaceAll(header, string(r), "") } } @@ -379,7 +383,7 @@ func printWarn(f string, a ...interface{}) { fmtc.Fprintf(os.Stderr, "{y}"+f+"{!}\n", a...) } -// printErrorAndExit prints error mesage and exit with exit code 1 +// printErrorAndExit prints error message and exit with exit code 1 func printErrorAndExit(f string, a ...interface{}) { printError(f, a...) os.Exit(1) @@ -387,27 +391,17 @@ func printErrorAndExit(f string, a ...interface{}) { // ////////////////////////////////////////////////////////////////////////////////// // -// showUsage prints usage info -func showUsage() { - genUsage().Render() -} - -// showAbout prints info about version -func showAbout(gitRev string) { - genAbout(gitRev).Render() -} - -// genCompletion generates completion for different shells -func genCompletion() int { +// printCompletion prints completion for given shell +func printCompletion() int { info := genUsage() switch options.GetS(OPT_COMPLETION) { case "bash": - fmt.Printf(bash.Generate(info, "mdtoc")) + fmt.Print(bash.Generate(info, "mdtoc")) case "fish": - fmt.Printf(fish.Generate(info, "mdtoc")) + fmt.Print(fish.Generate(info, "mdtoc")) case "zsh": - fmt.Printf(zsh.Generate(info, optMap, "mdtoc")) + fmt.Print(zsh.Generate(info, optMap, "mdtoc")) default: return 1 } @@ -415,16 +409,14 @@ func genCompletion() int { return 0 } -// genMan generates man page -func genMan() int { +// printMan prints man page +func printMan() { fmt.Println( man.Generate( genUsage(), genAbout(""), ), ) - - return 0 } // genUsage generates usage info @@ -448,17 +440,17 @@ func genUsage() *usage.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", - License: "Apache License, Version 2.0 ", - UpdateChecker: usage.UpdateChecker{"essentialkaos/mdtoc", update.GitHubChecker}, + App: APP, + Version: VER, + Desc: DESC, + Year: 2006, + Owner: "ESSENTIAL KAOS", + License: "Apache License, Version 2.0 ", } if gitRev != "" { about.Build = "git:" + gitRev + about.UpdateChecker = usage.UpdateChecker{"essentialkaos/mdtoc", update.GitHubChecker} } return about diff --git a/cli/support/support.go b/cli/support/support.go deleted file mode 100644 index 6fb1347..0000000 --- a/cli/support/support.go +++ /dev/null @@ -1,166 +0,0 @@ -package support - -// ////////////////////////////////////////////////////////////////////////////////// // -// // -// Copyright (c) 2023 ESSENTIAL KAOS // -// Apache License, Version 2.0 // -// // -// ////////////////////////////////////////////////////////////////////////////////// // - -import ( - "fmt" - "os" - "runtime" - "strings" - - "github.com/essentialkaos/ek/v12/fmtc" - "github.com/essentialkaos/ek/v12/fmtutil" - "github.com/essentialkaos/ek/v12/fsutil" - "github.com/essentialkaos/ek/v12/hash" - "github.com/essentialkaos/ek/v12/strutil" - "github.com/essentialkaos/ek/v12/system" - - "github.com/essentialkaos/depsy" -) - -// ////////////////////////////////////////////////////////////////////////////////// // - -// ShowSupportInfo prints verbose info about application, system, dependencies and -// important environment -func ShowSupportInfo(app, ver, gitRev string, gomod []byte) { - fmtutil.SeparatorTitleColorTag = "{s-}" - fmtutil.SeparatorFullscreen = false - fmtutil.SeparatorColorTag = "{s-}" - fmtutil.SeparatorSize = 80 - - showApplicationInfo(app, ver, gitRev) - showOSInfo() - 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 != "" { - 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) - } - } -} - -// showOSInfo shows verbose information about system -func showOSInfo() { - osInfo, err := system.GetOSInfo() - - if err == nil { - fmtutil.Separator(false, "OS INFO") - - printInfo(12, "Name", osInfo.Name) - printInfo(12, "Pretty Name", osInfo.PrettyName) - printInfo(12, "Version", osInfo.VersionID) - 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, "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 { - case fsutil.IsExist("/.dockerenv"): - containerEngine = "Yes (Docker)" - case fsutil.IsExist("/run/.containerenv"): - containerEngine = "Yes (Podman)" - } - - fmtc.NewLine() - - printInfo(12, "Container", containerEngine) -} - -// 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) - } - } -} - -// 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 = 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/go.mod b/go.mod index cf76efe..0be64d3 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/essentialkaos/mdtoc -go 1.17 +go 1.18 + +require github.com/essentialkaos/ek/v12 v12.113.1 require ( - github.com/essentialkaos/depsy v1.0.0 - github.com/essentialkaos/ek/v12 v12.60.0 + github.com/essentialkaos/depsy v1.1.0 // indirect + golang.org/x/sys v0.18.0 // indirect ) - -require golang.org/x/sys v0.5.0 // indirect diff --git a/go.sum b/go.sum index 57bcd24..efceef3 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +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/depsy v1.0.0 h1:FikBtTnNhk+xFO/hFr+CfiKs6QnA3wMD6tGL0XTEUkc= -github.com/essentialkaos/depsy v1.0.0/go.mod h1:XVsB2eVUonEzmLKQP3ig2P6v2+WcHVgJ10zm0JLqFMM= -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.113.1 h1:3opV9dwRpIQq1fqg5mkaSEt6ogECL4VLzrH/829qeYg= +github.com/essentialkaos/ek/v12 v12.113.1/go.mod h1:SslW97Se34YQKc08Ume2V/8h/HPTgLS1+Iok64cNF/U= +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=