diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000..16f43246 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,39 @@ +version: "2" + +checks: + argument-count: + enabled: true + config: + threshold: 6 + complex-logic: + enabled: true + config: + threshold: 6 + file-lines: + enabled: true + config: + threshold: 1000 + method-complexity: + enabled: true + config: + threshold: 8 + method-count: + enabled: true + config: + threshold: 20 + method-lines: + enabled: true + config: + threshold: 100 + nested-control-flow: + enabled: true + config: + threshold: 6 + return-statements: + enabled: true + config: + threshold: 6 + similar-code: + enabled: false + identical-code: + enabled: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abb62347..f9e0a8bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - SRC_DIR: src/github.com/${{ github.repository }} - jobs: Go: name: Go @@ -30,34 +27,28 @@ jobs: go: [ '1.19.x', '1.20.x' ] steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Go uses: actions/setup-go@v4 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: Run tests - working-directory: ${{env.SRC_DIR}} run: go test -covermode=count ./parser ./recipe -coverprofile=cover.out - name: Send coverage data - uses: essentialkaos/goveralls-action@v1 + uses: essentialkaos/goveralls-action@v2 env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - path: ${{env.SRC_DIR}} profile: cover.out parallel: true flag-name: linux-${{ matrix.go }} @@ -70,7 +61,7 @@ jobs: steps: - name: Finish parallel tests - uses: essentialkaos/goveralls-action@v1 + uses: essentialkaos/goveralls-action@v2 env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -83,24 +74,20 @@ jobs: needs: Go steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.19.x' - - name: Checkout - uses: actions/checkout@v3 - with: - path: ${{env.SRC_DIR}} - - name: Download dependencies - working-directory: ${{env.SRC_DIR}} run: make deps - name: Check Golang sources with Aligo - uses: essentialkaos/aligo-action@v1 + uses: essentialkaos/aligo-action@v2 with: - path: ${{env.SRC_DIR}} files: ./... Shellcheck: @@ -133,6 +120,19 @@ jobs: with: files: .docker/centos7.docker .docker/ol7.docker .docker/ol8.docker .docker/ol9.docker + Typos: + name: Typos + runs-on: ubuntu-latest + + needs: Go + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Check spelling + uses: crate-ci/typos@master + DockerBuild: name: Docker Build Check runs-on: ubuntu-latest diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000..55aead83 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,2 @@ +[files] +extend-exclude = ["go.sum"] diff --git a/COOKBOOK.md b/COOKBOOK.md index abb7f4f6..273b298b 100644 --- a/COOKBOOK.md +++ b/COOKBOOK.md @@ -24,6 +24,7 @@ * [`wait-output`](#wait-output) * [`output-match`](#output-match) * [`output-contains`](#output-contains) + * [`output-empty`](#output-empty) * [`output-trim`](#output-trim) * [Filesystem](#filesystem) * [`chdir`](#chdir) @@ -310,7 +311,7 @@ command "echo 'ABCD'" "Simple echo command" ``` ```yang -command "USER=john ID=123 echo 'ABCD'" "Simple echo command with enviroment variables" +command "USER=john ID=123 echo 'ABCD'" "Simple echo command with environment variables" expect "ABCD" exit 0 ``` @@ -602,6 +603,23 @@ command "echo 'ABCD'" "Simple echo command" +##### `output-empty` + +Checks if the output is empty. + +**Syntax:** `output-empty` + +**Negative form:** Yes + +**Example:** + +```yang +command "echo 'ABCD'" "Simple echo command" + !output-empty +``` + + + ##### `output-trim` Trims output (_remove output data from store_). diff --git a/Makefile b/Makefile index aa94a179..d48c38e8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ ################################################################################ -# This Makefile generated by GoMakeGen 2.1.0 using next command: +# This Makefile generated by GoMakeGen 2.2.0 using next command: # gomakegen --mod . # # More info: https://kaos.sh/gomakegen @@ -105,6 +105,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.1.0\033[0m\n' + @echo -e '\033[90mGenerated by GoMakeGen 2.2.0\033[0m\n' ################################################################################ diff --git a/README.md b/README.md index 10a73eab..f8b6cfe1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@

- GitHub Actions CI Status GoReportCard + Code Climate Maintainability codebeat badge - GitHub Actions CodeQL Status +
+ GitHub Actions CI Status + GitHub Actions CodeQL Status

@@ -25,7 +27,7 @@ Information about bibop recipe syntax you can find in our [cookbook](COOKBOOK.md #### From source -To build the `bibop` from scratch, make sure you have a working Go 1.18+ workspace ([instructions](https://golang.org/doc/install)), then: +To build the `bibop` from scratch, make sure you have a working Go 1.18+ workspace ([instructions](https://go.dev/doc/install)), then: ``` go install github.com/essentialkaos/bibop@latest diff --git a/action/io.go b/action/io.go index 75af8d98..df88a523 100644 --- a/action/io.go +++ b/action/io.go @@ -151,6 +151,18 @@ func OutputContains(action *recipe.Action, output *OutputContainer) error { return nil } +// OutputEmpty is action processor for "output-empty" +func OutputEmpty(action *recipe.Action, output *OutputContainer) error { + switch { + case !action.Negative && !output.IsEmpty(): + return fmt.Errorf("Output contains data") + case action.Negative && output.IsEmpty(): + return fmt.Errorf("Output is empty") + } + + return nil +} + // OutputTrim is action processor for "output-trim" func OutputTrim(action *recipe.Action, output *OutputContainer) error { output.Purge() diff --git a/cli/cli.go b/cli/cli.go index eb2d349f..277cbb50 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -39,7 +39,7 @@ import ( // Application info const ( APP = "bibop" - VER = "7.1.0" + VER = "7.2.0" DESC = "Utility for testing command-line tools" ) @@ -58,7 +58,7 @@ const ( OPT_ERROR_DIR = "e:error-dir" OPT_TAG = "t:tag" OPT_QUIET = "q:quiet" - OPT_INGORE_PACKAGES = "ip:ignore-packages" + OPT_IGNORE_PACKAGES = "ip:ignore-packages" OPT_NO_CLEANUP = "nl:no-cleanup" OPT_NO_COLOR = "nc:no-color" OPT_HELP = "h:help" @@ -83,11 +83,11 @@ var optMap = options.Map{ OPT_ERROR_DIR: {}, OPT_TAG: {Mergeble: true}, OPT_QUIET: {Type: options.BOOL}, - OPT_INGORE_PACKAGES: {Type: options.BOOL}, + OPT_IGNORE_PACKAGES: {Type: options.BOOL}, OPT_NO_CLEANUP: {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: {}, @@ -119,7 +119,7 @@ func Run(gitRev string, gomod []byte) { printMan() os.Exit(0) case options.GetB(OPT_VER): - genAbout(gitRev).Print() + genAbout(gitRev).Print(options.GetS(OPT_VER)) os.Exit(0) case options.GetB(OPT_VERB_VER): support.Print(APP, VER, gitRev, gomod) @@ -352,7 +352,7 @@ func getValidationConfig(tags []string) *executor.ValidationConfig { vc.IgnorePrivileges = true } - if options.GetB(OPT_INGORE_PACKAGES) { + if options.GetB(OPT_IGNORE_PACKAGES) { vc.IgnoreDependencies = true } @@ -403,7 +403,7 @@ func printWarn(f string, a ...interface{}) { } } -// printErrorAndExit print error mesage and exit with exit code 1 +// printErrorAndExit print error message and exit with exit code 1 func printErrorAndExit(f string, a ...interface{}) { printError(f, a...) os.Exit(1) @@ -456,7 +456,7 @@ func genUsage() *usage.Info { info.AddOption(OPT_ERROR_DIR, "Path to directory for errors data", "dir") info.AddOption(OPT_TAG, "One or more command tags to run", "tag") info.AddOption(OPT_QUIET, "Quiet mode") - info.AddOption(OPT_INGORE_PACKAGES, "Do not check system for installed packages") + info.AddOption(OPT_IGNORE_PACKAGES, "Do not check system for installed packages") info.AddOption(OPT_NO_CLEANUP, "Disable deleting files created during tests") info.AddOption(OPT_NO_COLOR, "Disable colors in output") info.AddOption(OPT_HELP, "Show this help message") diff --git a/cli/executor/executor.go b/cli/executor/executor.go index ee610259..988fae16 100644 --- a/cli/executor/executor.go +++ b/cli/executor/executor.go @@ -43,12 +43,13 @@ const MAX_STORAGE_SIZE = 8 * 1024 * 1024 // 8 MB // Executor is executor struct type Executor struct { - config *Config // Config - start time.Time // Time when recipe execution started - passes int // Number of passed commands - fails int // Number of failed commands - skipped int // Number of skipped commands - logger *log.Logger // Pointer to logger + config *Config // Configuration + start time.Time // Time when recipe execution started + passes int // Number of passed commands + fails int // Number of failed commands + skipped int // Number of skipped commands + logger *log.Logger // Pointer to logger + wrkDirObjs map[string]bool // Map with working dir objects } // ExecutorConfig contains executor configuration @@ -180,6 +181,8 @@ func (e *Executor) Run(rr render.Renderer, r *recipe.Recipe, tags []string) bool os.Chdir(r.Dir) } + e.wrkDirObjs = getWorkingDirObjects(r.Dir) + applyRecipeOptions(e, rr, r) processRecipe(e, rr, r, tags) @@ -387,6 +390,8 @@ func runAction(a *recipe.Action, cmdEnv *CommandEnv) error { return action.WaitOutput(a, cmdEnv.output) case recipe.ACTION_OUTPUT_CONTAINS: return action.OutputContains(a, cmdEnv.output) + case recipe.ACTION_OUTPUT_EMPTY: + return action.OutputEmpty(a, cmdEnv.output) case recipe.ACTION_OUTPUT_MATCH: return action.OutputMatch(a, cmdEnv.output) case recipe.ACTION_OUTPUT_TRIM: @@ -495,7 +500,7 @@ func logError(e *Executor, c *recipe.Command, a *recipe.Action, ce *CommandEnv, } } -// getErrorOrigin returns info about error orign +// getErrorOrigin returns info about error origin func getErrorOrigin(c *recipe.Command, a *recipe.Action, id string) string { switch a { case nil: @@ -552,23 +557,38 @@ func cleanTempData() { temp.Clean() } +// getWorkingDirObjects returns map with all objects in working dir +func getWorkingDirObjects(workingDir string) map[string]bool { + targets := fsutil.ListAll(workingDir, false) + fsutil.ListToAbsolute(workingDir, targets) + + if len(targets) == 0 { + return nil + } + + result := make(map[string]bool) + + for _, target := range targets { + result[target] = true + } + + return result +} + // cleanupWorkingDir cleanup working dir func cleanupWorkingDir(e *Executor, workingDir string) { if e.config.DisableCleanup { return } - targets := fsutil.ListAll(workingDir, false, fsutil.ListingFilter{ - CTimeYounger: e.start.Unix(), - }) - + targets := fsutil.ListAll(workingDir, false) fsutil.ListToAbsolute(workingDir, targets) - if len(targets) == 0 { - return - } - for _, target := range targets { + if e.wrkDirObjs != nil && e.wrkDirObjs[target] { + continue + } + os.RemoveAll(target) } } diff --git a/go.mod b/go.mod index d3a8ad60..0c675ed7 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,15 @@ go 1.18 require ( github.com/buger/jsonparser v1.1.1 github.com/essentialkaos/check v1.4.0 - github.com/essentialkaos/depsy v1.0.0 - github.com/essentialkaos/ek/v12 v12.63.0 + github.com/essentialkaos/depsy v1.1.0 + github.com/essentialkaos/ek/v12 v12.67.1 github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 ) require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.6.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/sys v0.8.0 // indirect ) diff --git a/go.sum b/go.sum index c76485b3..f09dbee0 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/essentialkaos/check v1.4.0 h1:kWdFxu9odCxUqo1NNFNJmguGrDHgwi3A8daXX1nkuKk= github.com/essentialkaos/check v1.4.0/go.mod h1:LMKPZ2H+9PXe7Y2gEoKyVAwUqXVgx7KtgibfsHJPus0= -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.63.0 h1:9yaEu5W3bx//9y52ShqYCoFDKOcwEdrnvgSkUYyatgI= -github.com/essentialkaos/ek/v12 v12.63.0/go.mod h1:9MlSuHpewu7OZ9tM9dLFHvoA8dflBIUPCA0Ctt97wRs= +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.67.1 h1:RSQxq0TaALADLVYhILky5nFQw8G1HeryUy8C6td35PA= +github.com/essentialkaos/ek/v12 v12.67.1/go.mod h1:SDR3JJJa8FySwRusH/h1gg5c6kpdF18aCkeK7eQSlic= github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 h1:CVuJwN34x4xM2aT4sIKhmeib40NeBPhRihNjQmpJsA4= github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -14,9 +14,10 @@ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3x 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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/recipe/tokens.go b/recipe/tokens.go index 4c022563..7faddcc1 100644 --- a/recipe/tokens.go +++ b/recipe/tokens.go @@ -31,6 +31,7 @@ const ( ACTION_WAIT_OUTPUT = "wait-output" ACTION_OUTPUT_MATCH = "output-match" ACTION_OUTPUT_CONTAINS = "output-contains" + ACTION_OUTPUT_EMPTY = "output-empty" ACTION_OUTPUT_TRIM = "output-trim" ACTION_PRINT = "print" @@ -139,6 +140,7 @@ var Tokens = []TokenInfo{ {ACTION_WAIT_OUTPUT, 1, 1, false, false}, {ACTION_OUTPUT_MATCH, 1, 1, false, true}, {ACTION_OUTPUT_CONTAINS, 1, 1, false, true}, + {ACTION_OUTPUT_EMPTY, 0, 0, false, true}, {ACTION_OUTPUT_TRIM, 0, 0, false, false}, {ACTION_PRINT, 1, 1, false, false},