Zb - an opinionated repo based tool for linting, testing and building go source

zb — an opinionated repo based tool for linting, testing and building go source

GoDoc Go Report Card

███████╗██████╗     ██████╗  ██████╗ ███████╗███████╗    ██╗████████╗
╚══███╔╝██╔══██╗    ██╔══██╗██╔═══██╗██╔════╝██╔════╝    ██║╚══██╔══╝
  ███╔╝ ██████╔╝    ██║  ██║██║   ██║█████╗  ███████╗    ██║   ██║
 ███╔╝  ██╔══██╗    ██║  ██║██║   ██║██╔══╝  ╚════██║    ██║   ██║
███████╗██████╔╝    ██████╔╝╚██████╔╝███████╗███████║    ██║   ██║
╚══════╝╚═════╝     ╚═════╝  ╚═════╝ ╚══════╝╚══════╝    ╚═╝   ╚═╝

[Help! your logo here]

Benefits

  • Faster builds (by defaulting to go install except for main packages, and by running concurrent go install commands when the dependency tree allows)
  • Faster testing (by caching test results and not retesting except when necessary)
  • Faster linting (by caching lint results from gometalinter)
  • Did I mention fast!
  • Automatically runs go generate if its dependency calculation determines it's required
  • Operates on all packages in a repository (by default) with intelligent support for vendored packages
  • Can complement other build tools like make
  • Does not interfere with tools like govendor or gb

Installation

Simply run go get jrubin.io/zb

Rationale

Many go repositories have multiple packages. It is often necessary to build/install, test, lint, etc. across all packages within the repository. Go "ellipsis" wildcards (...) can be used to select all subdirectories of a given repository, but don't exclude vendored packages which makes running tests and linting complicated. Some operations should be aware of vendored packages (e.g. build/install), while others should ignore them (e.g. lint). Yet others need to be aware of changes in vendored packages, but should not operate directly on them (e.g. test). Traditional build tools, like make, can be used to supplement the go command to work only on the intended packages. Building dependency lists for make targets, which are required, for example, to dynamically identify modified .go files and what would need to be rebuilt as a result, is at the very least complicated (consider .go files modified outside the repo) and at best slow.

zb fixes all of this

Packages → Repositories

zb is aware of the directory it is called from. If any of its commands is called without a package argument, it will use the current working directory.

zb can also be passed packages just like the go command. Both package names (e.g. fmt, jrubin.io/zb) and relative package names (e.g. ./jrubin.io/zb) are supported, as are ellipsis (...).

zb will identify the repository associated with each package by locating the directory and walking up the directory tree to find the repository directory (containing .git [only git is supported at present]). It will then execute the command for all packages in each repository it identified.

go generate

go generate is great, but sometimes it needs to be executed before a build. Forgetting to execute go generate can be a major problem if, for example, new values were added to a stringer.

zb does its own dependency calculation and can identify go generate dependencies provided an additional annotation is also present.

The following formats are available to define dependencies of go generate

zb:generate formats

  • //zb:generate glob glob... Causes go generate to be executed on the go file with the annotation if the go file itself is newer than the files expanded from the globs. This is useful with commands like stringer:

    //go:generate stringer -type=YourType
    //zb:generate yourtype_string.go
  • //zb:generate -patsubst %pattern %replacement glob glob... Works like make's patsubst. Causes go generate to be executed on the go file with the annotation if any of the files expanded from the globs is newer than any of the filenames generated through the pattern substitution.

    //go:generate make proto
    //zb:generate -patsubst %.proto %.pb.go *.proto

    This command first matches all files matching *.proto and then performs the substitution by extracting the part of each of those file names before .proto (identified with the % in %.proto) and taking that extracted pattern and inserting it into the % part of %.pb.go.

    So if there were files message.proto and types.proto, go generate would be executed if either of those files were newer than message.pb.go or types.pb.go (including if the .pb.go files did not yet exist).

  • //zb:generate -target file glob glob... Basically a simplified -patsubst. Causes go generate to be executed if any of the files expanded from the globs is newer than file Can also be written as //zb:generate -patsubst % file glob glob...

Commands

install

Initially, zb install appears to do the same things as go install (just for all packages in the repositories). In fact, zb install just calls go install under the hood and supports all of its flags. There are a few differences though.

  • go generate may be called before building the package according to the //go:generate and //zb:generate annotations
  • main packages (commands) are built with extra linker flags that cause main.gitCommit and main.buildDate variables to be set if they exist. See zb/main.go as an example of how to utilize this.
  • Executes go install for each stale package it finds and will execute concurrent go install processes when the dependency tree allows. Concurrency can be limited with $GOMAXPROCS.
  • If any of the non-vendored .go files in the repository contain TODO or FIXME these lines will be emitted to the console as warnings (unless the global -n flag is enabled).

build

zb build differs from go build in that non main packages will be installed with go install. Commands, however, will not be installed (to $GOPATH/bin), they are built with the binary being placed in the root of the repository tree. If there is already a directory in the root of the repository with the same name as the command, the command will be placed in that directory instead.

Otherwise, zb build is identical to zb install.

lint

Delegates functionality to gometalinter but with more useful defaults and caching of results.

  • The --concurrency, -j flag is dynamically calculated to be 1 less than half the number of CPU cores (but at least 1) [gometalinter default is 16]
  • --tests is enabled by default
  • --deadline is set to 30s [gometalinter default is 5s]
  • --enable-gc is enabled by default
  • alighcheck, dupl, gocyclo and structcheck are disabled by default
  • errcheck, gofmt, goimports and unused are enabled (in addition to all other default enabled checkers) by default

The -n flag can be used to hide golint warnings about missing comments.

Since dependency calculation can sometimes add a non-trivial amount of time to the zb lint command, go generate will not be executed.

Files matching certain suffixes will be excluded from the results. This list can be modified with the --ignore-suffix flag. By default files with the following suffixes will be excluded:

  • .pb.go
  • .pb.gw.go
  • _string.go
  • bindata.go
  • bindata_assetfs.go
  • static.go

All other gometalinter flags will be honored as defined.

test

Delegates functionality to go test but caches the results (like gt). Honors all other flags just like go test except those intended to be passed directly to the test binary.

Use the -f flag to treat the test results as uncached, forcing the tests to be executed (and cached) again.

To see which tests would be executed (because their results are not-cached or the -f flag was provided), use the -l flag.

Since dependency calculation can sometimes add a non-trivial amount of time to the zb test command, go generate will not be executed.

complete

zb has full support for shell autocompletion in both bash and zsh. Simply execute eval "$(zb complete)" (or put in your init files) to enable.

clean

Removes the executables produced by zb build

commands

Lists the absolute paths where each of the commands (from main packages) will be placed with zb build

list

Similar to go list (and takes the same flags) but will list all of the packages in each of the repositories. Use the --vendor flag to exclude vendored packages.

help

zb contains a built-in, comprehensive help system. Running zb by itself (or with the -h or --help flags) will list the commands and global flags. zb help <command>, zb <command> -h and zb <command> --help will show contextual help for the given command.

Global Flags

--log-level, -l, $LOG_LEVEL

Defaults to info. Available levels are:

  • error
  • warn
  • info
  • debug

--no-warn-todo-fixme, -n, $NO_WARN_TODO_FIXME

Do not warn when finding WARN or FIXME in .go files

--cache, $CACHE

Modify the base directory used for storing results of commands that cache their results (test and lint). Defaults to $HOME/Library/Caches/zb on mac and $HOME/.cache/zb elsewhere.

--package, -p

Causes zb to execute only on the explicitly listed packages and not on all packages in their repositories.

Still Planned

  • Support for other version control systems [#2]
  • Complete all godoc documentation [#3]
  • Add comprehensive testing [#4]
  • Detect import cycles in dependency calculation [#5]
  • Wrap govendor in an opinionated way [#6]
  • Setup continuous integration [#7]
Comments
  • Test command raises panic

    Test command raises panic

    Seeing if anyone else got this issue when running zb test.

    $ zb test
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x13fae2]
    
    goroutine 1 [running]:
    panic(0x444000, 0xc420014120)
            /usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:500 +0x1a1
    jrubin.io/zb/lib/project.(*Package).Hash(0x0, 0xc4204f8450, 0xc420422ab1, 0x25, 0x71e7e0)
            /Users/thinh/go/src/jrubin.io/zb/lib/project/package.go:280 +0x42
    jrubin.io/zb/lib/project.(*Package).TestHash(0xc4202c6d90, 0xc42015a788, 0x1432a0, 0xc420377550, 0x0, 0x1)
            /Users/thinh/go/src/jrubin.io/zb/lib/project/package.go:259 +0x50a
    jrubin.io/zb/lib/zbtest.(*ZBTest).CacheFile(0xc42015a780, 0xc4202c6d90, 0x1, 0x0, 0x0, 0x0)
            /Users/thinh/go/src/jrubin.io/zb/lib/zbtest/zbtest.go:37 +0x53
    jrubin.io/zb/lib/zbtest.(*ZBTest).HaveResult(0xc42015a780, 0xc4202c6d90, 0xc420313501, 0x35, 0xc420377610)
            /Users/thinh/go/src/jrubin.io/zb/lib/zbtest/zbtest.go:55 +0x48
    jrubin.io/zb/cmd/test.(*cc).buildPackagesLists(0xc42015a780, 0xc42014f500, 0x6, 0x8, 0xc4200335a8, 0x1, 0x1, 0x0, 0x0, 0x0, ...)
            /Users/thinh/go/src/jrubin.io/zb/cmd/test/test.go:111 +0x108
    jrubin.io/zb/cmd/test.(*cc).buildProjectsLists(0xc42015a780, 0xc420032408, 0x1, 0x1, 0xc420032408, 0x1, 0x1, 0x0, 0x0, 0x1e, ...)
            /Users/thinh/go/src/jrubin.io/zb/cmd/test/test.go:126 +0xc9
    jrubin.io/zb/cmd/test.(*cc).runProjects(0xc42015a780, 0x6d3f80, 0xc420032010, 0xc42000c0a0, 0x6, 0x6, 0xc42013dc00, 0xc42009c000, 0xc420051808, 0xccbf1, ...)
            /Users/thinh/go/src/jrubin.io/zb/cmd/test/test.go:99 +0x98
    jrubin.io/zb/cmd/test.(*cc).run(0xc42015a780, 0x6d3f80, 0xc420032010, 0xc42000c0a0, 0x6, 0x6, 0xc42013dc00, 0x0)
            /Users/thinh/go/src/jrubin.io/zb/cmd/test/test.go:66 +0x2f0
    jrubin.io/zb/cmd/test.(*cc).New.func1(0xc420174140, 0x0, 0x702200)
            /Users/thinh/go/src/jrubin.io/zb/cmd/test/test.go:37 +0x6c
    main.wrapFn.func1(0xc420174140, 0x0, 0xc420174140)
            /Users/thinh/go/src/jrubin.io/zb/wrap.go:33 +0x33
    jrubin.io/zb/vendor/github.com/urfave/cli.HandleAction(0x42df60, 0xc42013cd50, 0xc420174140, 0xc420074b00, 0x0)
            /Users/thinh/go/src/jrubin.io/zb/vendor/github.com/urfave/cli/app.go:483 +0xb9
    jrubin.io/zb/vendor/github.com/urfave/cli.Command.Run(0x4c18a5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4da8df, 0x46, 0x0, ...)
            /Users/thinh/go/src/jrubin.io/zb/vendor/github.com/urfave/cli/command.go:193 +0xb96
    jrubin.io/zb/vendor/github.com/urfave/cli.(*App).Run(0xc42008d520, 0xc42000c080, 0x8, 0x8, 0x0, 0x0)
            /Users/thinh/go/src/jrubin.io/zb/vendor/github.com/urfave/cli/app.go:250 +0x812
    main.main()
            /Users/thinh/go/src/jrubin.io/zb/main.go:110 +0x51
    
  • Cannot install due to Cloudflare SSL issue

    Cannot install due to Cloudflare SSL issue

    Attempting to install gives this error:

    package jrubin.io/zb: unrecognized import path "jrubin.io/zb" (parse https://jrubin.io/zb?go-get=1: no go-import meta tags ()

    Trying to fetch that URL manually (or just visit jrubin.io in a browser) gives a 526 error (a code that seems to be Cloudflare specific) indicating that the origin site SSL cert is invalid.

  • Unable to install on Windows

    Unable to install on Windows

    Installation fails when I use go get:

    λ go get -u jrubin.io/zb
    # jrubin.io/zb/vendor/srcd.works/go-git.v4
    E:\Installs\go-tools\src\jrubin.io\zb\vendor\srcd.works\go-git.v4\worktree.go:74:45: undefined: syscall.Stat_t
    E:\Installs\go-tools\src\jrubin.io\zb\vendor\srcd.works\go-git.v4\worktree.go:92:23: undefined: syscall.Stat_t
    
  • `zb build` doesn't report about missing dependencies errors

    `zb build` doesn't report about missing dependencies errors

    How to replicate:

    • create a new empty environment (GOPATH), eg:

      mkdir ~/tmp-go
      export GOPATH=~/tmp-go
      
    • clone repository (without using go get) with some dependencies, eg:

      git clone https://github.com/robert-zaremba/log15.git ~/tmp-go/src/github.com/robert-zaremba/log15
      
    • run zb build there.

    It will return quietly without reporting error about missing dependencies. If you run go build (eg inside the log15/example package) it will return with errors.

  • feature request: support build output dir/platform

    feature request: support build output dir/platform

    i use something like

    #!/usr/bin/env bash
    set -o errexit
    set -o pipefail
    set -o nounset
    IFS=$'\n\t'
    # shellcheck disable=SC2154
    trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
    
    exec 2>&1
    
    gofmt -e -s -w *.go
    
    mkdir -p bin
    
    function build {
      echo building for $GOOS-$GOARCH
      go build -o bin/controller-$GOOS-$GOARCH controller.go
    }
    
    #GOOS=android   GOARCH=arm      build
    #GOOS=darwin    GOARCH=386      build
    GOOS=darwin    GOARCH=amd64    build
    #GOOS=darwin    GOARCH=arm      build
    #GOOS=darwin    GOARCH=arm64    build
    #GOOS=dragonfly GOARCH=amd64    build
    #GOOS=freebsd   GOARCH=386      build
    #GOOS=freebsd   GOARCH=amd64    build
    #GOOS=freebsd   GOARCH=arm      build
    #GOOS=linux     GOARCH=386      build
    GOOS=linux     GOARCH=amd64    build
    #GOOS=linux     GOARCH=arm      build
    #GOOS=linux     GOARCH=arm64    build
    #GOOS=linux     GOARCH=ppc64    build
    #GOOS=linux     GOARCH=ppc64le  build
    #GOOS=linux     GOARCH=mips64   build
    #GOOS=linux     GOARCH=mips64le build
    #GOOS=netbsd    GOARCH=386      build
    #GOOS=netbsd    GOARCH=amd64    build
    #GOOS=netbsd    GOARCH=arm      build
    #GOOS=openbsd   GOARCH=386      build
    #GOOS=openbsd   GOARCH=amd64    build
    #GOOS=openbsd   GOARCH=arm      build
    #GOOS=plan9     GOARCH=386      build
    #GOOS=plan9     GOARCH=amd64    build
    #GOOS=solaris   GOARCH=amd64    build
    #GOOS=windows   GOARCH=386      build
    GOOS=windows   GOARCH=amd64    build
    

    it's just what i made - idk what i'm doing.

    anyway, that's good for building artifacts taht i include in gh releases or whatever. it would be super cool to add an output dir flag to zb build so i wouldn't have to backport the stuff that adds the ldflags for commit/builddate.

    the goos and goarch already pass through, so i have no problem building for other platforms, but i'd like to be able to specify like --outpattern bin/%name-%GOOS-%GOARCH or something.

    it's also very possible that this is doable with no modification, but idk how. perhaps this is just a docs fix.

    thanks for your consideration. also i really like zb so far.

  • Panic

    Panic

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x5b9d39]
    
    goroutine 19 [running]:
    jrubin.io/zb/lib/dependency.(*Target).key(0xc420497ab0, 0x8d8800, 0xc42012e850)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/dependency/target.go:62 +0x29
    jrubin.io/zb/lib/dependency.(*Targets).existsNoLock(0xb47480, 0xc420497ab0, 0x300000002, 0x0)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/dependency/target.go:123 +0x3c
    jrubin.io/zb/lib/dependency.(*Targets).insertNoLock(0xb47480, 0xc420497ab0, 0x8d8800)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/dependency/target.go:96 +0x39
    jrubin.io/zb/lib/dependency.(*Targets).Insert(0xb47480, 0xc420497ab0, 0xc420497a01)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/dependency/target.go:90 +0x4e
    jrubin.io/zb/lib/dependency.NewTarget(0x0, 0x0, 0xc42012e850, 0xc420497a40)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/dependency/target.go:50 +0x6f
    jrubin.io/zb/lib/project.(*Package).Targets(0xc4201335c0, 0xb13820, 0xb47b60, 0xb46910, 0xb46900, 0x0, 0xc42011e580, 0x17, 0x0, 0xc420130820, ...)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/project/package.go:120 +0x327
    jrubin.io/zb/lib/project.Packages.targets.func1(0x8, 0x9124d0)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/lib/project/packages.go:68 +0x94
    jrubin.io/zb/vendor/golang.org/x/sync/errgroup.(*Group).Go.func1(0xc4201f96c0, 0xc4201354a0)
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/vendor/golang.org/x/sync/errgroup/errgroup.go:58 +0x57
    created by jrubin.io/zb/vendor/golang.org/x/sync/errgroup.(*Group).Go
        /home/michael/code/go-thirdparty/src/jrubin.io/zb/vendor/golang.org/x/sync/errgroup/errgroup.go:66 +0x66
    
  • Support go build `-o` flag

    Support go build `-o` flag

    It's is beneficial to build only the requested package / main program (eg by explicitly pointing to the package we want to build using full name or .) and decide where to place the binary. This is especially useful in projects with lot of binaries.

Generate Markdown table for starred repo for a user.
Generate Markdown table for starred repo for a user.

List of Starred Repository How this generated? Id Name Description Star Counts Topics/Tags Last Updated 1 coreutils Cross-platform Rust rewrite of the

Dec 14, 2022
A demo repo to show KICS Github Action in Action

?? KICS GitHub Actions Demo This repository shows how KICS GitHub Action can be set and was fully inspired by the documentation on KICS GitHub Actions

Nov 23, 2021
This repo introduces a simple server, which provided some APIs for search DAS account's records or reverse records

Prerequisites Install Usage Others Das-Account-Indexer This repo introduces a simple server, which provided some APIs for search DAS account's records

Dec 13, 2022
A single-binary cross-platform lightweight client/server connection testing tool.

conntest A single-binary cross-platform lightweight client/server connection testing tool. Currently supports L7 TCP (HTTP). Configuration Options con

Jan 13, 2022
A small command line tool for testing grok patterns on file contents.

grogg A small command line tool for testing grok patterns on file contents. It uses the vjeantet/grok library for parsing and pterm for some extra glo

Feb 5, 2022
🛠 A Go SDK for building applications on top of Uniswap V3

?? A Go SDK for building applications on top of Uniswap V3

Jan 1, 2023
Building block for mobile money api clients

base the base code for creating mobile money api clients using golang build request request := NewRequestBuilder("login request", http.MethodPost, "ht

Jan 4, 2022
A Golang Client Library for building Cosmos SDK chain clients

Cosmos Client Lib in Go This is the start of ideas around how to implement the cosmos client libraries in a seperate repo How to instantiate and use t

Jan 6, 2023
DiscSpam is the best free and open source tool to spam/raid Discord servers.
DiscSpam is the best free and open source tool to spam/raid Discord servers.

DiscSpam Fast, Free, Easy to use Discord.com raid tool Report Bug , Request Feature About The Project There are a few Discord raid tools on GitHub, ho

Dec 27, 2022
Hassle-free REST API testing for Go

melatonin melatonin is a fluent, flexible REST API testing library for Go. It provides many of the benefits of a domain-specific test language but wit

Jan 8, 2022
This program performs stress testing for the Cosmos module

Cosmos Modules Testing Program ?? Overview This program performs stress testing for the Cosmos module. Support: Liquidity , IBC transfer Note: Require

May 25, 2022
Testing ground for build-your-own golang/grpc demo app.

Getting started Prereqs You will need to install both Go and the protoc compiler (version 3): Go installation protoc installation Install the protobuf

Dec 15, 2021
A Golang localhost TLS Server for testing Mutual Authentication (A.K.A Client-Side Authentication)

goMutualAuthServer goMutualAuthServer implements a localhost TLS server in Golang, which can be used to perform Mutual Authentication (A.K.A Client-Si

Dec 23, 2021
Maven-client - A command line tool to query first order and transitive maven coordinates based off an initial list of coordinates.

maven-client Description This CLI reads a list of maven group artifact version (GAV) coordinates and returns an ordered list of first order and transi

Jan 6, 2022
💅🏽💄 A local development tool to replace docker-compose, based on Make
💅🏽💄 A local development tool to replace docker-compose, based on Make

???? Makeup ?? A local development tool to replace Docker Compose, based on Make. Makeup uses simple Makefiles to create a faster developer workflow c

Dec 2, 2022
Go(lang) client library for Cachet (open source status page system).

cachet Go(lang) client library for Cachet (open source status page system). Features Full API support Components Incidents Metrics Subscribers Various

Sep 27, 2022
Go library for querying Source servers

go-steam go-steam is a Go library for querying Source servers. Requirements Go 1.1 or above Installation go get -u github.com/sostronk/go-steam To us

Oct 28, 2022
This is the new api repository for Feel the Movies. Written in Go, totally open source.
This is the new api repository for Feel the Movies. Written in Go, totally open source.

This is the new API repository for Feel the Movies. Written in Go, totally open source. App Currently available for Android only. I have plans for an

Sep 18, 2022
Pre-constructed source for CDKTF AWS Golang

cdktf-provider-aws-go Terraform CDK aws Provider v3.64.2 go get github.com/hortau/cdktf-provider-aws-go Example: package main import ( "github.com/

Nov 27, 2021