diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 93cab7c..1e5b871 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -4,9 +4,7 @@ _Before opening an issue, search for similar bug reports or feature requests on
**System info:**
-* **Version used (`bop -v`):**
-* **OS (e.g. from `/etc/*-release`):**
-* **Kernel (`uname -a`):**
+* **Verbose version info (`bop -vv`):**
* **Install tools:**
**Steps to reproduce:**
diff --git a/README.md b/README.md
index 333aa3a..3232596 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
-Installation • Docker support • Command-line completion • Usage • Build Status • Contributing • License
+Installation • Docker support • Command-line completion • Man documentation • Usage • Build Status • Contributing • License
@@ -64,6 +64,14 @@ Fish:
sudo bop --completion=fish 1> /usr/share/fish/vendor_completions.d/bop.fish
```
+### Man documentation
+
+You can generate man page using next command:
+
+```bash
+bop --generate-man | sudo gzip > /usr/share/man/man1/bop.1.gz
+```
+
### Usage
```
@@ -79,9 +87,14 @@ Options
Examples
- bop redis redis*.rpm
- Generate tests for Redis package
+ bop htop htop*.rpm
+ Generate simple tests for package
+
+ bop redis redis*.rpm -s redis
+ Generate tests with service check
+ bop -o zl.recipe zlib zlib*.rpm minizip*.rpm
+ Generate tests with custom name
```
### Build Status
diff --git a/bop.go b/bop.go
index d6c9a12..427da24 100644
--- a/bop.go
+++ b/bop.go
@@ -5,17 +5,27 @@ package main
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
import (
+ _ "embed"
+
CLI "github.com/essentialkaos/bop/cli"
)
// ////////////////////////////////////////////////////////////////////////////////// //
+//go:embed go.mod
+var gomod []byte
+
+// gitrev is short hash of the latest git commit
+var gitrev string
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+
func main() {
- CLI.Init()
+ CLI.Init(gitrev, gomod)
}
diff --git a/cli/cli.go b/cli/cli.go
index 2568c0f..de66c5a 100644
--- a/cli/cli.go
+++ b/cli/cli.go
@@ -2,7 +2,7 @@ package cli
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
@@ -24,11 +24,13 @@ import (
"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/bop/extractor"
"github.com/essentialkaos/bop/generator"
"github.com/essentialkaos/bop/rpm"
+ "github.com/essentialkaos/bop/support"
)
// ////////////////////////////////////////////////////////////////////////////////// //
@@ -36,7 +38,7 @@ import (
// App info
const (
APP = "bop"
- VER = "1.1.6"
+ VER = "1.2.0"
DESC = "Utility for generating formal bibop tests for RPM packages"
)
@@ -50,7 +52,9 @@ const (
OPT_HELP = "h:help"
OPT_VER = "v:version"
- OPT_COMPLETION = "completion"
+ OPT_VERB_VER = "vv:verbose-version"
+ OPT_COMPLETION = "completion"
+ OPT_GENERATE_MAN = "generate-man"
)
// ////////////////////////////////////////////////////////////////////////////////// //
@@ -62,13 +66,15 @@ var optMap = options.Map{
OPT_HELP: {Type: options.BOOL, Alias: "u:usage"},
OPT_VER: {Type: options.BOOL, Alias: "ver"},
- OPT_COMPLETION: {},
+ OPT_VERB_VER: {Type: options.BOOL},
+ OPT_COMPLETION: {},
+ OPT_GENERATE_MAN: {Type: options.BOOL},
}
// ////////////////////////////////////////////////////////////////////////////////// //
// Init is main function
-func Init() {
+func Init(gitRev string, gomod []byte) {
args, errs := options.Parse(optMap)
if len(errs) != 0 {
@@ -83,16 +89,18 @@ func Init() {
fmtc.DisableColors = true
}
- if options.Has(OPT_COMPLETION) {
- genCompletion()
- }
-
- if options.GetB(OPT_VER) {
- showAbout()
+ switch {
+ case options.Has(OPT_COMPLETION):
+ os.Exit(genCompletion())
+ case options.Has(OPT_GENERATE_MAN):
+ os.Exit(genMan())
+ case options.GetB(OPT_VER):
+ showAbout(gitRev)
return
- }
-
- if options.GetB(OPT_HELP) || len(args) < 2 {
+ case options.GetB(OPT_VERB_VER):
+ support.ShowSupportInfo(APP, VER, gitRev, gomod)
+ return
+ case options.GetB(OPT_HELP) || len(args) < 2:
showUsage()
return
}
@@ -205,23 +213,13 @@ func showUsage() {
genUsage().Render()
}
-// genUsage generates usage info
-func genUsage() *usage.Info {
- info := usage.NewInfo("", "name", "package…")
-
- info.AddOption(OPT_OUTPUT, "Output file", "file")
- info.AddOption(OPT_SERVICE, "List of services for checking {c}(mergable){!}", "service")
- 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("redis redis*.rpm", "Generate tests for Redis package")
-
- return info
+// showAbout prints info about version
+func showAbout(gitRev string) {
+ genAbout(gitRev).Render()
}
// genCompletion generates completion for different shells
-func genCompletion() {
+func genCompletion() int {
info := genUsage()
switch options.GetS(OPT_COMPLETION) {
@@ -232,15 +230,44 @@ func genCompletion() {
case "zsh":
fmt.Printf(zsh.Generate(info, optMap, "bop"))
default:
- os.Exit(1)
+ return 1
}
- os.Exit(0)
+ return 0
}
-// showAbout prints info about version
-func showAbout() {
- about := &usage.About{
+// genMan generates man page
+func genMan() int {
+ fmt.Println(
+ man.Generate(
+ genUsage(),
+ genAbout(""),
+ ),
+ )
+
+ return 0
+}
+
+// genUsage generates usage info
+func genUsage() *usage.Info {
+ info := usage.NewInfo("", "name", "package…")
+
+ info.AddOption(OPT_OUTPUT, "Output file", "file")
+ info.AddOption(OPT_SERVICE, "List of services for checking {c}(mergable){!}", "service")
+ 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("htop htop*.rpm", "Generate simple tests for package")
+ info.AddExample("redis redis*.rpm -s redis", "Generate tests with service check")
+ info.AddExample("-o zl.recipe zlib zlib*.rpm minizip*.rpm", "Generate tests with custom name")
+
+ return info
+}
+
+// genAbout generates info about version
+func genAbout(gitRev string) *usage.About {
+ return &usage.About{
App: APP,
Version: VER,
Desc: DESC,
@@ -249,6 +276,4 @@ func showAbout() {
License: "Apache License, Version 2.0 ",
UpdateChecker: usage.UpdateChecker{"essentialkaos/bop", update.GitHubChecker},
}
-
- about.Render()
}
diff --git a/data/data.go b/data/data.go
index fa4cb3f..9fd99ad 100644
--- a/data/data.go
+++ b/data/data.go
@@ -2,7 +2,7 @@ package data
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
diff --git a/extractor/extractor.go b/extractor/extractor.go
index 1de9673..d642fac 100644
--- a/extractor/extractor.go
+++ b/extractor/extractor.go
@@ -2,7 +2,7 @@ package extractor
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
diff --git a/generator/generator.go b/generator/generator.go
index b4848ac..a977cb3 100644
--- a/generator/generator.go
+++ b/generator/generator.go
@@ -2,7 +2,7 @@ package generator
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
diff --git a/go.mod b/go.mod
index f065838..6c67dda 100644
--- a/go.mod
+++ b/go.mod
@@ -2,6 +2,9 @@ module github.com/essentialkaos/bop
go 1.17
-require github.com/essentialkaos/ek/v12 v12.57.1
+require (
+ github.com/essentialkaos/depsy v1.0.0
+ github.com/essentialkaos/ek/v12 v12.57.1
+)
-require golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
+require golang.org/x/sys v0.4.0 // indirect
diff --git a/go.sum b/go.sum
index 8cbe5e3..a2a6f58 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
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.57.1 h1:9dj32HLCVmseBoa43F6HaZz1qbKts5GNBCtFdrpQPno=
github.com/essentialkaos/ek/v12 v12.57.1/go.mod h1:G8ghiSKh8ToJQCdB2bAhE3CnI6dn9nTJdWH3bQIVr1U=
github.com/essentialkaos/go-linenoise/v3 v3.4.0/go.mod h1:t1kNLY2bSMQCy1JXOefD2BDLs/TTPMtTv3DFNV5uDSI=
@@ -20,8 +22,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
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 h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.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=
diff --git a/rpm/rpm.go b/rpm/rpm.go
index be603ab..f92119a 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -2,7 +2,7 @@ package rpm
// ////////////////////////////////////////////////////////////////////////////////// //
// //
-// Copyright (c) 2022 ESSENTIAL KAOS //
+// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
diff --git a/support/support.go b/support/support.go
new file mode 100644
index 0000000..29e6ae6
--- /dev/null
+++ b/support/support.go
@@ -0,0 +1,146 @@
+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/hash"
+ "github.com/essentialkaos/ek/v12/strutil"
+
+ "github.com/essentialkaos/depsy"
+)
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+
+// Pkg contains basic package info
+type Pkg struct {
+ Name string
+ Version string
+}
+
+// Pkgs is slice with packages
+type Pkgs []Pkg
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+
+// ShowSupportInfo prints verbose info about application, system, dependencies and
+// important environment
+func ShowSupportInfo(app, ver, gitRev string, gomod []byte) {
+ pkgs := collectEnvInfo()
+
+ fmtutil.SeparatorTitleColorTag = "{s-}"
+ fmtutil.SeparatorFullscreen = false
+ fmtutil.SeparatorColorTag = "{s-}"
+ fmtutil.SeparatorSize = 80
+
+ showApplicationInfo(app, ver, gitRev)
+ showOSInfo()
+ showEnvInfo(pkgs)
+ 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, "Git SHA", 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)
+ }
+ }
+}
+
+// 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)
+ }
+}
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+
+// getMaxSize returns max package name size
+func (p Pkgs) getMaxSize() int {
+ size := 0
+
+ for _, pkg := range p {
+ if len(pkg.Name) > size {
+ size = len(pkg.Name)
+ }
+ }
+
+ return size
+}
diff --git a/support/support_linux.go b/support/support_linux.go
new file mode 100644
index 0000000..1bb41af
--- /dev/null
+++ b/support/support_linux.go
@@ -0,0 +1,130 @@
+package support
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+// //
+// Copyright (c) 2023 ESSENTIAL KAOS //
+// Apache License, Version 2.0 //
+// //
+// ////////////////////////////////////////////////////////////////////////////////// //
+
+import (
+ "os/exec"
+ "strings"
+
+ "github.com/essentialkaos/ek/v12/fmtc"
+ "github.com/essentialkaos/ek/v12/fmtutil"
+ "github.com/essentialkaos/ek/v12/fsutil"
+ "github.com/essentialkaos/ek/v12/system"
+)
+
+// ////////////////////////////////////////////////////////////////////////////////// //
+
+// 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, "Version", systemInfo.Version)
+ }
+ }
+
+ 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)
+}
+
+// showEnvInfo shows info about environment
+func showEnvInfo(pkgs Pkgs) {
+ fmtutil.Separator(false, "ENVIRONMENT")
+
+ size := pkgs.getMaxSize()
+
+ for _, pkg := range pkgs {
+ printInfo(size, pkg.Name, pkg.Version)
+ }
+}
+
+// collectEnvInfo collects info about packages
+func collectEnvInfo() Pkgs {
+ return Pkgs{
+ getPackageInfo("rpm"),
+ }
+}
+
+// getPackageVersion returns package name from rpm database
+func getPackageInfo(name string) Pkg {
+ switch {
+ case isDEBBased():
+ return getDEBPackageInfo(name)
+ case isRPMBased():
+ return getRPMPackageInfo(name)
+ }
+
+ return Pkg{name, ""}
+}
+
+// isDEBBased returns true if is DEB-based distro
+func isRPMBased() bool {
+ return fsutil.IsExist("/usr/bin/rpm")
+}
+
+// isDEBBased returns true if is DEB-based distro
+func isDEBBased() bool {
+ return fsutil.IsExist("/usr/bin/dpkg-query")
+}
+
+// getRPMPackageInfo returns info about RPM package
+func getRPMPackageInfo(name string) Pkg {
+ cmd := exec.Command("rpm", "-q", name)
+ out, err := cmd.Output()
+
+ if err != nil || len(out) == 0 {
+ return Pkg{name, ""}
+ }
+
+ return Pkg{name, strings.TrimRight(string(out), "\n\r")}
+}
+
+// getDEBPackageInfo returns info about DEB package
+func getDEBPackageInfo(name string) Pkg {
+ cmd := exec.Command("dpkg-query", "--showformat=${Version}", "--show", name)
+ out, err := cmd.Output()
+
+ if err != nil || len(out) == 0 {
+ return Pkg{name, ""}
+ }
+
+ return Pkg{name, string(out)}
+}