├── .bingo ├── .gitignore ├── README.md ├── Variables.mk ├── copyright.mod ├── copyright.sum ├── embedmd.mod ├── embedmd.sum ├── faillint.mod ├── faillint.sum ├── go.mod ├── goimports.mod ├── goimports.sum ├── golangci-lint.mod ├── golangci-lint.sum ├── mdox.mod ├── mdox.sum ├── misspell.mod ├── misspell.sum ├── proxy.mod ├── proxy.sum └── variables.env ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── workflows │ └── go.yaml ├── .gitignore ├── .mdox.validators.yaml ├── CHANGELOG.md ├── COPYRIGHT ├── LICENSE ├── Makefile ├── README.md ├── builtin ├── embed.go └── help │ └── cmd.tmpl ├── cmd.go ├── examples ├── bingo-demo.gif ├── demo-nav.sh └── demo.sh ├── get.go ├── get_e2e_test.go ├── get_e2e_utils_test.go ├── get_test.go ├── go.mod ├── go.sum ├── main.go ├── pkg ├── bingo │ ├── helpers.go │ ├── mod.go │ ├── mod_test.go │ ├── test.mod │ ├── test2.mod │ ├── test3.mod │ ├── test4.mod │ ├── test5.mod │ └── variables.go ├── cpy │ └── cpy.go ├── envars │ ├── envars.go │ ├── eval.go │ ├── eval_test.go │ └── testdata │ │ ├── bingo.env │ │ ├── export.env │ │ └── simple.env ├── makefile │ ├── parser.go │ └── parser_test.go ├── mod │ ├── doc.go │ ├── mod.go │ └── mod_test.go ├── runner │ ├── runner.go │ └── runner_test.go └── version │ └── version.go ├── scripts └── build-check-comments.sh └── testdata ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── main.go ├── testproject_with_bingo_v0_6 └── .bingo │ ├── .gitignore │ ├── README.md │ ├── Variables.mk │ ├── buildable-v2.mod │ ├── buildable-v2.sum │ ├── buildable-withReplace.mod │ ├── buildable-withReplace.sum │ ├── buildable.1.mod │ ├── buildable.1.sum │ ├── buildable.2.mod │ ├── buildable.2.sum │ ├── buildable.mod │ ├── buildable.sum │ ├── buildable2.mod │ ├── buildable2.sum │ ├── go.mod │ └── variables.env └── testproject_with_bingo_v0_7 └── .bingo ├── .gitignore ├── README.md ├── Variables.mk ├── buildable-v2.mod ├── buildable-v2.sum ├── buildable-withReplace.mod ├── buildable-withReplace.sum ├── buildable.1.mod ├── buildable.1.sum ├── buildable.2.mod ├── buildable.2.sum ├── buildable.mod ├── buildable.sum ├── buildable2.mod ├── buildable2.sum ├── go.mod └── variables.env /.bingo/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore everything 3 | * 4 | 5 | # But not these files: 6 | !.gitignore 7 | !*.mod 8 | !*.sum 9 | !README.md 10 | !Variables.mk 11 | !variables.env 12 | 13 | *tmp.mod 14 | -------------------------------------------------------------------------------- /.bingo/README.md: -------------------------------------------------------------------------------- 1 | # Project Development Dependencies. 2 | 3 | This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. 4 | 5 | * Run `bingo get` to install all tools having each own module file in this directory. 6 | * Run `bingo get ` to install that have own module file in this directory. 7 | * For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. 8 | * For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. 9 | * For go: Import `.bingo/variables.go` to for variable names. 10 | * See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. 11 | 12 | ## Requirements 13 | 14 | * Go 1.14+ 15 | -------------------------------------------------------------------------------- /.bingo/Variables.mk: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 4 | GOPATH ?= $(shell go env GOPATH) 5 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 6 | GO ?= $(shell which go) 7 | 8 | # Below generated variables ensure that every time a tool under each variable is invoked, the correct version 9 | # will be used; reinstalling only if needed. 10 | # For example for copyright variable: 11 | # 12 | # In your main Makefile (for non array binaries): 13 | # 14 | #include .bingo/Variables.mk # Assuming -dir was set to .bingo . 15 | # 16 | #command: $(COPYRIGHT) 17 | # @echo "Running copyright" 18 | # @$(COPYRIGHT) 19 | # 20 | COPYRIGHT := $(GOBIN)/copyright-v0.0.0-20210112004814-138d5e5695fe 21 | $(COPYRIGHT): $(BINGO_DIR)/copyright.mod 22 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 23 | @echo "(re)installing $(GOBIN)/copyright-v0.0.0-20210112004814-138d5e5695fe" 24 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=copyright.mod -o=$(GOBIN)/copyright-v0.0.0-20210112004814-138d5e5695fe "github.com/efficientgo/tools/copyright" 25 | 26 | EMBEDMD := $(GOBIN)/embedmd-v1.0.0 27 | $(EMBEDMD): $(BINGO_DIR)/embedmd.mod 28 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 29 | @echo "(re)installing $(GOBIN)/embedmd-v1.0.0" 30 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=embedmd.mod -o=$(GOBIN)/embedmd-v1.0.0 "github.com/campoy/embedmd" 31 | 32 | FAILLINT := $(GOBIN)/faillint-v1.11.0 33 | $(FAILLINT): $(BINGO_DIR)/faillint.mod 34 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 35 | @echo "(re)installing $(GOBIN)/faillint-v1.11.0" 36 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=faillint.mod -o=$(GOBIN)/faillint-v1.11.0 "github.com/fatih/faillint" 37 | 38 | GOIMPORTS := $(GOBIN)/goimports-v0.0.0-20210112230658-8b4aab62c064 39 | $(GOIMPORTS): $(BINGO_DIR)/goimports.mod 40 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 41 | @echo "(re)installing $(GOBIN)/goimports-v0.0.0-20210112230658-8b4aab62c064" 42 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=goimports.mod -o=$(GOBIN)/goimports-v0.0.0-20210112230658-8b4aab62c064 "golang.org/x/tools/cmd/goimports" 43 | 44 | GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.55.2 45 | $(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod 46 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 47 | @echo "(re)installing $(GOBIN)/golangci-lint-v1.55.2" 48 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.55.2 "github.com/golangci/golangci-lint/cmd/golangci-lint" 49 | 50 | MDOX := $(GOBIN)/mdox-v0.9.0 51 | $(MDOX): $(BINGO_DIR)/mdox.mod 52 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 53 | @echo "(re)installing $(GOBIN)/mdox-v0.9.0" 54 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mdox.mod -o=$(GOBIN)/mdox-v0.9.0 "github.com/bwplotka/mdox" 55 | 56 | MISSPELL := $(GOBIN)/misspell-v0.3.4 57 | $(MISSPELL): $(BINGO_DIR)/misspell.mod 58 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 59 | @echo "(re)installing $(GOBIN)/misspell-v0.3.4" 60 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=misspell.mod -o=$(GOBIN)/misspell-v0.3.4 "github.com/client9/misspell/cmd/misspell" 61 | 62 | PROXY := $(GOBIN)/proxy-v0.10.0 63 | $(PROXY): $(BINGO_DIR)/proxy.mod 64 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 65 | @echo "(re)installing $(GOBIN)/proxy-v0.10.0" 66 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=proxy.mod -o=$(GOBIN)/proxy-v0.10.0 "github.com/gomods/athens/cmd/proxy" 67 | 68 | -------------------------------------------------------------------------------- /.bingo/copyright.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.15 4 | 5 | require github.com/efficientgo/tools/copyright v0.0.0-20210112004814-138d5e5695fe 6 | -------------------------------------------------------------------------------- /.bingo/copyright.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= 4 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4 h1:EBTWhcAX7rNQ80RLwLCpHZBBrJuzallFHnF+yMXo928= 6 | github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= 7 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 8 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 9 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 10 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 11 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 12 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/efficientgo/tools v0.0.0-20201228165755-e2b84817bf79 h1:Pi2rMrMQeQ45UAZr8Ple+eSk9WCyglYpkFh0r22E00c= 15 | github.com/efficientgo/tools v0.0.0-20201228165755-e2b84817bf79/go.mod h1:jQUsxCcf91LHRhOnGqrx/yrleJbosynzf29/UlCbzlk= 16 | github.com/efficientgo/tools/copyright v0.0.0-20210112004814-138d5e5695fe h1:UxrVXSiy2FNfp1y+Hli0iooqqls5Yoh11KPrR9GOhg8= 17 | github.com/efficientgo/tools/copyright v0.0.0-20210112004814-138d5e5695fe/go.mod h1:5J0wuuxLMX06WeEgnpf+SvTCptlR9+RHRNO/WEMAwSw= 18 | github.com/efficientgo/tools/core v0.0.0-20210106193344-1108f4e7d16b h1:yi5z8x/FKDHrqtEFiAsxF5b7Sz2+CJrRwBC2kbyhVcA= 19 | github.com/efficientgo/tools/core v0.0.0-20210106193344-1108f4e7d16b/go.mod h1:RJm2+KCRfMUwgEgRte3obd5uLdVY5YbDZjgSMPY0HSA= 20 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 21 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 22 | github.com/felixge/fgprof v0.9.1/go.mod h1:7/HK6JFtFaARhIljgP2IV8rJLIoHDoOYoUphsnGvqxE= 23 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 24 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 27 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 28 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 29 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 30 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 31 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 32 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 33 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 34 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 35 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 36 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 37 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 38 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 39 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 40 | github.com/google/pprof v0.0.0-20200615235658-03e1cf38a040/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 41 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 42 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 43 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 44 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 45 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 46 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 47 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 48 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 49 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 50 | github.com/protoconfig/protoconfig/go v0.0.0-20210106192113-733758adefac h1:PWrv6uwNBua14NbS74ukVgXgdRDQPx/2B+Rf6KXXoQk= 51 | github.com/protoconfig/protoconfig/go v0.0.0-20210106192113-733758adefac/go.mod h1:ig8lL2CeTS14ijDIIRoi6pTap0BHc0Xrnke+SKmn9QM= 52 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 53 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 54 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 55 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 56 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 57 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 58 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 59 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 60 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 61 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 62 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 63 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 64 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 65 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 66 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 67 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 68 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 69 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 70 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 71 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 72 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 73 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 74 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 75 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 76 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 77 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 78 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 79 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 80 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 81 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 82 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 84 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 86 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 87 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 88 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 89 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 90 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 91 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 92 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 93 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 94 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 95 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 96 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 97 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 98 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 99 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 100 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 101 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 102 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 103 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 104 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 105 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 106 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 107 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 108 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 109 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 110 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 111 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 112 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 113 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 114 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 115 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 116 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 117 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 118 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 119 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 120 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 121 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 122 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 123 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 124 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 125 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 126 | -------------------------------------------------------------------------------- /.bingo/embedmd.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/campoy/embedmd v1.0.0 6 | -------------------------------------------------------------------------------- /.bingo/embedmd.sum: -------------------------------------------------------------------------------- 1 | github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= 2 | github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | -------------------------------------------------------------------------------- /.bingo/faillint.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/fatih/faillint v1.11.0 6 | -------------------------------------------------------------------------------- /.bingo/faillint.sum: -------------------------------------------------------------------------------- 1 | dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 h1:o4lAkfETerCnr1kF9/qwkwjICnU+YLHNDCM8h2xj7as= 2 | dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363/go.mod h1:WG7q7swWsS2f9PYpt5DoEP/EBYWx8We5UoRltn9vJl8= 3 | github.com/fatih/faillint v1.5.0 h1:fUolG+EsD6zdRW4rapzrM0tSf7VdpxWG3GLCPafUOcE= 4 | github.com/fatih/faillint v1.5.0/go.mod h1:e+3n7K3aI9fnZS4aNCggnoB+uBF4hwjqqR7BmA35PCE= 5 | github.com/fatih/faillint v1.8.0 h1:wV/mhyU+FcDtXx4RByy+H2FjrwHU9hEiFMyWPYmKqPE= 6 | github.com/fatih/faillint v1.8.0/go.mod h1:Yu1OT32SIjnX4Kn/h4/YPQOuNfuITtL3Gps70ac4lQk= 7 | github.com/fatih/faillint v1.11.0 h1:EhmAKe8k0Cx2gnf+/JiX/IAeeKjwsQao5dY8oG6cQB4= 8 | github.com/fatih/faillint v1.11.0/go.mod h1:d9kdQwFcr+wD4cLXOdjTw1ENUUvv5+z0ctJ5Wm0dTvA= 9 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 10 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 12 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 13 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 14 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 15 | golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= 16 | golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= 17 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 18 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 19 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 20 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 21 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 22 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 23 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 24 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 25 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 26 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 27 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 28 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 29 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= 33 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 36 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 38 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 39 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 40 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 41 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 42 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 43 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 44 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 45 | golang.org/x/tools v0.0.0-20200207224406-61798d64f025 h1:i84/3szN87uN9jFX/jRqUbszQto2oAsFlqPf6lbR8H4= 46 | golang.org/x/tools v0.0.0-20200207224406-61798d64f025/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 47 | golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= 48 | golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= 49 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 50 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 51 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 52 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 53 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 54 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 55 | -------------------------------------------------------------------------------- /.bingo/go.mod: -------------------------------------------------------------------------------- 1 | module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files. -------------------------------------------------------------------------------- /.bingo/goimports.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 // cmd/goimports 6 | -------------------------------------------------------------------------------- /.bingo/goimports.sum: -------------------------------------------------------------------------------- 1 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 2 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 3 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 4 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 5 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 6 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 7 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 8 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 9 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 10 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 12 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 13 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 14 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 15 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 16 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 17 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 18 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 19 | golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 h1:BmCFkEH4nJrYcAc2L08yX5RhYGD4j58PTMkEUDkpz2I= 20 | golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 21 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 23 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 24 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 25 | -------------------------------------------------------------------------------- /.bingo/golangci-lint.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.20 4 | 5 | require github.com/golangci/golangci-lint v1.55.2 // cmd/golangci-lint 6 | -------------------------------------------------------------------------------- /.bingo/mdox.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.15 4 | 5 | require github.com/bwplotka/mdox v0.9.0 6 | -------------------------------------------------------------------------------- /.bingo/misspell.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/client9/misspell v0.3.4 // cmd/misspell 6 | -------------------------------------------------------------------------------- /.bingo/misspell.sum: -------------------------------------------------------------------------------- 1 | github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= 2 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 3 | -------------------------------------------------------------------------------- /.bingo/proxy.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.15 4 | 5 | // Current version of golang.org/x/sys in github.com/gomods/athens does not 6 | // compile on darwin/arm64 7 | replace golang.org/x/sys => golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f 8 | 9 | require github.com/gomods/athens v0.10.0 // cmd/proxy 10 | -------------------------------------------------------------------------------- /.bingo/variables.env: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 4 | GOBIN=${GOBIN:=$(go env GOBIN)} 5 | 6 | if [ -z "$GOBIN" ]; then 7 | GOBIN="$(go env GOPATH)/bin" 8 | fi 9 | 10 | 11 | COPYRIGHT="${GOBIN}/copyright-v0.0.0-20210112004814-138d5e5695fe" 12 | 13 | EMBEDMD="${GOBIN}/embedmd-v1.0.0" 14 | 15 | FAILLINT="${GOBIN}/faillint-v1.11.0" 16 | 17 | GOIMPORTS="${GOBIN}/goimports-v0.0.0-20210112230658-8b4aab62c064" 18 | 19 | GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.55.2" 20 | 21 | MDOX="${GOBIN}/mdox-v0.9.0" 22 | 23 | MISSPELL="${GOBIN}/misspell-v0.3.4" 24 | 25 | PROXY="${GOBIN}/proxy-v0.10.0" 26 | 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed 5 | # in Windows via a file share from Linux, the scripts will work. 6 | *.{cmd,[cC][mM][dD]} text eol=crlf 7 | *.{bat,[bB][aA][tT]} text eol=crlf 8 | 9 | # Force bash scripts to always use LF line endings so that if a repo is accessed 10 | # in Unix via a file share from Windows, the scripts will work. 11 | *.sh text eol=lf 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: Something isn't working as expected 4 | title: '' 5 | assignees: '' 6 | 7 | --- 8 | 9 | **`bingo version` output**: 10 | 11 | **`go version` output**: 12 | 13 | **What happened**: 14 | 15 | **What you expected to happen**: 16 | 17 | **How to reproduce it (as minimally and precisely as possible)**: 18 | 19 | **Logs (use `bingo get -v ` for verbose output)**: 20 | 21 | **Anything else we need to know**: 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: Suggest an idea for improving Thanos 4 | title: '' 5 | labels: "feature request / improvement" 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /.github/workflows/go.yaml: -------------------------------------------------------------------------------- 1 | name: go 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | pull_request: 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | name: Linters (Static Analysis) for Go 14 | steps: 15 | - name: Checkout code into the Go module directory. 16 | uses: actions/checkout@v3 17 | 18 | - name: Install Go 19 | uses: actions/setup-go@v3 20 | with: 21 | go-version: 1.21.x 22 | 23 | - uses: actions/cache@v3 24 | with: 25 | path: ~/go/pkg/mod 26 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 27 | 28 | - name: Linting & vetting. 29 | env: 30 | GOBIN: /tmp/.bin 31 | run: make lint 32 | tests: 33 | runs-on: ${{ matrix.platform }} 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x'] 38 | platform: [ubuntu-latest, macos-latest] 39 | 40 | name: Unit tests on Go ${{ matrix.go }} ${{ matrix.platform }} 41 | steps: 42 | - name: Checkout code into the Go module directory. 43 | uses: actions/checkout@v3 44 | 45 | - name: Install Go 46 | uses: actions/setup-go@v3 47 | with: 48 | go-version: ${{ matrix.go }} 49 | 50 | - uses: actions/cache@v3 51 | with: 52 | path: ~/go/pkg/mod 53 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 54 | 55 | - name: Run unit tests. 56 | env: 57 | GOBIN: /tmp/.bin 58 | run: make test 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/ 16 | .bin 17 | 18 | .idea/ 19 | examples/tmp-demo 20 | 21 | # Ignore envrc 22 | .envrc 23 | -------------------------------------------------------------------------------- /.mdox.validators.yaml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | validators: 4 | - regex: 'twitter.com' 5 | type: 'ignore' 6 | - regex: 'github.com/bwplotka/bingo/releases/' 7 | type: 'ignore' 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | See [Releases](https://github.com/bwplotka/bingo/releases) 4 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) Bartłomiej Płotka @bwplotka 2 | Licensed under the Apache License 2.0. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include .bingo/Variables.mk 2 | FILES_TO_FMT ?= $(shell find . -path ./vendor -prune -o -name '*.go' -print) 3 | MDOX_VALIDATE_CONFIG ?= .mdox.validators.yaml 4 | 5 | GO111MODULE ?= on 6 | export GO111MODULE 7 | 8 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 9 | PATH := $(PATH):$(GOBIN) 10 | 11 | # Tools. 12 | GIT ?= $(shell which git) 13 | 14 | # Support gsed on OSX (installed via brew), falling back to sed. On Linux 15 | # systems gsed won't be installed, so will use sed as expected. 16 | SED ?= $(shell which gsed 2>/dev/null || which sed) 17 | 18 | define require_clean_work_tree 19 | @git update-index -q --ignore-submodules --refresh 20 | 21 | @if ! git diff-files --quiet --ignore-submodules --; then \ 22 | echo >&2 "cannot $1: you have unstaged changes."; \ 23 | git diff-files --name-status -r --ignore-submodules -- >&2; \ 24 | echo >&2 "Please commit or stash them."; \ 25 | exit 1; \ 26 | fi 27 | 28 | @if ! git diff-index --cached --quiet HEAD --ignore-submodules --; then \ 29 | echo >&2 "cannot $1: your index contains uncommitted changes."; \ 30 | git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2; \ 31 | echo >&2 "Please commit or stash them."; \ 32 | exit 1; \ 33 | fi 34 | 35 | endef 36 | 37 | help: ## Displays help. 38 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST) 39 | 40 | .PHONY: all 41 | all: format build 42 | 43 | .PHONY: build 44 | build: ## Build bingo. 45 | @echo ">> building bingo" 46 | @go install github.com/bwplotka/bingo 47 | 48 | .PHONY: check-comments 49 | check-comments: ## Checks Go code comments if they have trailing period (excludes protobuffers and vendor files). Comments with more than 3 spaces at beginning are omitted from the check, example: '// - foo'. 50 | @echo ">> checking Go comments trailing periods\n\n\n" 51 | @./scripts/build-check-comments.sh 52 | 53 | .PHONY: deps 54 | deps: ## Ensures fresh go.mod and go.sum. 55 | @go mod tidy 56 | @go mod verify 57 | 58 | .PHONY: docs 59 | docs: build $(MDOX) ## Generates config snippets, doc formatting and check links. 60 | @echo ">> generating docs $(PATH)" 61 | @$(MDOX) fmt -l --links.validate.config-file=$(MDOX_VALIDATE_CONFIG) *.md 62 | 63 | .PHONY: check-docs 64 | check-docs: build $(MDOX) ## Generates config snippets and doc formatting and checks links. 65 | @echo ">> checking docs $(PATH)" 66 | @$(MDOX) fmt --check -l --links.validate.config-file=$(MDOX_VALIDATE_CONFIG) *.md 67 | 68 | .PHONY: format 69 | format: ## Formats Go code including imports and cleans up white noise. 70 | format: $(GOIMPORTS) 71 | @echo ">> formatting code" 72 | @$(GOIMPORTS) -w $(FILES_TO_FMT) 73 | 74 | .PHONY: test 75 | test: ## Runs all Go unit tests. 76 | export GOCACHE=/tmp/cache 77 | test: 78 | @echo ">> running unit tests (without cache)" 79 | @rm -rf $(GOCACHE) 80 | @go test -v -timeout=30m $(shell go list ./... | grep -v /vendor/); 81 | 82 | .PHONY: check-git 83 | check-git: 84 | ifneq ($(GIT),) 85 | @test -x $(GIT) || (echo >&2 "No git executable binary found at $(GIT)."; exit 1) 86 | else 87 | @echo >&2 "No git binary found."; exit 1 88 | endif 89 | 90 | # PROTIP: 91 | # Add 92 | # --cpu-profile-path string Path to CPU profile output file 93 | # --mem-profile-path string Path to memory profile output file 94 | # to debug big allocations during linting. 95 | lint: ## Runs various static analysis against our code. 96 | lint: $(FAILLINT) $(GOLANGCI_LINT) $(COPYRIGHT) $(MISSPELL) format check-docs check-git deps 97 | $(call require_clean_work_tree,"detected not clean master before running lint") 98 | @echo ">> verifying modules being imported" 99 | @$(FAILLINT) -paths "errors=github.com/pkg/errors" ./... 100 | @$(FAILLINT) -paths "fmt.{Print,Printf,Println,Sprint}" -ignore-tests ./... 101 | @echo ">> examining all of the Go files" 102 | @go vet -stdmethods=false ./... 103 | @echo ">> linting all of the Go files GOGC=${GOGC}" 104 | @$(GOLANGCI_LINT) run 105 | @echo ">> detecting misspells" 106 | @find . -type f | grep -v vendor/ | grep -vE '\./\..*' | xargs $(MISSPELL) -error 107 | @echo ">> ensuring Copyright headers" 108 | @$(COPYRIGHT) $(shell go list -f "{{.Dir}}" ./... | xargs -i find "{}" -name "*.go") 109 | $(call require_clean_work_tree,"detected white noise or/and files without copyright; run 'make lint' file and commit changes.") 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bingo 2 | 3 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/bwplotka/bingo) [![Latest Release](https://img.shields.io/github/release/bwplotka/bingo.svg?style=flat-square)](https://github.com/bwplotka/bingo/releases/latest) [![CI](https://github.com/bwplotka/bingo/workflows/go/badge.svg)](https://github.com/bwplotka/bingo/actions?query=workflow%3Ago) [![Go Report Card](https://goreportcard.com/badge/github.com/bwplotka/bingo)](https://goreportcard.com/report/github.com/bwplotka/bingo) [![Slack](https://img.shields.io/badge/join%20slack-%23bingo-brightgreen.svg)](https://gophers.slack.com/) 4 | 5 | `go get` like, simple CLI that allows automated versioning of Go package level binaries (e.g required as dev tools by your project!) built on top of Go Modules, allowing reproducible dev environments. 6 | 7 | [![Demo](examples/bingo-demo.gif)](examples/) 8 | 9 | ## Features 10 | 11 | From our experience all repositories and projects require some tools and binaries to be present on the machine to be able to perform various development operations like building, formatting, releasing or static analysis. For smooth development all such tools should be pinned to a certain version and bounded to the code commits there were meant to be used against. 12 | 13 | Go modules does not aim to solve this problem, and even if they will do at some point it will not be on the package level, which makes it impossible to e.g pin minor version `X.Y.0` of package `module1/cmd/abc` and version `X.Z.0` of `module1/cmd/def`. 14 | 15 | At the end `bingo`, has following features: 16 | 17 | * It allows maintaining separate, hidden, nested Go modules for Go buildable packages you need **without obfuscating your own module or worrying with tool's cross dependencies**! 18 | * Package level versioning, which allows versioning different (or the same!) package multiple times from a single module in different versions. 19 | * Works also for non-Go projects. It only requires the tools to be written in Go. 20 | * No need to install `bingo` in order to **use** pinned tools. This avoids the "chicken & egg" problem. You only need `go build`. 21 | * Easy upgrade, downgrade, addition, and removal of the needed binary's version, with no risk of dependency conflicts. 22 | * NOTE: Tools are **often** not following semantic versioning, so `bingo` allows to pin by commit ID. 23 | * Immutable binary names. This creates a reliable way for users and CIs to use expected version of the binaries, reinstalling on-demand only if needed. 24 | * Works with all buildable Go projects, including pre Go modules and complex projects with complex directives like `replace`, `retract` or `exclude` statements. (e.g Prometheus) 25 | * Optional, automatic integration with Makefiles. 26 | 27 | You can read full a story behind `bingo` [in this blog post](https://www.bwplotka.dev/2020/bingo/). 28 | 29 | ## Requirements 30 | 31 | * Go 1.18+ 32 | * Linux or MacOS (Want Windows support? [Helps us out](https://github.com/bwplotka/bingo/issues/26)) 33 | * All tools that you wish to "pin" have to be built in Go (they don't need to use Go modules at all). 34 | 35 | ## Installing 36 | 37 | In your repository (does not need to be a Go project) 38 | 39 | ```shell 40 | go install github.com/bwplotka/bingo@latest 41 | ``` 42 | 43 | > For [go version before 1.17](https://go.dev/doc/go-get-install-deprecation) use `go get github.com/bwplotka/bingo` instead. 44 | 45 | Recommended: Ideally you want to pin `bingo` tool to the single version too (inception!). Do it via: 46 | 47 | ```shell 48 | bingo get -l github.com/bwplotka/bingo 49 | ``` 50 | 51 | ## Usage 52 | 53 | ### `go get` but for binaries! 54 | 55 | The key idea is that you can manage your tools similar to your Go dependencies via `go get`: 56 | 57 | ```shell 58 | bingo get [[@version1 or none,version2,version3...]] 59 | ``` 60 | 61 | For example: 62 | 63 | * `bingo get github.com/fatih/faillint` 64 | * `bingo get github.com/fatih/faillint@latest` 65 | * `bingo get github.com/fatih/faillint@v1.5.0` 66 | * `bingo get github.com/fatih/faillint@v1.1.0,v1.5.0` 67 | 68 | After this, make sure to commit `.bingo` directory in git repository, so the tools will stay versioned! Once pinned, anyone can install correct version of the tool with correct dependencies by either doing: 69 | 70 | ```bash 71 | bingo get 72 | ``` 73 | 74 | For example `bingo get faillint` 75 | 76 | ... or without `bingo`: 77 | 78 | ```bash 79 | go build -mod=mod -modfile .bingo/.mod -o=$GOBIN/- 80 | ``` 81 | 82 | For example `go build -mod=mod -modfile .bingo/faillint.mod -o=$GOBIN/faillint-v1.5.0` 83 | 84 | `bingo` allows to easily maintain a separate, nested Go Module for each binary. By default, it will keep it `.bingo/.mod` This allows to correctly pin the binary without polluting the main go module or other's tool module. 85 | 86 | ### Using Installed Tools 87 | 88 | `bingo get` builds pinned tool or tools in your `$GOBIN` path. Binaries have a name following `-` pattern. So after installation you can do: 89 | 90 | * From shell: 91 | 92 | ```bash 93 | ${GOBIN}/- 94 | ``` 95 | 96 | For example: `${GOBIN}/faillint-v1.5.0` 97 | 98 | While it's not the easiest for humans to read or type, it's essential to ensure your scripts use pinned version instead of some non-deterministic "latest version". 99 | 100 | > NOTE: If you use `-l` option, bingo creates symlink to . Use it with care as it's easy to have side effects by having another binary with same name e.g on CI. 101 | 102 | `bingo` does not have `run` command [(for a reason)](https://github.com/bwplotka/bingo/issues/52), it provides useful helper variables for script or adhoc use: 103 | 104 | > NOTE: Below helpers makes it super easy to install or use pinned binaries without even installing `bingo` (it will use just `go build`!) 💖 105 | 106 | * From shell: 107 | 108 | ```bash 109 | source .bingo/variables.env 110 | ${} 111 | ``` 112 | 113 | * From Makefile: 114 | 115 | ```Makefile 116 | include .bingo/Variables.mk 117 | run: 118 | $() 119 | ``` 120 | 121 | ### Real life examples! 122 | 123 | Let's show a few, real, sometimes novel examples showcasing `bingo` capabilities: 124 | 125 | 1. [`golangci-lint`](https://github.com/golangci/golangci-lint) is all-in-one lint framework. It's important to pin it on CI so CI runs are reproducible no matter what new linters are added, removed or changed in new release. Let's pin it to `v1.35.2` and use path recommended by https://golangci-lint.run/welcome/install/#install-from-source doc: ` github.com/golangci/golangci-lint/cmd/golangci-lint` (funny enough they discourage `go get` exactly because of the lack of pinning features `bingo` have!) 126 | 127 | ```shell 128 | bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.35.2 129 | ``` 130 | 131 | This will pin to that commit and install `${GOBIN}/golangci-lint-v1.35.2` 132 | 133 | 2. It's very common in Go world to use `goimports`, popular `gofmt` replacement which formats Go code including imports. However, not many know that it's breaking compatibility a lot between versions (there are no releases). If you want to assert certain formatting of the Go code in the CI etc your only option is to pin `goimports` version. You can do it via `bingo get`: 134 | 135 | ```shell 136 | bingo get golang.org/x/tools/cmd/goimports@latest 137 | ``` 138 | 139 | This will install (at the time of writing) latest binary: `${GOBIN}/goimports-v0.0.0-20210112230658-8b4aab62c064` 140 | 141 | 3. You rather like older formatting? No issue, let's downgrade. Since `goimports` was already installed you can reference it by just `goimports`. Let's pick the commit we want e.g `e64124511800702a4d8d79e04cf6f1af32e7bef2`: 142 | 143 | ```shell 144 | bingo get goimports@e64124511800702a4d8d79e04cf6f1af32e7bef2 145 | ``` 146 | 147 | This will pin to that commit and install `${GOBIN}/goimports-v0.0.0-20200519204825-e64124511800` 148 | 149 | 4. Installing (and pinning) multiple versions: 150 | 151 | ```shell 152 | bingo get goimports@e64124511800702a4d8d79e04cf6f1af32e7bef2,v0.0.0-20200601175630-2caf76543d99,af9456bb636557bdc2b14301a9d48500fdecc053 153 | ``` 154 | 155 | This will pin and install three versions of goimports. Very useful to compatibility testing. 156 | 157 | 5. Updating to the current latest: 158 | 159 | ```shell 160 | bingo get goimports@latest 161 | ``` 162 | 163 | This will find the latest module version, pin and install it. 164 | 165 | 6. Listing binaries you have pinned: 166 | 167 | ```shell 168 | bingo list 169 | ``` 170 | 171 | 7. Unpinning `goimports` totally from the project: 172 | 173 | ```shell 174 | bingo get goimports@none 175 | ``` 176 | 177 | > PS: `go get` also allows `@none` suffix! Did you know? I didn't (:* 178 | 179 | 8. Installing all tools: 180 | 181 | ```shell 182 | bingo get 183 | ``` 184 | 185 | 9. **Bonus**: Have you ever dreamed to pin command from bigger project like... `thanos`? I was. Can you even install it using Go tooling? Let's try: 186 | 187 | ```shell 188 | go get github.com/thanos-io/thanos/cmd/thanos@v0.17.2 189 | # Output: go: cannot use path@version syntax in GOPATH mode 190 | ``` 191 | 192 | Ups you cannot use this in non-Go project at all... (: Let's create setup go mod and retry: 193 | 194 | ```shell 195 | go mod init _ 196 | # Output: go: creating new go.mod: module 197 | go get github.com/thanos-io/thanos/cmd/thanos@v0.17.2 198 | # go get github.com/thanos-io/thanos/cmd/thanos@v0.17.2 199 | # go: downloading github.com/thanos-io/thanos v0.17.2 200 | # go: found github.com/thanos-io/thanos/cmd/thanos in github.com/thanos-io/thanos v0.17.2 201 | # go get: github.com/thanos-io/thanos@v0.17.2 requires 202 | # github.com/cortexproject/cortex@v1.5.1-0.20201111110551-ba512881b076 requires 203 | # github.com/thanos-io/thanos@v0.13.1-0.20201030101306-47f9a225cc52 requires 204 | # github.com/cortexproject/cortex@v1.4.1-0.20201030080541-83ad6df2abea requires 205 | # github.com/thanos-io/thanos@v0.13.1-0.20201019130456-f41940581d9a requires 206 | # github.com/cortexproject/cortex@v1.3.1-0.20200923145333-8587ea61fe17 requires 207 | # github.com/thanos-io/thanos@v0.13.1-0.20200807203500-9b578afb4763 requires 208 | # github.com/cortexproject/cortex@v1.2.1-0.20200805064754-d8edc95e2c91 requires 209 | # github.com/thanos-io/thanos@v0.13.1-0.20200731083140-69b87607decf requires 210 | # github.com/cortexproject/cortex@v0.6.1-0.20200228110116-92ab6cbe0995 requires 211 | # github.com/prometheus/alertmanager@v0.19.0 requires 212 | # github.com/prometheus/prometheus@v0.0.0-20190818123050-43acd0e2e93f requires 213 | # k8s.io/client-go@v12.0.0+incompatible: reading https://proxy.golang.org/k8s.io/client-go/@v/v12.0.0+incompatible.mod: 410 Gone 214 | # server response: not found: k8s.io/client-go@v12.0.0+incompatible: invalid version: +incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required 215 | ``` 216 | 217 | The reasoning is complex but [TL;DR: Go Modules are just sometimes hard to be properly used for some projects](https://twitter.com/bwplotka/status/1347104281120403458). This is why bigger projects like `Kubernetes`, `Prometheus` or `Thanos` has to use `replace` statements (plus others like `exclude` or `retract`). To make this `go get` work we would need to manually craft `replace` statements in our own go `mod` file. But what if we don't want to do that or don't know how or simply we want to install pinned version of Thanos locally without having Go project? Just use bingo: 218 | 219 | ```shell 220 | bingo get github.com/thanos-io/thanos/cmd/thanos@v0.17.2 221 | ${GOBIN}/thanos-v0.17.2 --help 222 | ``` 223 | 224 | ## Advanced Techniques 225 | 226 | * Using advanced go build flags and environment variables. 227 | 228 | To tell bingo to use certain env vars and tags during build time, just add them as a comment to the go.mod file manually and do `bingo get`. Done! 229 | 230 | NOTE: Order of comment matters. First bingo expects relative package name (optional), then environment variables, then flags. All space delimited. 231 | 232 | Real example from production project that relies on extended Hugo. 233 | 234 | ``` 235 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 236 | 237 | go 1.16 238 | 239 | require github.com/gohugoio/hugo v0.83.1 // CGO_ENABLED=1 -tags=extended 240 | ``` 241 | 242 | Run `bingo list` to see if build options are parsed correctly. Run `bingo get` to install all binaries including the modified one with new build flags. 243 | 244 | ## Production Usage 245 | 246 | To see production example see: 247 | 248 | * [bingo's own tools](https://github.com/bwplotka/bingo/tree/master/.bingo) 249 | * [Thanos's tools](https://github.com/thanos-io/thanos/tree/7bf3b0f8f3af57ac3aef033f6efb58860f273c78/.bingo) 250 | * [go-grpc-middleware's tools](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/5b83c99199db53d4258b05646007b48e4658b3af/.bingo) 251 | 252 | ## Contributing 253 | 254 | Any contributions are welcome! Just use GitHub Issues and Pull Requests as usual. We follow [Thanos Go coding style](https://thanos.io/tip/contributing/coding-style-guide.md/) guide. 255 | 256 | See an extensive and up-to-date description of the `bingo` usage below: 257 | 258 | ## Command Help 259 | 260 | ```bash mdox-exec="bingo --help" mdox-expect-exit-code=2 261 | bingo: 'go get' like, simple CLI that allows automated versioning of 262 | Go package level binaries (e.g required as dev tools by your project!) 263 | 264 | built on top of Go Modules, allowing reproducible dev environments. 265 | 'bingo' allows to easily maintain a separate, nested Go Module for each binary. 266 | 267 | For detailed examples and documentation see: https://github.com/bwplotka/bingo 268 | 269 | Usage: 270 | bingo [command] 271 | 272 | Commands: 273 | completion Generate the autocompletion script for the specified shell 274 | get add development tools to the current project (e.g: bingo get github.com/fatih/faillint@latest) 275 | list List enumerates all or one binary that are/is currently pinned in this project. 276 | version Prints bingo Version. 277 | 278 | Options: 279 | -h, --help help for bingo 280 | -m, --moddir string Directory where separate modules for each binary will be maintained. 281 | Feel free to commit this directory to your VCS to bond binary versions to your project code. 282 | If the directory does not exist bingo logs and assumes a fresh project. (default ".bingo") 283 | -v, --verbose Print more 284 | 285 | Use "bingo [command] --help" for more information about a command. 286 | ``` 287 | 288 | ## Initial Author 289 | 290 | [@bwplotka](https://bwplotka.dev) inspired by [Paul's](https://github.com/myitcv) research and with a bit of help from [Duco](https://github.com/Helcaraxan) (: 291 | -------------------------------------------------------------------------------- /builtin/embed.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package builtin 5 | 6 | import _ "embed" 7 | 8 | //go:embed help/cmd.tmpl 9 | var CommandHelpTemplate string 10 | -------------------------------------------------------------------------------- /builtin/help/cmd.tmpl: -------------------------------------------------------------------------------- 1 | Usage:{{if .Runnable}} 2 | {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} 3 | {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} 4 | 5 | Aliases: 6 | {{.NameAndAliases}}{{end}}{{if .HasExample}} 7 | 8 | Examples: 9 | {{.Example}}{{end}}{{if .HasAvailableSubCommands}} 10 | 11 | Commands:{{range .Commands}}{{if (ne .Name "help")}} 12 | {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} 13 | 14 | Options: 15 | {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} 16 | 17 | Global Flags: 18 | {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} 19 | 20 | Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} 21 | {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} 22 | 23 | Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} 24 | -------------------------------------------------------------------------------- /cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package main 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/signal" 12 | "path/filepath" 13 | "regexp" 14 | "syscall" 15 | 16 | "github.com/pkg/errors" 17 | "github.com/spf13/cobra" 18 | 19 | "github.com/bwplotka/bingo/pkg/bingo" 20 | "github.com/bwplotka/bingo/pkg/runner" 21 | "github.com/bwplotka/bingo/pkg/version" 22 | ) 23 | 24 | func NewBingoGetCommand(logger *log.Logger) *cobra.Command { 25 | var ( 26 | goCmd string 27 | rename string 28 | name string 29 | insecure bool 30 | link bool 31 | timeOut uint 32 | ) 33 | 34 | cmd := &cobra.Command{ 35 | Use: "get [flags] [[@version1 or none,version2,version3...]]", 36 | Example: "bingo get github.com/fatih/faillint\n" + 37 | "bingo get github.com/fatih/faillint@latest\n" + 38 | "bingo get github.com/fatih/faillint@v1.5.0\n" + 39 | "bingo get github.com/fatih/faillint@v1.1.0,v1.5.0\n" + 40 | "bingo get github.com/fatih/faillint@none // this will be deleted ", 41 | Short: "add development tools to the current project (e.g: bingo get github.com/fatih/faillint@latest)", 42 | Long: "go get like, simple CLI that allows automated versioning of Go package level \n" + 43 | "binaries(e.g required as dev tools by your project!) built on top of Go Modules, allowing reproducible dev environments.", 44 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 45 | if len(goCmd) == 0 { 46 | return errors.New("'go' flag cannot be empty") 47 | } 48 | if len(args) > 1 { 49 | return errors.New("too many arguments except none or binary/package") 50 | } 51 | if len(rename) > 0 && len(name) > 0 { 52 | return errors.New("Both -n and -r were specified. You can either rename or create new one.") 53 | } 54 | if len(name) > 0 && !regexp.MustCompile(`[a-zA-Z0-9.-_]+`).MatchString(name) { 55 | return errors.New("-n name contains not allowed characters") 56 | } 57 | if len(rename) > 0 && !regexp.MustCompile(`[a-zA-Z0-9.-_]+`).MatchString(rename) { 58 | return errors.New("-r name contains not allowed characters") 59 | } 60 | return nil 61 | }, 62 | RunE: func(cmd *cobra.Command, args []string) error { 63 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) 64 | defer cancel() 65 | 66 | modDirAbs, err := filepath.Abs(moddir) 67 | if err != nil { 68 | return errors.Wrap(err, "abs") 69 | } 70 | defer func() { 71 | if err == nil { 72 | // Leave tmp files on error for debug purposes. 73 | if cerr := cleanGoGetTmpFiles(modDirAbs); cerr != nil { 74 | logger.Println("cannot clean tmp files", err) 75 | } 76 | } 77 | }() 78 | 79 | r, err := runner.NewRunner(ctx, logger, insecure, goCmd) 80 | if err != nil { 81 | return err 82 | } 83 | if verbose { 84 | r.Verbose() 85 | } 86 | 87 | cfg := getConfig{ 88 | runner: r, 89 | modDir: modDirAbs, 90 | relModDir: moddir, 91 | name: name, 92 | rename: rename, 93 | link: link, 94 | timeOut: timeOut, 95 | verbose: verbose, 96 | } 97 | var target string 98 | if len(args) > 0 { 99 | target = args[0] 100 | } 101 | 102 | if err := get(ctx, logger, cfg, target); err != nil { 103 | return errors.Wrap(err, "get") 104 | } 105 | 106 | pkgs, err := bingo.ListPinnedMainPackages(logger, modDirAbs, true) 107 | if err != nil { 108 | return errors.Wrap(err, "list pinned") 109 | } 110 | if len(pkgs) == 0 { 111 | return bingo.RemoveHelpers(modDirAbs) 112 | } 113 | return bingo.GenHelpers(moddir, version.Version, pkgs) 114 | }, 115 | } 116 | flags := cmd.Flags() 117 | flags.StringVarP(&name, "name", "n", "", "The -n flag instructs to get binary and name it with given name instead of default,\n"+ 118 | "so the last element of package directory. Allowed characters [A-z0-9._-]. If -n is used and no package/binary is specified,\n"+ 119 | "bingo get will return error. If -n is used with existing binary name, copy of this binary will be done. Cannot be used with -r") 120 | flags.StringVarP(&rename, "rename", "r", "", "The -r flag instructs to get existing binary and rename it with given name. Allowed characters [A-z0-9._-]. \n"+ 121 | "If -r is used and no package/binary is specified or non existing binary name is used, bingo will return error. Cannot be used with -n.") 122 | flags.StringVar(&goCmd, "go", "go", "Path to the go command.") 123 | flags.BoolVar(&insecure, "insecure", insecure, `Use -insecure flag when using 'go get'`) 124 | flags.BoolVarP(&link, "link", "l", link, "If enabled, bingo will also create soft link called that links to the current - binary.\n"+ 125 | "Use Variables.mk and variables.env if you want to be sure that what you are invoking is what is pinned.") 126 | flags.UintVarP(&timeOut, "timeout", "t", 5, "The maximum time (in minutes) to wait for each go command before killing it.\n"+ 127 | "Set this flag to 0 to indefinitely wait on them.") 128 | return cmd 129 | } 130 | 131 | func NewBingoListCommand(logger *log.Logger) *cobra.Command { 132 | cmd := &cobra.Command{ 133 | Use: "list []", 134 | Version: version.Version, 135 | Short: "List enumerates all or one binary that are/is currently pinned in this project. ", 136 | Long: "List enumerates all or one binary that are/is currently pinned in this project. It will print exact path, Version and immutable output.", 137 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 138 | if len(args) > 1 { 139 | return errors.New("too many arguments except none or binary/package") 140 | } 141 | return nil 142 | }, 143 | RunE: func(cmd *cobra.Command, args []string) error { 144 | modDir, err := filepath.Abs(moddir) 145 | if err != nil { 146 | return errors.Wrap(err, "abs") 147 | } 148 | pkgs, err := bingo.ListPinnedMainPackages(logger, modDir, false) 149 | if err != nil { 150 | return err 151 | } 152 | var target string 153 | if len(args) > 0 { 154 | target = args[0] 155 | } 156 | bingo.SortRenderables(pkgs) 157 | return pkgs.PrintTab(target, os.Stdout) 158 | }, 159 | } 160 | return cmd 161 | } 162 | 163 | func NewBingoVersionCommand() *cobra.Command { 164 | cmd := &cobra.Command{ 165 | Use: "version", 166 | Short: "Prints bingo Version.", 167 | Long: `Prints bingo Version.`, 168 | Run: func(cmd *cobra.Command, args []string) { 169 | _, _ = fmt.Fprintln(os.Stdout, version.Version) 170 | }, 171 | } 172 | return cmd 173 | } 174 | -------------------------------------------------------------------------------- /examples/bingo-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwplotka/bingo/eebab197b1b510268e8658e9526b907dd009ff96/examples/bingo-demo.gif -------------------------------------------------------------------------------- /examples/demo-nav.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Source & docs: https://github.com/bwplotka/demo-nav 4 | 5 | # Script options (define those variables before registering commands): 6 | # 7 | # The speed to "type" the text (Default: 0 so no typing view). 8 | # TYPE_SPEED=40 9 | # 10 | # If false next command will be shown only after enter (Default: false). 11 | # IMMEDIATE_REVEAL=true 12 | # 13 | # If true prefix line with number; easier to navigate (Default: false). 14 | # NUMS=true 15 | # 16 | # If NUMS = false this prefix will be used (Default: ''). 17 | # PREFIX="CustomPrefix" 18 | # 19 | # Color vars for pretty prompts. 20 | # Feel free to use those colors in registered commands. 21 | BLACK="\033[0;30m" 22 | YELLOW="\033[1;33m" 23 | BLUE="\033[0;34m" 24 | GREEN="\033[0;32m" 25 | CYAN="\033[0;36m" 26 | RED="\033[0;31m" 27 | PURPLE="\033[0;35m" 28 | BROWN="\033[0;33m" 29 | WHITE="\033[1;37m" 30 | COLOR_RESET="\033[0m" 31 | 32 | # Shortcuts bindings. 33 | NEXT_KEY=$'\x6E' # n 34 | PREV_KEY=$'\x70' # p 35 | BEGIN_KEY=$'\x62' # b 36 | END_KEY=$'\x65' # e 37 | QUIT_KEY=$'\x71' # q 38 | INVOKE_KEY=$'\x0' # enter 39 | 40 | # Variables. 41 | PRINT=() 42 | CMDS=() 43 | CLEAN_AFTER=() 44 | 45 | # Strip ANSI escape codes/sequences [$1: input string, $2: target variable] 46 | function strip_escape_codes_and_comments() { 47 | local _input="$1" _i _j _token _escape=0 48 | local -n _output="$2"; _output="" 49 | for (( _i=0; _i < ${#_input}; _i++ )); do 50 | if (( ${_escape} == 1 )); then 51 | if [[ "${_input:_i:1}" =~ [a-zA-Z] ]]; then 52 | _escape=0 53 | fi 54 | continue 55 | fi 56 | if [[ "${_input:_i:5}" == "\033[" ]]; then 57 | _escape=1 58 | continue 59 | fi 60 | 61 | if [[ "${_input:_i:1}" == '#' ]]; then 62 | break 63 | fi 64 | _output+="${_input:_i:1}" 65 | done 66 | } 67 | 68 | ## 69 | # Registers a command into navigable script. Order of registration matters. 70 | # 71 | # Takes 1 or 2 parameters: 72 | # 1) The string command to show. 73 | # 2) Optionally: The string command to run. If empty, parameter 1 is used. 74 | # 75 | # usage: 76 | # 77 | # r "ls -l" 78 | # r "list me please" "ls -l" 79 | ## 80 | function r() { 81 | PRINT+=("${1}") 82 | 83 | TO_RUN="${2:-${1}}" 84 | 85 | # Sanitize. 86 | strip_escape_codes_and_comments "${TO_RUN}" TO_RUN_SANITIZED 87 | CMDS+=("${TO_RUN_SANITIZED}") 88 | CLEAN_AFTER+=(false) 89 | } 90 | 91 | ## 92 | # Same as 'r' but removes the command *AFTER* the execution. 93 | ## 94 | function rc() { 95 | r "$1" "$2" 96 | 97 | CLEAN_AFTER[-1]=true 98 | } 99 | 100 | ## 101 | # Runs in a mode that enables easy navigation of the 102 | # commands in the sequential manner. 103 | # 104 | # TODO(bwplotka): Add search (ctlr+r) functionality 105 | ## 106 | function navigate() { 107 | CONTINUE=${1-false} 108 | 109 | curr=0 110 | if ${CONTINUE} && [[ -f ./.demo-last-step ]]; then 111 | curr=$(< ./.demo-last-step) 112 | fi 113 | 114 | while true 115 | do 116 | # Check boundaries. 117 | if (( ${curr} < 0 )); then 118 | curr=0 119 | fi 120 | if (( ${curr} >= ${#CMDS[@]} )); then 121 | let curr="${#CMDS[@]} - 1" 122 | fi 123 | 124 | print=${PRINT[${curr}]} 125 | if [[ ${print} == "" ]]; then 126 | print=${CMDS[${curr}]} 127 | fi 128 | 129 | prefix="${PREFIX}" 130 | if ${NUMS}; then 131 | prefix="${curr}) " 132 | fi 133 | # Make sure input will not break the print. 134 | stty -echo 135 | if [[ -z $TYPE_SPEED ]]; then 136 | echo -en "${prefix}${YELLOW}$print${COLOR_RESET}" 137 | else 138 | echo -en "${prefix}${YELLOW}$print${COLOR_RESET}" | pv -qL $[$TYPE_SPEED+(-2 + RANDOM%5)]; 139 | fi 140 | stty echo 141 | 142 | # Ignore accidentally buffered input (introduces 0.5 input lag). 143 | read -rst 0.3 -n 10000 discard 144 | 145 | # Allow for interactive navigation. 146 | read -rsn1 input 147 | case "${input}" in 148 | ${BEGIN_KEY}) 149 | # Skip this command and move to beginning. 150 | curr=0 151 | echo -en "\033[2K\r" 152 | ;; 153 | ${END_KEY}) 154 | # Skip this command and move to the end. 155 | let curr="${#CMDS[@]} - 1" 156 | echo -en "\033[2K\r" 157 | ;; 158 | ${NEXT_KEY}) 159 | # Skip this command and move to next. 160 | ((curr++)) 161 | echo -en "\033[2K\r" 162 | ;; 163 | ${PREV_KEY}) 164 | # Skip this command and move to previous. 165 | ((curr--)) 166 | echo -en "\033[2K\r" 167 | ;; 168 | ${INVOKE_KEY}) 169 | # enter - Eval this and move to next. 170 | if ${CLEAN_AFTER[${curr}]}; then 171 | echo -en "\033[2K\r" 172 | else 173 | echo "" 174 | fi 175 | eval "${CMDS[${curr}]}" 176 | ((curr++)) 177 | 178 | if ${IMMEDIATE_REVEAL}; then 179 | # Wait for enter at the end. 180 | read -rst 0.3 -n 10000 discard 181 | read -rsn1 input 182 | case ${input} in 183 | ${NEXT_KEY}) 184 | ((curr++)) 185 | echo -en "\033[2K\r" 186 | ;; 187 | ${PREV_KEY}) 188 | ((curr--)) 189 | echo -en "\033[2K\r" 190 | ;; 191 | ${QUIT_KEY}) 192 | echo "" 193 | echo "Bye!" 194 | exit 0 195 | ;; 196 | esac 197 | fi 198 | ;; 199 | ${QUIT_KEY}) 200 | # q - Quit. 201 | echo "" 202 | echo "Bye!" 203 | exit 0 204 | ;; 205 | *) 206 | # Print again, not supported input. 207 | echo -en "\r" 208 | ;; 209 | esac 210 | echo ${curr} > ./.demo-last-step 211 | done 212 | } 213 | 214 | # Internal function for checking pv tool that is used to simulate typing. 215 | function _check_pv() { 216 | command -v pv >/dev/null 2>&1 || { 217 | echo "" 218 | echo "'pv' tool is required, but it's not installed. Aborting." >&2; 219 | echo "" 220 | echo -e "${COLOR_RESET}Installing pv:" 221 | echo "" 222 | echo -e "${BLUE}Mac:${COLOR_RESET} $ brew install pv" 223 | echo "" 224 | echo -e "${BLUE}Other:${COLOR_RESET} http://www.ivarch.com/programs/pv.shtml" 225 | echo -e "${COLOR_RESET}" 226 | exit 1; 227 | } 228 | } 229 | 230 | if ! [[ -z ${TYPE_SPEED} ]]; then 231 | _check_pv 232 | fi 233 | -------------------------------------------------------------------------------- /examples/demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | 5 | # Import https://github.com/bwplotka/demo-nav bash library. 6 | TYPE_SPEED=40 7 | IMMEDIATE_REVEAL=false 8 | NUMS=false 9 | PREFIX="➜ " 10 | curl https://raw.githubusercontent.com/bwplotka/demo-nav/master/demo-nav.sh -o ${DIR}/demo-nav.sh 11 | . "${DIR}/demo-nav.sh" 12 | 13 | rm -rf ${DIR}/tmp-demo 14 | mkdir ${DIR}/tmp-demo 15 | cp -r ${DIR}/../testdata/go.* ${DIR}/tmp-demo 16 | cp -r ${DIR}/../testdata/main.go ${DIR}/tmp-demo 17 | cd ${DIR}/tmp-demo 18 | 19 | clear 20 | 21 | # `r` registers command to be invoked. 22 | # 23 | # First argument specifies what should be printed. 24 | # Second argument specifies what will be actually executed. 25 | # 26 | # NOTE: Use `'` to quote strings inside command. 27 | r "${RED}# 'bingo' Demo time!" 28 | r "${YELLOW}# Let's start with simple dev project (e.g Go project):" "ls -l" 29 | r "${GREEN}export GOBIN=\`pwd\`/.bin && export PATH=\${PATH}:\${GOBIN} ${YELLOW}# We are exporting GOBIN envvar that controls where the binaries will be built." 30 | r "${YELLOW}# Let's install 'bingo' (Go 1.14+ required):" 31 | r "${GREEN}go get -u github.com/bwplotka/bingo" 32 | r "${YELLOW}# Let's say we want to have proper lint tool, just 'bingo get' it!" 33 | r "${GREEN}bingo get github.com/golangci/golangci-lint/cmd/golangci-lint" 34 | r "${GREEN}ls -l .bingo" 35 | r "${YELLOW}# As we can see above, 'bingo' created .bingo directory which stores separate .mod file for each pinned binary." 36 | r "${GREEN}cat .bingo/golangci-lint.mod" 37 | r "${YELLOW}# In fact, this is just standard Go modules with special preserved comments. You can modify it in place as normal Go .mod file!" 38 | r "${GREEN}ls -l \${GOBIN}" 39 | r "${YELLOW}# We also can see the golangci-lint was installed. NOTE that 'bingo' produces immutable names intentionally." 40 | r "${GREEN}bingo list ${YELLOW}# 'bingo' can also list us what tools are pinned." 41 | r "${YELLOW}# Let's install exact commit of goimports, 'bingo' can do that too:" 42 | r "${GREEN}bingo get golang.org/x/tools/cmd/goimports@688b3c5d9fa5ae5ca974e3c59a6557c26007e0e6" 43 | r "${GREEN}ls -l .bingo" 44 | r "${GREEN}ls -l \${GOBIN}" 45 | r "${GREEN}bingo list" 46 | r "${GREEN}bingo get -u goimports ${YELLOW}# This is how your upgrade the pinned tool." 47 | r "${GREEN}bingo get golangci-lint@v1.23.7 ${YELLOW}# This is how your downgrade if you need to." 48 | r "${GREEN}bingo list" 49 | r "${YELLOW}# Let's remove all installed binaries now to simulate freshly cloned repository:" 50 | r "${GREEN}rm -rf \${GOBIN}" 51 | r "${YELLOW}# Now, to install ALL required tools you can either use single 'bingo' command:" 52 | r "${GREEN}go get github.com/bwplotka/bingo && bingo get && ls -l \${GOBIN}" 53 | r "${YELLOW}# ...or if you don't want to depend on bingo for read access of you repo, just Go build command (no 'bingo' required):" 54 | r "${GREEN}rm -rf \${GOBIN}" 55 | r "${GREEN}go build -modfile=.bingo/golangci-lint.mod -o=\${GOBIN}/golangci-lint-v1.23.7 github.com/golangci/golangci-lint/cmd/golangci-lint && ls -l \${GOBIN}" 56 | r "${YELLOW}# Thanks! See https://github.com/bwplotka/bingo for details. Demo created with https://github.com/bwplotka/demo-nav." 57 | 58 | # Last entry to run navigation mode. 59 | navigate 60 | -------------------------------------------------------------------------------- /get_e2e_utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package main_test 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "strings" 13 | "testing" 14 | 15 | "github.com/efficientgo/core/errors" 16 | "github.com/efficientgo/core/testutil" 17 | ) 18 | 19 | type testProject struct { 20 | pwd, root string 21 | isGoProject bool 22 | } 23 | 24 | func newTestProject(t testing.TB, base string, target string, isGoProject bool) *testProject { 25 | t.Helper() 26 | 27 | wd, err := os.Getwd() 28 | testutil.Ok(t, err) 29 | 30 | _, err = execCmd(wd, nil, "cp", "-r", base, target) 31 | testutil.Ok(t, err) 32 | 33 | if isGoProject { 34 | _, err = execCmd(wd, nil, "cp", filepath.Join(wd, "testdata", "main.go"), target) 35 | testutil.Ok(t, err) 36 | _, err = execCmd(wd, nil, "cp", filepath.Join(wd, "testdata", "go.mod"), target) 37 | testutil.Ok(t, err) 38 | _, err = execCmd(wd, nil, "cp", filepath.Join(wd, "testdata", "go.sum"), target) 39 | testutil.Ok(t, err) 40 | } 41 | 42 | _, err = execCmd(wd, nil, "cp", filepath.Join(wd, "testdata", "Makefile"), target) 43 | testutil.Ok(t, err) 44 | return &testProject{ 45 | pwd: wd, 46 | root: target, 47 | isGoProject: isGoProject, 48 | } 49 | } 50 | 51 | func (g *testProject) assertNotChanged(t testing.TB, except ...string) { 52 | t.Helper() 53 | 54 | if g.isGoProject { 55 | g.assertGoModDidNotChange(t).assertGoSumDidNotChange(t) 56 | except = append(except, "main.go", "go.sum", "go.mod") 57 | } 58 | g.assertProjectRootIsClean(t, except...) 59 | } 60 | 61 | func (g *testProject) assertGoModDidNotChange(t testing.TB) *testProject { 62 | t.Helper() 63 | 64 | a, err := os.ReadFile(filepath.Join(g.root, "go.mod")) 65 | testutil.Ok(t, err) 66 | 67 | b, err := os.ReadFile(filepath.Join(g.pwd, "testdata", "go.mod")) 68 | testutil.Ok(t, err) 69 | 70 | testutil.Equals(t, string(b), string(a)) 71 | 72 | return g 73 | } 74 | 75 | func (g *testProject) assertGoSumDidNotChange(t testing.TB) *testProject { 76 | t.Helper() 77 | 78 | a, err := os.ReadFile(filepath.Join(g.root, "go.sum")) 79 | testutil.Ok(t, err) 80 | 81 | b, err := os.ReadFile(filepath.Join(g.pwd, "testdata", "go.sum")) 82 | testutil.Ok(t, err) 83 | 84 | testutil.Equals(t, string(b), string(a)) 85 | return g 86 | } 87 | 88 | func (g *testProject) assertProjectRootIsClean(t testing.TB, extra ...string) *testProject { 89 | t.Helper() 90 | 91 | expected := map[string]struct{}{ 92 | "Makefile": {}, 93 | } 94 | for _, e := range extra { 95 | expected[e] = struct{}{} 96 | } 97 | if g.isGoProject { 98 | expected["go.mod"] = struct{}{} 99 | expected["go.sum"] = struct{}{} 100 | expected["main.go"] = struct{}{} 101 | } 102 | 103 | i, err := os.ReadDir(g.root) 104 | testutil.Ok(t, err) 105 | got := map[string]struct{}{} 106 | for _, f := range i { 107 | got[f.Name()] = struct{}{} 108 | } 109 | testutil.Equals(t, expected, got) 110 | 111 | return g 112 | } 113 | 114 | type goEnv struct { 115 | goroot, gopath, goproxy, gobin, gocache, tmpDir string 116 | } 117 | 118 | func execCmd(dir string, env []string, command string, args ...string) (string, error) { 119 | var cmd *exec.Cmd 120 | if env == nil { 121 | cmd = exec.Command(command, args...) 122 | } else { 123 | // Since we want to have synthetic PATH, do not allows unspecified paths. 124 | // Otherwise unit test environment PATH will be used for lookup as exec.LookPath is not parametrized. 125 | // TL;DR: command has to have path separator. 126 | cmd = &exec.Cmd{ 127 | Env: env, 128 | Path: command, 129 | Args: append([]string{command}, args...), 130 | } 131 | } 132 | cmd.Dir = dir 133 | var b bytes.Buffer 134 | cmd.Stdout = &b 135 | cmd.Stderr = &b 136 | if err := cmd.Run(); err != nil { 137 | if _, ok := err.(*exec.ExitError); ok { 138 | return "", errors.Newf("error while running command %q; out: %s; err: %v", cmd.String(), b.String(), err) 139 | 140 | } 141 | return "", errors.Newf("error while running command %q; out: %s; err: %v", cmd.String(), b.String(), err) 142 | } 143 | return b.String(), nil 144 | } 145 | 146 | func buildInitialGobin(t *testing.T, targetDir string) { 147 | t.Helper() 148 | 149 | wd, err := os.Getwd() 150 | testutil.Ok(t, err) 151 | 152 | testutil.Ok(t, os.Setenv("GOBIN", filepath.Join(wd, ".bin"))) 153 | 154 | _, err = execCmd(wd, nil, "make", "build") 155 | testutil.Ok(t, err) 156 | _, err = execCmd(wd, nil, "cp", filepath.Join(os.Getenv("GOBIN"), bingoBin), targetDir) 157 | testutil.Ok(t, err) 158 | } 159 | 160 | func makePath(t *testing.T) string { 161 | t.Helper() 162 | 163 | makePath, err := execCmd("", nil, "which", "make") 164 | testutil.Ok(t, err) 165 | return strings.TrimSuffix(makePath, "\n") 166 | } 167 | 168 | func newIsolatedGoEnv(t testing.TB, goproxy string) *goEnv { 169 | tmpDir, err := os.MkdirTemp("", "bingo-tmpgoenv") 170 | testutil.Ok(t, err) 171 | 172 | tmpDir, err = filepath.Abs(tmpDir) 173 | testutil.Ok(t, err) 174 | 175 | goRoot, err := execCmd("", nil, "which", "go") 176 | testutil.Ok(t, err) 177 | 178 | gopath := filepath.Join(tmpDir, "gopath") 179 | return &goEnv{ 180 | tmpDir: tmpDir, 181 | goroot: filepath.Dir(goRoot), 182 | gopath: gopath, 183 | // Making sure $GOBIN is actually different than standard one to test advanced stuff. 184 | gobin: filepath.Join(tmpDir, "bin"), 185 | gocache: filepath.Join(tmpDir, "gocache"), 186 | goproxy: goproxy, 187 | } 188 | } 189 | 190 | // Clear clears all go env dirs but not goroot, gopath and gocache. 191 | func (g *goEnv) Clear(t testing.TB) { 192 | t.Helper() 193 | 194 | _, err := execCmd("", nil, "chmod", "-R", "777", g.tmpDir) 195 | testutil.Ok(t, err) 196 | 197 | dirs, err := os.ReadDir(g.tmpDir) 198 | testutil.Ok(t, err) 199 | 200 | for _, d := range dirs { 201 | switch filepath.Join(g.tmpDir, d.Name()) { 202 | case g.gocache, g.goroot, g.gopath: 203 | default: 204 | testutil.Ok(t, os.RemoveAll(filepath.Join(g.tmpDir, d.Name()))) 205 | } 206 | } 207 | } 208 | 209 | func (g *goEnv) TmpDir() string { 210 | return g.tmpDir 211 | } 212 | 213 | func (g *goEnv) syntheticEnv() []string { 214 | return []string{ 215 | // Make sure we don't require clang to build etc. 216 | "CGO_ENABLED=0", 217 | fmt.Sprintf("PATH=%s:%s:%s", g.goroot, g.tmpDir, g.gobin), 218 | fmt.Sprintf("GO=%s", filepath.Join(g.goroot, "go")), 219 | fmt.Sprintf("GOBIN=%s", g.gobin), 220 | fmt.Sprintf("GOPATH=%s", g.gopath), 221 | fmt.Sprintf("GOCACHE=%s", g.gocache), 222 | fmt.Sprintf("GOPROXY=%s", g.goproxy), 223 | } 224 | } 225 | 226 | func (g *goEnv) ExecOutput(t testing.TB, dir string, command string, args ...string) string { 227 | t.Helper() 228 | 229 | b, err := execCmd(dir, g.syntheticEnv(), command, args...) 230 | testutil.Ok(t, err) 231 | return b 232 | } 233 | 234 | func (g *goEnv) ExpectErr(dir string, command string, args ...string) error { 235 | _, err := execCmd(dir, g.syntheticEnv(), command, args...) 236 | return err 237 | } 238 | 239 | func (g *goEnv) existingBinaries(t *testing.T) []string { 240 | t.Helper() 241 | 242 | var filenames []string 243 | files, err := os.ReadDir(g.gobin) 244 | if os.IsNotExist(err) { 245 | return []string{} 246 | } 247 | testutil.Ok(t, err) 248 | 249 | for _, f := range files { 250 | if f.IsDir() { 251 | t.Fatal("Did not expect directory in gobin", g.gobin, "got", f.Name()) 252 | } 253 | filenames = append(filenames, f.Name()) 254 | } 255 | return filenames 256 | } 257 | 258 | func (g *goEnv) Close(t testing.TB) { 259 | t.Helper() 260 | 261 | _, err := execCmd("", nil, "chmod", "-R", "777", g.tmpDir) 262 | testutil.Ok(t, err) 263 | testutil.Ok(t, os.RemoveAll(g.tmpDir)) 264 | } 265 | -------------------------------------------------------------------------------- /get_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package main 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/efficientgo/core/errors" 10 | "github.com/efficientgo/core/testutil" 11 | ) 12 | 13 | func TestParseTarget(t *testing.T) { 14 | for _, tcase := range []struct { 15 | target string 16 | 17 | expectedName string 18 | expectedPkgPath string 19 | expectedVersions []string 20 | expectedErr error 21 | }{ 22 | { 23 | target: "", 24 | expectedErr: errors.New("target is empty, this should be filtered earlier"), 25 | }, 26 | { 27 | target: "tool", 28 | expectedName: "tool", 29 | expectedVersions: []string{""}, 30 | }, 31 | { 32 | target: "github.com/bwplotka/bingo", 33 | expectedName: "bingo", expectedPkgPath: "github.com/bwplotka/bingo", 34 | expectedVersions: []string{""}, 35 | }, 36 | { 37 | target: "sigs.k8s.io/kustomize/kustomize", 38 | expectedName: "kustomize", expectedPkgPath: "sigs.k8s.io/kustomize/kustomize", 39 | expectedVersions: []string{""}, 40 | }, 41 | { 42 | target: "sigs.k8s.io/kustomize/kustomize/v3", 43 | expectedName: "kustomize", expectedPkgPath: "sigs.k8s.io/kustomize/kustomize/v3", 44 | expectedVersions: []string{""}, 45 | }, 46 | { 47 | target: "github.com/bwplotka/bingo/v21314213532", 48 | expectedName: "bingo", expectedPkgPath: "github.com/bwplotka/bingo/v21314213532", 49 | expectedVersions: []string{""}, 50 | }, 51 | { 52 | target: "tool@version1", 53 | expectedName: "tool", expectedVersions: []string{"version1"}, 54 | }, 55 | { 56 | target: "tool@version1123,version3,version1241", 57 | expectedName: "tool", expectedVersions: []string{"version1123", "version3", "version1241"}, 58 | }, 59 | { 60 | target: "tool@none", 61 | expectedName: "tool", expectedVersions: []string{"none"}, 62 | }, 63 | { 64 | target: "tool@version1123,version13,version1123", 65 | expectedErr: errors.New("version duplicates are not allowed, got: [version1123 version13 version1123]"), 66 | }, 67 | { 68 | target: "tool@version1123,version13,none", 69 | expectedErr: errors.New("none is not allowed when there are more than one specified Version, got: [version1123 version13 none]"), 70 | }, 71 | { 72 | target: "github.com/bwplotka/bingo/v2@v0.2.5-rc.1214,bb92924b84d060515f8eb35f428a8fd816c1d938,version1241", 73 | expectedName: "bingo", expectedPkgPath: "github.com/bwplotka/bingo/v2", expectedVersions: []string{"v0.2.5-rc.1214", "bb92924b84d060515f8eb35f428a8fd816c1d938", "version1241"}, 74 | }, 75 | } { 76 | t.Run("", func(t *testing.T) { 77 | n, p, v, err := parseTarget(tcase.target) 78 | if tcase.expectedErr != nil { 79 | testutil.NotOk(t, err) 80 | testutil.Equals(t, tcase.expectedErr.Error(), err.Error()) 81 | return 82 | } 83 | 84 | testutil.Ok(t, err) 85 | testutil.Equals(t, tcase.expectedName, n) 86 | testutil.Equals(t, tcase.expectedPkgPath, p) 87 | testutil.Equals(t, tcase.expectedVersions, v) 88 | }) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bwplotka/bingo 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/Masterminds/semver v1.5.0 7 | github.com/efficientgo/core v1.0.0-rc.0 8 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 9 | github.com/pkg/errors v0.9.1 10 | github.com/spf13/cobra v1.5.0 11 | golang.org/x/mod v0.12.0 12 | mvdan.cc/sh/v3 v3.7.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/spf13/pflag v1.0.5 // indirect 18 | golang.org/x/sync v0.2.0 // indirect 19 | golang.org/x/sys v0.8.0 // indirect 20 | golang.org/x/term v0.8.0 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 2 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 5 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 6 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 10 | github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= 11 | github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= 12 | github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 13 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 14 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 15 | github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= 16 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 17 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 18 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 19 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 20 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 21 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 22 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 23 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 24 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 25 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 26 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 27 | github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= 28 | github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 29 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 30 | github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= 31 | github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= 32 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 33 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 34 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 35 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 36 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 37 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 38 | golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 39 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 40 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 41 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 42 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 43 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 44 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 45 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 46 | golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 47 | golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 48 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 49 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 50 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 51 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 52 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 53 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 54 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 56 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 57 | golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= 58 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 59 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 60 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 61 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 62 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 63 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 64 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 65 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 66 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 67 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 68 | mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= 69 | mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= 70 | mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= 71 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | //lint:file-ignore faillint main.go can use fmt.Print* family for error logging, when logger is not ready. 5 | 6 | package main 7 | 8 | import ( 9 | "log" 10 | "os" 11 | 12 | "github.com/bwplotka/bingo/builtin" 13 | 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var verbose bool 18 | var moddir string 19 | 20 | func NewBingoCommand(logger *log.Logger) *cobra.Command { 21 | cmd := &cobra.Command{ 22 | Use: "bingo", 23 | Long: `bingo: 'go get' like, simple CLI that allows automated versioning of 24 | Go package level binaries (e.g required as dev tools by your project!) 25 | 26 | built on top of Go Modules, allowing reproducible dev environments. 27 | 'bingo' allows to easily maintain a separate, nested Go Module for each binary. 28 | 29 | For detailed examples and documentation see: https://github.com/bwplotka/bingo 30 | `, 31 | } 32 | flags := cmd.PersistentFlags() 33 | flags.BoolVarP(&verbose, "verbose", "v", false, "Print more") 34 | flags.StringVarP(&moddir, "moddir", "m", ".bingo", "Directory where separate modules for each binary will be maintained. \n"+ 35 | "Feel free to commit this directory to your VCS to bond binary versions to your project code. \n"+ 36 | "If the directory does not exist bingo logs and assumes a fresh project.") 37 | cmd.AddCommand(NewBingoGetCommand(logger)) 38 | cmd.AddCommand(NewBingoListCommand(logger)) 39 | cmd.AddCommand(NewBingoVersionCommand()) 40 | cmd.SetUsageTemplate(builtin.CommandHelpTemplate) 41 | return cmd 42 | } 43 | 44 | func main() { 45 | logger := log.New(os.Stderr, "", 0) 46 | rootCmd := NewBingoCommand(logger) 47 | err := rootCmd.Execute() 48 | if err != nil { 49 | logger.Println(err) 50 | os.Exit(1) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/bingo/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package bingo 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "text/template" 10 | 11 | "github.com/efficientgo/core/errors" 12 | ) 13 | 14 | // RemoveHelpers deletes helpers from mod directory. 15 | func RemoveHelpers(modDir string) error { 16 | for ext := range templatesByFileExt { 17 | v := "variables." + ext 18 | if ext == "mk" { 19 | // Exception: for backward compatibility. 20 | v = "Variables.mk" 21 | } 22 | if err := os.RemoveAll(filepath.Join(modDir, v)); err != nil { 23 | return err 24 | } 25 | } 26 | return nil 27 | } 28 | 29 | // GenHelpers generates helpers to allows reliable binaries use. Regenerate if needed. 30 | // It is expected to have at least one mod file. 31 | // TODO(bwplotka): Allow installing those optionally? 32 | func GenHelpers(relModDir, version string, pkgs []PackageRenderable) error { 33 | for ext, tmpl := range templatesByFileExt { 34 | v := "variables." + ext 35 | if ext == "mk" { 36 | // Exception: for backward compatibility. 37 | v = "Variables.mk" 38 | } 39 | if err := genHelper(v, tmpl, relModDir, version, pkgs); err != nil { 40 | return errors.Wrap(err, v) 41 | } 42 | } 43 | return nil 44 | } 45 | 46 | type templateData struct { 47 | Version string 48 | GobinPath string 49 | MainPackages []PackageRenderable 50 | RelModDir string 51 | } 52 | 53 | func genHelper(f, tmpl, relModDir, version string, pkgs []PackageRenderable) error { 54 | t, err := template.New(f).Parse(tmpl) 55 | if err != nil { 56 | return errors.Wrap(err, "parse template") 57 | } 58 | 59 | data := templateData{ 60 | Version: version, 61 | MainPackages: pkgs, 62 | } 63 | 64 | fb, err := os.Create(filepath.Join(relModDir, f)) 65 | if err != nil { 66 | return errors.Wrap(err, "create") 67 | } 68 | defer func() { 69 | if cerr := fb.Close(); cerr != nil { 70 | if err != nil { 71 | err = errors.Wrapf(err, "additionally error on close: %v", cerr) 72 | return 73 | } 74 | err = cerr 75 | } 76 | }() 77 | return t.Execute(fb, data) 78 | } 79 | -------------------------------------------------------------------------------- /pkg/bingo/mod.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package bingo 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "io" 10 | "log" 11 | "os" 12 | "path" 13 | "path/filepath" 14 | "sort" 15 | "strings" 16 | "text/tabwriter" 17 | 18 | "github.com/bwplotka/bingo/pkg/cpy" 19 | 20 | "github.com/bwplotka/bingo/pkg/envars" 21 | "github.com/bwplotka/bingo/pkg/mod" 22 | "github.com/bwplotka/bingo/pkg/runner" 23 | "github.com/efficientgo/core/errcapture" 24 | "github.com/efficientgo/core/errors" 25 | "golang.org/x/mod/module" 26 | ) 27 | 28 | const ( 29 | // FakeRootModFileName is a name for fake go module that we have to maintain, until https://github.com/bwplotka/bingo/issues/20 is fixed. 30 | FakeRootModFileName = "go.mod" 31 | 32 | NoDirectiveCommand = "bingo:no_directive_fetch" 33 | 34 | PackageRenderablesPrintHeader = "Name\tBinary Name\tPackage @ Version\tBuild EnvVars\tBuild Flags\n" + 35 | "----\t-----------\t-----------------\t-------------\t-----------\n" 36 | 37 | metaComment = "Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT" 38 | ) 39 | 40 | // NameFromModFile returns binary name from module file path. 41 | func NameFromModFile(modFile string) (name string, oneOfMany bool) { 42 | n := strings.Split(strings.TrimSuffix(filepath.Base(modFile), ".mod"), ".") 43 | if len(n) > 1 { 44 | oneOfMany = true 45 | } 46 | return n[0], oneOfMany 47 | } 48 | 49 | // A Package (for clients, a bingo.Package) is defined by a module path, package relative path and version pair. 50 | // These are stored in their plain (unescaped) form. 51 | type Package struct { 52 | Module module.Version 53 | 54 | // RelPath is a path that together with Module.Path composes a package path, like "/pkg/makefile". 55 | // Empty if the module is a full package path. 56 | // If Module.Path is empty and RelPath specified, it means that we don't know what is a module what is the package path. 57 | RelPath string 58 | 59 | // BuildEnvs are environment variables to be used during go build process. 60 | BuildEnvs envars.EnvSlice 61 | // BuildFlags are flags to be used during go build process. 62 | BuildFlags []string 63 | } 64 | 65 | // String returns a representation of the Package suitable for `go` tools and logging. 66 | // (Module.Path/RelPath@Module.Version, or Module.Path/RelPath if Version is empty). 67 | func (m Package) String() string { 68 | if m.Module.Version == "" { 69 | return m.Path() 70 | } 71 | return m.Path() + "@" + m.Module.Version 72 | } 73 | 74 | // Path returns a full package path. package path is platform independent, usually in 75 | // the form of "a/b/c" with forward slashes 76 | func (m Package) Path() string { 77 | return path.Join(m.Module.Path, m.RelPath) 78 | } 79 | 80 | // ModFile is a wrapper over module file with bingo specific data. 81 | type ModFile struct { 82 | *mod.File 83 | 84 | directPackage *Package 85 | directivesAutoFetchDisabled bool 86 | } 87 | 88 | // OpenModFile opens bingo mod file. 89 | // It also adds meta if missing and trims all require direct module imports except first within the parsed syntax. 90 | // It's a caller responsibility to Close the file when not using anymore. 91 | func OpenModFile(modFile string) (_ *ModFile, err error) { 92 | f, err := mod.OpenFile(modFile) 93 | if err != nil { 94 | return nil, err 95 | } 96 | defer func() { 97 | if err != nil { 98 | errcapture.Do(&err, f.Close, "close") 99 | } 100 | }() 101 | 102 | m, comment := f.Module() 103 | if m == "" { 104 | m = "_" 105 | } 106 | if comment != metaComment { 107 | if err := f.SetModule(m, metaComment); err != nil { 108 | return nil, err 109 | } 110 | } 111 | 112 | mf := &ModFile{File: f} 113 | return mf, mf.Reload() 114 | } 115 | 116 | func (mf *ModFile) IsDirectivesAutoFetchDisabled() bool { 117 | return mf.directivesAutoFetchDisabled 118 | } 119 | 120 | func (mf *ModFile) Reload() error { 121 | if err := mf.File.Reload(); err != nil { 122 | return err 123 | } 124 | 125 | for _, c := range mf.Comments() { 126 | if strings.Contains(c, NoDirectiveCommand) { 127 | mf.directivesAutoFetchDisabled = true 128 | break 129 | } 130 | } 131 | 132 | // We expect just one direct import if any. 133 | var directPackage *Package 134 | for _, r := range mf.RequireDirectives() { 135 | if r.Indirect { 136 | continue 137 | } 138 | 139 | directPackage = &Package{Module: r.Module} 140 | if len(r.ExtraSuffixComment) > 0 { 141 | directPackage.RelPath, directPackage.BuildEnvs, directPackage.BuildFlags = parseDirectPackageMeta(strings.Trim(r.ExtraSuffixComment, "\n")) 142 | } 143 | break 144 | } 145 | 146 | if directPackage != nil { 147 | return mf.SetDirectRequire(*directPackage) 148 | } 149 | return nil 150 | } 151 | 152 | func SumFilePath(modFilePath string) string { 153 | return strings.TrimSuffix(modFilePath, ".mod") + ".sum" 154 | } 155 | 156 | // CreateFromExistingOrNew creates and opens new bingo enhanced module file. 157 | // If existing file exists and is not malformed it copies this as the source, otherwise completely new is created. 158 | // It's a caller responsibility to Close the file when not using anymore. 159 | func CreateFromExistingOrNew(ctx context.Context, r *runner.Runner, logger *log.Logger, existingFile, modFile string) (*ModFile, error) { 160 | if err := os.RemoveAll(modFile); err != nil { 161 | return nil, errors.Wrap(err, "rm") 162 | } 163 | 164 | if existingFile != "" { 165 | _, err := os.Stat(existingFile) 166 | if err != nil && !os.IsNotExist(err) { 167 | return nil, errors.Wrapf(err, "stat module file %s", existingFile) 168 | } 169 | if err == nil { 170 | // Only use existing mod file on successful parse. 171 | o, err := OpenModFile(existingFile) 172 | if err == nil { 173 | if err := o.Close(); err != nil { 174 | return nil, err 175 | } 176 | if err := cpy.File(existingFile, modFile); err != nil { 177 | return nil, err 178 | } 179 | existingSumFile := SumFilePath(existingFile) 180 | sumFile := SumFilePath(modFile) 181 | if err := cpy.File(existingSumFile, sumFile); err != nil && !os.IsNotExist(err) { 182 | return nil, err 183 | } 184 | return OpenModFile(modFile) 185 | } 186 | logger.Printf("bingo tool module file %v is malformed; it will be recreated; err: %v\n", existingFile, err) 187 | } 188 | } 189 | 190 | // Create from scratch. 191 | if err := r.ModInit(ctx, filepath.Dir(existingFile), modFile, "_"); err != nil { 192 | return nil, errors.Wrap(err, "mod init") 193 | } 194 | return OpenModFile(modFile) 195 | } 196 | 197 | func parseDirectPackageMeta(line string) (relPath string, buildEnv []string, buildFlags []string) { 198 | elem := strings.Split(line, " ") 199 | for i, l := range elem { 200 | if l == "" { 201 | continue 202 | } 203 | 204 | if l[0] == '-' { 205 | buildFlags = elem[i:] 206 | break 207 | } 208 | 209 | if !strings.Contains(l, "=") { 210 | relPath = l 211 | continue 212 | } 213 | buildEnv = append(buildEnv, l) 214 | } 215 | return relPath, buildEnv, buildFlags 216 | } 217 | 218 | func (mf *ModFile) DirectPackage() *Package { 219 | return mf.directPackage 220 | } 221 | 222 | // SetDirectRequire removes all require statements and set to the given one. It supports package level versioning. 223 | func (mf *ModFile) SetDirectRequire(target Package) (err error) { 224 | r := mod.RequireDirective{Module: target.Module} 225 | 226 | var meta []string 227 | 228 | // Add sub package info if needed. 229 | if target.RelPath != "" && target.RelPath != "." { 230 | meta = append(meta, target.RelPath) 231 | } 232 | meta = append(meta, target.BuildEnvs...) 233 | meta = append(meta, target.BuildFlags...) 234 | 235 | if len(meta) > 0 { 236 | r.ExtraSuffixComment = strings.Join(meta, " ") 237 | } 238 | mf.directPackage = &target 239 | return mf.SetRequireDirectives(r) 240 | } 241 | 242 | // ModDirectPackage return the first direct package from bingo enhanced module file. The package suffix (if any) is 243 | // encoded in the line comment, in the same line as module and version. 244 | func ModDirectPackage(modFile string) (pkg Package, err error) { 245 | mf, err := OpenModFile(modFile) 246 | if err != nil { 247 | return Package{}, err 248 | } 249 | defer errcapture.Do(&err, mf.Close, "close") 250 | 251 | if mf.directPackage == nil { 252 | return Package{}, errors.Newf("no direct package found in %s; empty module?", mf.Filepath()) 253 | } 254 | return *mf.directPackage, nil 255 | } 256 | 257 | // ModIndirectModules return the all indirect mod from any module file. 258 | func ModIndirectModules(modFile string) (mods []module.Version, err error) { 259 | m, err := mod.OpenFile(modFile) 260 | if err != nil { 261 | return nil, err 262 | } 263 | 264 | for _, r := range m.RequireDirectives() { 265 | if !r.Indirect { 266 | continue 267 | } 268 | 269 | mods = append(mods, r.Module) 270 | } 271 | return mods, nil 272 | } 273 | 274 | // PackageVersionRenderable is used in variables.go. Modify with care. 275 | type PackageVersionRenderable struct { 276 | Version string 277 | ModFile string 278 | } 279 | 280 | // PackageRenderable is used in variables.go. Modify with care. 281 | type PackageRenderable struct { 282 | Name string 283 | ModPath string 284 | PackagePath string 285 | EnvVarName string 286 | Versions []PackageVersionRenderable 287 | 288 | BuildFlags []string 289 | BuildEnvVars []string 290 | } 291 | 292 | func (p PackageRenderable) ToPackages() []Package { 293 | ret := make([]Package, 0, len(p.Versions)) 294 | for _, v := range p.Versions { 295 | relPath, _ := filepath.Rel(p.ModPath, p.PackagePath) 296 | 297 | ret = append(ret, Package{ 298 | Module: module.Version{ 299 | Version: v.Version, 300 | Path: p.ModPath, 301 | }, 302 | RelPath: relPath, 303 | }) 304 | } 305 | return ret 306 | } 307 | 308 | type PackageRenderables []PackageRenderable 309 | 310 | func (pkgs PackageRenderables) PrintTab(target string, w io.Writer) error { 311 | tw := new(tabwriter.Writer) 312 | tw.Init(w, 1, 8, 1, '\t', tabwriter.AlignRight) 313 | defer func() { _ = tw.Flush() }() 314 | 315 | _, _ = fmt.Fprint(tw, PackageRenderablesPrintHeader) 316 | for _, p := range pkgs { 317 | if target != "" && p.Name != target { 318 | continue 319 | } 320 | for _, v := range p.Versions { 321 | fields := []string{ 322 | p.Name, 323 | p.Name + "-" + v.Version, 324 | p.PackagePath + "@" + v.Version, 325 | strings.Join(p.BuildEnvVars, " "), 326 | strings.Join(p.BuildFlags, " "), 327 | } 328 | _, _ = fmt.Fprintln(tw, strings.Join(fields, "\t")) 329 | } 330 | if target != "" { 331 | return nil 332 | } 333 | } 334 | 335 | if target != "" { 336 | return errors.Newf("Pinned tool %s not found", target) 337 | } 338 | return nil 339 | } 340 | 341 | // ListPinnedMainPackages lists all bingo pinned binaries (Go main packages) in the same order as seen in the filesystem. 342 | func ListPinnedMainPackages(logger *log.Logger, modDir string, remMalformed bool) (pkgs PackageRenderables, _ error) { 343 | modFiles, err := filepath.Glob(filepath.Join(modDir, "*.mod")) 344 | if err != nil { 345 | return nil, err 346 | } 347 | ModLoop: 348 | for _, f := range modFiles { 349 | if filepath.Base(f) == FakeRootModFileName { 350 | continue 351 | } 352 | 353 | pkg, err := ModDirectPackage(f) 354 | if err != nil { 355 | if remMalformed { 356 | logger.Printf("found malformed module file %v, removing due to error: %v\n", f, err) 357 | if err := os.RemoveAll(strings.TrimSuffix(f, ".") + "*"); err != nil { 358 | return nil, err 359 | } 360 | } 361 | continue 362 | } 363 | 364 | name, _ := NameFromModFile(f) 365 | varName := strings.ReplaceAll(strings.ReplaceAll(strings.ToUpper(name), ".", "_"), "-", "_") 366 | for i, p := range pkgs { 367 | if p.Name == name { 368 | pkgs[i].EnvVarName = varName + "_ARRAY" 369 | // Preserve order. Unfortunately first array mod file has no number, so it's last. 370 | if filepath.Base(f) == p.Name+".mod" { 371 | pkgs[i].Versions = append([]PackageVersionRenderable{{ 372 | Version: pkg.Module.Version, 373 | ModFile: filepath.Base(f), 374 | }}, pkgs[i].Versions...) 375 | continue ModLoop 376 | } 377 | 378 | pkgs[i].Versions = append(pkgs[i].Versions, PackageVersionRenderable{ 379 | Version: pkg.Module.Version, 380 | ModFile: filepath.Base(f), 381 | }) 382 | continue ModLoop 383 | } 384 | } 385 | pkgs = append(pkgs, PackageRenderable{ 386 | Name: name, 387 | Versions: []PackageVersionRenderable{ 388 | {Version: pkg.Module.Version, ModFile: filepath.Base(f)}, 389 | }, 390 | BuildFlags: pkg.BuildFlags, 391 | BuildEnvVars: pkg.BuildEnvs, 392 | 393 | EnvVarName: varName, 394 | PackagePath: pkg.Path(), 395 | ModPath: pkg.Module.Path, 396 | }) 397 | } 398 | return pkgs, nil 399 | } 400 | 401 | func SortRenderables(pkgs []PackageRenderable) { 402 | for _, p := range pkgs { 403 | sort.Slice(p.Versions, func(i, j int) bool { 404 | return p.Versions[i].Version < p.Versions[j].Version 405 | }) 406 | } 407 | sort.Slice(pkgs, func(i, j int) bool { 408 | if pkgs[i].Name == pkgs[j].Name { 409 | return pkgs[i].PackagePath < pkgs[j].PackagePath 410 | } 411 | return pkgs[i].Name < pkgs[j].Name 412 | }) 413 | } 414 | -------------------------------------------------------------------------------- /pkg/bingo/mod_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package bingo 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | 14 | "github.com/bwplotka/bingo/pkg/runner" 15 | "github.com/bwplotka/bingo/pkg/version" 16 | "github.com/efficientgo/core/testutil" 17 | "golang.org/x/mod/module" 18 | ) 19 | 20 | func goVersion(r *runner.Runner) string { 21 | // Starting from Go 1.21, `go mod init` adds complete semver to modfile. 22 | // Thus, we return . for < 1.21, and full semver otherwise. 23 | if r.GoVersion().Compare(version.Go121) == -1 { 24 | return fmt.Sprintf("%v.%v", r.GoVersion().Major(), r.GoVersion().Minor()) 25 | } 26 | 27 | return r.GoVersion().String() 28 | } 29 | 30 | func TestCreateFromExistingOrNew(t *testing.T) { 31 | logger := log.New(os.Stderr, "", 0) 32 | r, err := runner.NewRunner(context.TODO(), logger, false, "go") 33 | testutil.Ok(t, err) 34 | 35 | t.Run("create new and close should create empty mod file with basic autogenerated meta", func(t *testing.T) { 36 | f, err := CreateFromExistingOrNew(context.TODO(), r, log.New(os.Stderr, "", 0), "non_existing.mod", "test.mod") 37 | testutil.Ok(t, err) 38 | testutil.Ok(t, f.Close()) 39 | 40 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 41 | 42 | go %s 43 | `, goVersion(r)), "test.mod") 44 | }) 45 | t.Run("create new and close should work and produce same output", func(t *testing.T) { 46 | f, err := CreateFromExistingOrNew(context.TODO(), r, log.New(os.Stderr, "", 0), "test.mod", "test2.mod") 47 | testutil.Ok(t, err) 48 | testutil.Ok(t, f.Close()) 49 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 50 | 51 | go %s 52 | `, goVersion(r)), "test.mod") 53 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 54 | 55 | go %s 56 | `, goVersion(r)), "test2.mod") 57 | }) 58 | t.Run("create new and set direct require should work", func(t *testing.T) { 59 | f, err := CreateFromExistingOrNew(context.TODO(), r, log.New(os.Stderr, "", 0), "", "test3.mod") 60 | testutil.Ok(t, err) 61 | testutil.Ok(t, f.SetDirectRequire(Package{Module: module.Version{Path: "github.com/yolo/best/v100", Version: "v100.0.0"}, RelPath: "thebest"})) 62 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/yolo/best/v100", Version: "v100.0.0"}, RelPath: "thebest"}, *f.DirectPackage()) 63 | testutil.Ok(t, f.Close()) 64 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 65 | 66 | go %s 67 | 68 | require github.com/yolo/best/v100 v100.0.0 // thebest 69 | `, goVersion(r)), "test3.mod") 70 | }) 71 | t.Run("create new and set direct require2 should work", func(t *testing.T) { 72 | f, err := CreateFromExistingOrNew(context.TODO(), r, log.New(os.Stderr, "", 0), "", "test4.mod") 73 | testutil.Ok(t, err) 74 | testutil.Ok(t, f.SetDirectRequire(Package{Module: module.Version{Path: "github.com/yolo/best/v100", Version: "v100.0.0"}})) 75 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/yolo/best/v100", Version: "v100.0.0"}}, *f.DirectPackage()) 76 | testutil.Ok(t, f.Close()) 77 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 78 | 79 | go %s 80 | 81 | require github.com/yolo/best/v100 v100.0.0 82 | `, goVersion(r)), "test4.mod") 83 | }) 84 | t.Run("copy and set direct require to something else", func(t *testing.T) { 85 | f, err := CreateFromExistingOrNew(context.TODO(), r, log.New(os.Stderr, "", 0), "test3.mod", "test5.mod") 86 | testutil.Ok(t, err) 87 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/yolo/best/v100", Version: "v100.0.0"}, RelPath: "thebest"}, *f.DirectPackage()) 88 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 89 | 90 | go %s 91 | 92 | require github.com/yolo/best/v100 v100.0.0 // thebest 93 | `, goVersion(r)), "test5.mod") 94 | 95 | testutil.Ok(t, f.SetDirectRequire(Package{Module: module.Version{Path: "github.com/yolo/not-best", Version: "v1"}})) 96 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/yolo/not-best", Version: "v1"}}, *f.DirectPackage()) 97 | testutil.Ok(t, f.Close()) 98 | expectContent(t, fmt.Sprintf(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 99 | 100 | go %s 101 | 102 | require github.com/yolo/not-best v1 103 | `, goVersion(r)), "test5.mod") 104 | }) 105 | } 106 | 107 | func expectContent(t *testing.T, expected string, file string) { 108 | t.Helper() 109 | 110 | b, err := os.ReadFile(file) 111 | testutil.Ok(t, err) 112 | testutil.Equals(t, expected, string(b)) 113 | } 114 | 115 | func TestModFile(t *testing.T) { 116 | tmpDir := t.TempDir() 117 | 118 | t.Run("without auto fetch directives", func(t *testing.T) { 119 | testFile := filepath.Join(tmpDir, "test.mod") 120 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 121 | 122 | go 1.14 123 | 124 | // bingo:no_directive_fetch 125 | 126 | replace ( 127 | // Ridiculous but Prometheus v2.4.3 did not have Go modules 128 | github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v5.0.0-beta.0.20161028183111-bd73d950fa44+incompatible 129 | github.com/Azure/go-autorest => github.com/Azure/go-autorest v9.9.0+incompatible 130 | github.com/cockroachdb/cmux => github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 131 | github.com/cockroachdb/cockroach => github.com/cockroachdb/cockroach v0.0.0-20170608034007-84bc9597164f 132 | github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.2.3-0.20180520015035-48a0ecefe2e4 133 | github.com/miekg/dns => github.com/miekg/dns v1.0.4 134 | github.com/prometheus/client_golang => github.com/prometheus/client_golang v0.9.0-pre1.0.20180607123607-faf4ec335fe0 135 | github.com/prometheus/common => github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 136 | github.com/prometheus/tsdb => github.com/prometheus/tsdb v0.0.0-20180921053122-9c8ca47399a7 137 | k8s.io/api => k8s.io/api v0.0.0-20180628040859-072894a440bd 138 | k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d 139 | k8s.io/client-go => k8s.io/client-go v8.0.0+incompatible 140 | k8s.io/klog => github.com/simonpasquier/klog-gokit v0.1.0 141 | ) 142 | 143 | require github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus 144 | `), os.ModePerm)) 145 | 146 | mf, err := OpenModFile(testFile) 147 | testutil.Ok(t, err) 148 | 149 | testutil.Equals(t, true, mf.IsDirectivesAutoFetchDisabled()) 150 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, RelPath: "cmd/prometheus"}, *mf.DirectPackage()) 151 | }) 152 | 153 | t.Run("with auto fetch directives", func(t *testing.T) { 154 | testFile := filepath.Join(tmpDir, "test.mod") 155 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 156 | 157 | go 1.14 158 | 159 | replace ( 160 | // Ridiculous but Prometheus v2.4.3 did not have Go modules 161 | github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v5.0.0-beta.0.20161028183111-bd73d950fa44+incompatible 162 | github.com/Azure/go-autorest => github.com/Azure/go-autorest v9.9.0+incompatible 163 | github.com/cockroachdb/cmux => github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 164 | github.com/cockroachdb/cockroach => github.com/cockroachdb/cockroach v0.0.0-20170608034007-84bc9597164f 165 | github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.2.3-0.20180520015035-48a0ecefe2e4 166 | github.com/miekg/dns => github.com/miekg/dns v1.0.4 167 | github.com/prometheus/client_golang => github.com/prometheus/client_golang v0.9.0-pre1.0.20180607123607-faf4ec335fe0 168 | github.com/prometheus/common => github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 169 | github.com/prometheus/tsdb => github.com/prometheus/tsdb v0.0.0-20180921053122-9c8ca47399a7 170 | k8s.io/api => k8s.io/api v0.0.0-20180628040859-072894a440bd 171 | k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d 172 | k8s.io/client-go => k8s.io/client-go v8.0.0+incompatible 173 | k8s.io/klog => github.com/simonpasquier/klog-gokit v0.1.0 174 | ) 175 | 176 | require github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus 177 | `), os.ModePerm)) 178 | 179 | mf, err := OpenModFile(testFile) 180 | testutil.Ok(t, err) 181 | 182 | testutil.Equals(t, false, mf.IsDirectivesAutoFetchDisabled()) 183 | testutil.Equals(t, Package{Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, RelPath: "cmd/prometheus"}, *mf.DirectPackage()) 184 | }) 185 | 186 | t.Run("with build attributes1", func(t *testing.T) { 187 | testFile := filepath.Join(tmpDir, "test.mod") 188 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 189 | 190 | go 1.14 191 | 192 | require github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus -tags=yolo,linux 193 | `), os.ModePerm)) 194 | 195 | mf, err := OpenModFile(testFile) 196 | testutil.Ok(t, err) 197 | 198 | testutil.Equals(t, false, mf.IsDirectivesAutoFetchDisabled()) 199 | testutil.Equals(t, Package{ 200 | Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, 201 | RelPath: "cmd/prometheus", 202 | BuildFlags: []string{"-tags=yolo,linux"}, 203 | }, *mf.DirectPackage()) 204 | }) 205 | 206 | t.Run("with build attributes2", func(t *testing.T) { 207 | testFile := filepath.Join(tmpDir, "test.mod") 208 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 209 | 210 | go 1.14 211 | 212 | require github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus CGO_ENABLED=1 GOWASM=somefeature 213 | `), os.ModePerm)) 214 | 215 | mf, err := OpenModFile(testFile) 216 | testutil.Ok(t, err) 217 | 218 | testutil.Equals(t, false, mf.IsDirectivesAutoFetchDisabled()) 219 | testutil.Equals(t, Package{ 220 | Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, 221 | RelPath: "cmd/prometheus", 222 | BuildEnvs: []string{"CGO_ENABLED=1", "GOWASM=somefeature"}, 223 | }, *mf.DirectPackage()) 224 | }) 225 | 226 | t.Run("with build attributes3", func(t *testing.T) { 227 | testFile := filepath.Join(tmpDir, "test.mod") 228 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 229 | 230 | go 1.14 231 | 232 | require github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus CGO_ENABLED=1 GOWASM=somefeature -tags=yolo,linux 233 | `), os.ModePerm)) 234 | 235 | mf, err := OpenModFile(testFile) 236 | testutil.Ok(t, err) 237 | 238 | testutil.Equals(t, false, mf.IsDirectivesAutoFetchDisabled()) 239 | testutil.Equals(t, Package{ 240 | Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, 241 | RelPath: "cmd/prometheus", 242 | BuildEnvs: []string{"CGO_ENABLED=1", "GOWASM=somefeature"}, 243 | BuildFlags: []string{"-tags=yolo,linux"}, 244 | }, *mf.DirectPackage()) 245 | }) 246 | t.Run("with build attributes without relpath", func(t *testing.T) { 247 | testFile := filepath.Join(tmpDir, "test.mod") 248 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 249 | 250 | go 1.14 251 | 252 | require github.com/prometheus/prometheus v2.4.3+incompatible // CGO_ENABLED=1 GOWASM=somefeature -tags=yolo,linux 253 | `), os.ModePerm)) 254 | 255 | mf, err := OpenModFile(testFile) 256 | testutil.Ok(t, err) 257 | 258 | testutil.Equals(t, false, mf.IsDirectivesAutoFetchDisabled()) 259 | testutil.Equals(t, Package{ 260 | Module: module.Version{Path: "github.com/prometheus/prometheus", Version: "v2.4.3+incompatible"}, 261 | BuildEnvs: []string{"CGO_ENABLED=1", "GOWASM=somefeature"}, 262 | BuildFlags: []string{"-tags=yolo,linux"}, 263 | }, *mf.DirectPackage()) 264 | }) 265 | } 266 | -------------------------------------------------------------------------------- /pkg/bingo/test.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /pkg/bingo/test2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /pkg/bingo/test3.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/yolo/best/v100 v100.0.0 // thebest 6 | -------------------------------------------------------------------------------- /pkg/bingo/test4.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/yolo/best/v100 v100.0.0 6 | -------------------------------------------------------------------------------- /pkg/bingo/test5.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/yolo/not-best v1 6 | -------------------------------------------------------------------------------- /pkg/bingo/variables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package bingo 5 | 6 | var ( 7 | templatesByFileExt = map[string]string{ 8 | // TODO(bwplotka): We might want to play with better escaping to allow spaces in dir names. 9 | // TODO(bwplotka): We get first binary as an example. It does not work if first one is array. 10 | "mk": `# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo {{ .Version }}. DO NOT EDIT. 11 | # All tools are designed to be build inside $GOBIN. 12 | BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 13 | GOPATH ?= $(shell go env GOPATH) 14 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 15 | GO ?= $(shell which go) 16 | 17 | # Below generated variables ensure that every time a tool under each variable is invoked, the correct version 18 | # will be used; reinstalling only if needed. 19 | # For example for {{ with (index .MainPackages 0) }}{{ .Name }}{{ end }} variable: 20 | # 21 | # In your main Makefile (for non array binaries): 22 | # 23 | #include .bingo/Variables.mk # Assuming -dir was set to .bingo . 24 | # 25 | #command: $({{ with (index .MainPackages 0) }}{{ .EnvVarName }}{{ end }}) 26 | # @echo "Running {{ with (index .MainPackages 0) }}{{ .Name }}{{ end }}" 27 | # @$({{ with (index .MainPackages 0) }}{{ .EnvVarName }}{{ end }}) 28 | # 29 | {{- range $p := .MainPackages }} 30 | {{ $p.EnvVarName }} :={{- range $p.Versions }} $(GOBIN)/{{ $p.Name }}-{{ .Version }}{{- end }} 31 | $({{ $p.EnvVarName }}):{{- range $p.Versions }} $(BINGO_DIR)/{{ .ModFile }}{{- end }} 32 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 33 | {{- range $p.Versions }} 34 | @echo "(re)installing $(GOBIN)/{{ $p.Name }}-{{ .Version }}" 35 | @cd $(BINGO_DIR) && GOWORK=off {{ range $p.BuildEnvVars }}{{ . }} {{ end }}$(GO) build {{ range $p.BuildFlags }}{{ . }} {{ end }}-mod=mod -modfile={{ .ModFile }} -o=$(GOBIN)/{{ $p.Name }}-{{ .Version }} "{{ $p.PackagePath }}" 36 | {{- end }} 37 | {{ end}} 38 | `, 39 | "env": `# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo {{ .Version }}. DO NOT EDIT. 40 | # All tools are designed to be build inside $GOBIN. 41 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 42 | GOBIN=${GOBIN:=$(go env GOBIN)} 43 | 44 | if [ -z "$GOBIN" ]; then 45 | GOBIN="$(go env GOPATH)/bin" 46 | fi 47 | 48 | {{range $p := .MainPackages }} 49 | {{ $p.EnvVarName }}="{{- range $i, $v := $p.Versions }}{{- if ne $i 0}} {{ end }}${GOBIN}/{{ $p.Name }}-{{ $v.Version }}{{- end }}" 50 | {{ end}} 51 | `, 52 | } 53 | ) 54 | -------------------------------------------------------------------------------- /pkg/cpy/cpy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package cpy 5 | 6 | import ( 7 | "io" 8 | "os" 9 | 10 | "github.com/efficientgo/core/errcapture" 11 | ) 12 | 13 | func File(src, dst string) (err error) { 14 | source, err := os.Open(src) 15 | if err != nil { 16 | return err 17 | } 18 | defer errcapture.Do(&err, source.Close, "close source") 19 | 20 | destination, err := os.Create(dst) 21 | if err != nil { 22 | return err 23 | } 24 | defer errcapture.Do(&err, destination.Close, "close destination") 25 | 26 | buf := make([]byte, 1024) 27 | for { 28 | n, err := source.Read(buf) 29 | if err != nil && err != io.EOF { 30 | return err 31 | } 32 | if n == 0 { 33 | break 34 | } 35 | 36 | if _, err := destination.Write(buf[:n]); err != nil { 37 | return err 38 | } 39 | } 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/envars/envars.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package envars 5 | 6 | import ( 7 | "strings" 8 | 9 | "mvdan.cc/sh/v3/expand" 10 | ) 11 | 12 | type EnvSlice []string 13 | 14 | func (e EnvSlice) Lookup(k string) (string, bool) { 15 | for _, ev := range e { 16 | sp := strings.SplitN(ev, "=", 2) 17 | if sp[0] == k { 18 | return sp[1], true 19 | } 20 | } 21 | return "", false 22 | } 23 | 24 | func (e *EnvSlice) Set(kvs ...string) { 25 | *e = MergeEnvSlices(*e, kvs...) 26 | } 27 | 28 | // Get retrieves a variable by its name. To check if the variable is 29 | // set, use Variable.IsSet. 30 | func (e *EnvSlice) Get(name string) expand.Variable { 31 | return expand.ListEnviron(*e...).Get(name) 32 | } 33 | 34 | // Each iterates over all the currently set variables, calling the 35 | // supplied function on each variable. Iteration is stopped if the 36 | // function returns false. 37 | // 38 | // The names used in the calls aren't required to be unique or sorted. 39 | // If a variable name appears twice, the latest occurrence takes 40 | // priority. 41 | // 42 | // Each is required to forward exported variables when executing 43 | // programs. 44 | func (e *EnvSlice) Each(f func(name string, vr expand.Variable) bool) { 45 | expand.ListEnviron(*e...).Each(f) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/envars/eval.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package envars 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "fmt" 10 | "io" 11 | "os" 12 | "sort" 13 | "strings" 14 | "text/scanner" 15 | 16 | "github.com/efficientgo/core/errors" 17 | "mvdan.cc/sh/v3/expand" 18 | "mvdan.cc/sh/v3/interp" 19 | "mvdan.cc/sh/v3/syntax" 20 | ) 21 | 22 | // EvalVariables evaluates dot env file in similar way `bash source` would do and returns all environment variables available at end of the 23 | // execution of the script. 24 | // Currently it supports any bash script and can cause side effects. 25 | // TODO(bwplotka): Walk over syntax and allow list few syntax elements only? 26 | func EvalVariables(ctx context.Context, r io.Reader, envSlice ...string) (ret EnvSlice, _ error) { 27 | const prefix = "[[dotenv.EvalVariables]]:" 28 | 29 | s, err := syntax.NewParser().Parse(r, "") 30 | if err != nil { 31 | return nil, errors.Wrap(err, "parse") 32 | } 33 | 34 | vars := listVarNames(s) 35 | if len(vars) == 0 { 36 | return nil, nil 37 | } 38 | 39 | // sh does not implement the declaration clauses like `declare` or `export`. Let's set to ignore it and get variable 40 | // values manually. 41 | trimDeclStmts(s) 42 | 43 | // Add env print at the end to get all environment variables that would be available at this point! 44 | // Create env slice to print at the end of script. 45 | var parts []syntax.WordPart 46 | for _, v := range vars { 47 | parts = append(parts, 48 | &syntax.Lit{Value: fmt.Sprintf("%v \"", v)}, 49 | &syntax.ParamExp{Param: &syntax.Lit{Value: v}}, 50 | &syntax.Lit{Value: "\" "}, 51 | ) 52 | } 53 | 54 | s.Stmts = append( 55 | s.Stmts, &syntax.Stmt{Cmd: &syntax.CallExpr{ 56 | Args: []*syntax.Word{ 57 | {Parts: []syntax.WordPart{&syntax.Lit{Value: "echo"}}}, 58 | {Parts: []syntax.WordPart{&syntax.DblQuoted{Parts: append([]syntax.WordPart{&syntax.Lit{Value: prefix}}, parts...)}}}, 59 | }}}, 60 | ) 61 | 62 | b := bytes.Buffer{} 63 | ru, err := interp.New(interp.StdIO(os.Stdin, &b, &b), interp.Env(expand.ListEnviron(envSlice...))) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | if err := ru.Run(ctx, s); err != nil { 69 | return nil, err 70 | } 71 | 72 | var sc scanner.Scanner 73 | sc.Init(strings.NewReader(b.String()[strings.Index(b.String(), prefix)+len(prefix):])) 74 | tok := sc.Scan() 75 | for tok != scanner.EOF { 76 | k := sc.TokenText() 77 | _ = sc.Scan() 78 | v := sc.TokenText() 79 | ret = append(ret, fmt.Sprintf("%s=%s", k, strings.Trim(v, "\""))) 80 | tok = sc.Scan() 81 | } 82 | return ret, nil 83 | } 84 | 85 | func listVarNames(ast *syntax.File) (vars []string) { 86 | dup := map[string]struct{}{} 87 | for _, s := range ast.Stmts { 88 | syntax.Walk(s, func(node syntax.Node) bool { 89 | switch n := node.(type) { 90 | case *syntax.Assign: 91 | if n.Name == nil { 92 | return false 93 | } 94 | if _, ok := dup[n.Name.Value]; ok { 95 | return false 96 | } 97 | dup[n.Name.Value] = struct{}{} 98 | vars = append(vars, n.Name.Value) 99 | return false 100 | } 101 | return true 102 | }) 103 | } 104 | return vars 105 | } 106 | 107 | func trimDeclStmts(ast *syntax.File) { 108 | for _, s := range ast.Stmts { 109 | syntax.Walk(s, func(node syntax.Node) bool { 110 | switch node.(type) { 111 | case *syntax.DeclClause: 112 | // TODO(bwplotka): Right not just trim them, but in future pull out assignments to statements on the parent level. 113 | node = nil // nolint 114 | return false 115 | } 116 | return true 117 | }) 118 | } 119 | } 120 | 121 | // MergeEnvSlices merges two slices into single, sorted, deduplicated slice by applying `over` slice into `base`. 122 | // The `over` slice will be used if the key overlaps. 123 | // See https://golang.org/pkg/os/exec/#Cmd `Env` field to read more about slice format. 124 | func MergeEnvSlices(base []string, over ...string) (merged []string) { 125 | sort.Strings(base) 126 | sort.Strings(over) 127 | 128 | var b, o int 129 | for b < len(base) || o < len(over) { 130 | 131 | if b >= len(base) { 132 | appendOrReplaceDup(&merged, over[o]) 133 | o++ 134 | continue 135 | } 136 | 137 | if o >= len(over) { 138 | appendOrReplaceDup(&merged, base[b]) 139 | b++ 140 | continue 141 | } 142 | 143 | switch strings.Compare(strings.Split(base[b], "=")[0], strings.Split(over[o], "=")[0]) { 144 | case 0: 145 | // Same keys. Instead of picking over element, ignore base one. This ensure correct behaviour if base 146 | // has duplicate elements. 147 | b++ 148 | case 1: 149 | appendOrReplaceDup(&merged, over[o]) 150 | o++ 151 | case -1: 152 | appendOrReplaceDup(&merged, base[b]) 153 | b++ 154 | } 155 | } 156 | return merged 157 | } 158 | 159 | func appendOrReplaceDup(appendable *[]string, item string) { 160 | if len(*appendable) == 0 { 161 | *appendable = append(*appendable, item) 162 | return 163 | } 164 | 165 | lastI := len(*appendable) - 1 166 | if strings.Compare(strings.Split((*appendable)[lastI], "=")[0], strings.Split(item, "=")[0]) == 0 { 167 | (*appendable)[lastI] = item 168 | return 169 | } 170 | *appendable = append(*appendable, item) 171 | } 172 | -------------------------------------------------------------------------------- /pkg/envars/eval_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package envars 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "os" 10 | "testing" 11 | 12 | "github.com/efficientgo/core/testutil" 13 | ) 14 | 15 | func TestEval(t *testing.T) { 16 | t.Run("simple.env", func(t *testing.T) { 17 | b, err := os.ReadFile("testdata/simple.env") 18 | testutil.Ok(t, err) 19 | 20 | e, err := EvalVariables( 21 | context.TODO(), 22 | bytes.NewReader(b), 23 | "PATH="+os.Getenv("PATH"), // go executable has to be available. 24 | "GOBIN=/home/something/bin", 25 | "HOME="+os.Getenv("HOME"), 26 | ) 27 | testutil.Ok(t, err) 28 | testutil.Equals(t, EnvSlice{ 29 | "VAR1=with space 124", "VAR2=with space 124-yolo", "VAR3=with\\n\\nnewline", 30 | }, e) 31 | }) 32 | t.Run("bingo.env", func(t *testing.T) { 33 | b, err := os.ReadFile("testdata/bingo.env") 34 | testutil.Ok(t, err) 35 | 36 | e, err := EvalVariables( 37 | context.TODO(), 38 | bytes.NewReader(b), 39 | "PATH="+os.Getenv("PATH"), // go executable has to be available. 40 | "GOBIN=/home/something/bin", 41 | "HOME="+os.Getenv("HOME"), 42 | ) 43 | testutil.Ok(t, err) 44 | testutil.Equals(t, EnvSlice{ 45 | "GOBIN=/home/something/bin", 46 | "COPYRIGHT=/home/something/bin/copyright-v0.0.0-20210107100701-44cf59f65a1b", 47 | "EMBEDMD=/home/something/bin/embedmd-v1.0.0", 48 | "FAILLINT=/home/something/bin/faillint-v1.5.0", 49 | "GOIMPORTS=/home/something/bin/goimports-v0.0.0-20200519204825-e64124511800", 50 | "GOLANGCI_LINT=/home/something/bin/golangci-lint-v1.26.0", 51 | "MDOX=/home/something/bin/mdox-v0.1.1-0.20201227133330-19093fdd9326", 52 | "MISSPELL=/home/something/bin/misspell-v0.3.4", 53 | "PROXY=/home/something/bin/proxy-v0.10.0", 54 | }, e) 55 | }) 56 | t.Run("export.env", func(t *testing.T) { 57 | b, err := os.ReadFile("testdata/export.env") 58 | testutil.Ok(t, err) 59 | 60 | e, err := EvalVariables( 61 | context.TODO(), 62 | bytes.NewReader(b), 63 | "PATH="+os.Getenv("PATH"), // go executable has to be available. 64 | "GOBIN=/home/something/bin", 65 | "HOME="+os.Getenv("HOME"), 66 | ) 67 | testutil.Ok(t, err) 68 | testutil.Equals(t, EnvSlice{"GOBIN=/home/something/bin", "PROXY=/home/something/bin/proxy-v0.10.0", "X=/home/something/bin/proxy-v0.12.0"}, e) 69 | }) 70 | 71 | } 72 | 73 | func TestMergeEnvSlices(t *testing.T) { 74 | t.Run("just base", func(t *testing.T) { 75 | testutil.Equals(t, []string{ 76 | "OPTION_A=1", 77 | "OPTION_B=2", 78 | "OPTION_C=", 79 | "OPTION_D=\\n", 80 | "OPTION_E=1", 81 | "OPTION_F=2", 82 | "OPTION_G=", 83 | "OPTION_H=\n", 84 | "OPTION_I=echo 'asd'", 85 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 86 | }, MergeEnvSlices([]string{ 87 | "OPTION_A=1", 88 | "OPTION_B=2", 89 | "OPTION_C=", 90 | "OPTION_D=\\n", 91 | "OPTION_E=1", 92 | "OPTION_F=2", 93 | "OPTION_G=", 94 | "OPTION_H=\n", 95 | "OPTION_I=echo 'asd'", 96 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 97 | })) 98 | }) 99 | t.Run("just over", func(t *testing.T) { 100 | testutil.Equals(t, []string{ 101 | "OPTION_A=1", 102 | "OPTION_B=2", 103 | "OPTION_C=", 104 | "OPTION_D=\\n", 105 | "OPTION_E=1", 106 | "OPTION_F=2", 107 | "OPTION_G=", 108 | "OPTION_H=\n", 109 | "OPTION_I=echo 'asd'", 110 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 111 | }, MergeEnvSlices([]string{}, 112 | "OPTION_A=1", 113 | "OPTION_B=2", 114 | "OPTION_C=", 115 | "OPTION_D=\\n", 116 | "OPTION_E=1", 117 | "OPTION_F=2", 118 | "OPTION_G=", 119 | "OPTION_H=\n", 120 | "OPTION_I=echo 'asd'", 121 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 122 | )) 123 | }) 124 | t.Run("same", func(t *testing.T) { 125 | testutil.Equals(t, []string{ 126 | "OPTION_A=1", 127 | "OPTION_B=2", 128 | "OPTION_C=", 129 | "OPTION_D=\\n", 130 | "OPTION_E=1", 131 | "OPTION_F=2", 132 | "OPTION_G=", 133 | "OPTION_H=\n", 134 | "OPTION_I=echo 'asd'", 135 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 136 | }, MergeEnvSlices([]string{ 137 | "OPTION_A=1", 138 | "OPTION_B=2", 139 | "OPTION_C=", 140 | "OPTION_D=\\n", 141 | "OPTION_E=1", 142 | "OPTION_F=2", 143 | "OPTION_G=", 144 | "OPTION_H=\n", 145 | "OPTION_I=echo 'asd'", 146 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 147 | }, 148 | "OPTION_A=1", 149 | "OPTION_B=2", 150 | "OPTION_C=", 151 | "OPTION_D=\\n", 152 | "OPTION_E=1", 153 | "OPTION_F=2", 154 | "OPTION_G=", 155 | "OPTION_H=\n", 156 | "OPTION_I=echo 'asd'", 157 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 158 | )) 159 | }) 160 | t.Run("real with dups", func(t *testing.T) { 161 | testutil.Equals(t, []string{ 162 | "OPTION_A=1", 163 | "OPTION_B=2", 164 | "OPTION_C=22", 165 | "OPTION_D=\\n22", 166 | "OPTION_E=122", 167 | "OPTION_F=222", 168 | "OPTION_G=22", 169 | "OPTION_H=\n22", 170 | "OPTION_I=echo 'asd'22", 171 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable22", 172 | }, MergeEnvSlices([]string{ 173 | "OPTION_A=1", 174 | "OPTION_B=2", 175 | "OPTION_C=", 176 | "OPTION_G=", 177 | "OPTION_H=\n", 178 | "OPTION_H=\n", 179 | "OPTION_H=\n", 180 | "OPTION_H=\n", 181 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable", 182 | }, 183 | "OPTION_C=22", 184 | "OPTION_D=\\n22", 185 | "OPTION_E=122", 186 | "OPTION_F=222", 187 | "OPTION_G=22", 188 | "OPTION_H=\n", 189 | "OPTION_H=\n", 190 | "OPTION_H=\n", 191 | "OPTION_H=\n22", 192 | "OPTION_I=echo 'asd'22", 193 | "OPTION_J=postgres://localhost:5432/database?sslmode=disable22", 194 | )) 195 | }) 196 | 197 | } 198 | -------------------------------------------------------------------------------- /pkg/envars/testdata/bingo.env: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.3.0. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 4 | GOBIN=${GOBIN:=$(go env GOBIN)} 5 | 6 | if [ -z "$GOBIN" ]; then 7 | GOBIN="$(go env GOPATH)/bin" 8 | fi 9 | 10 | 11 | COPYRIGHT="${GOBIN}/copyright-v0.0.0-20210107100701-44cf59f65a1b" 12 | 13 | EMBEDMD="${GOBIN}/embedmd-v1.0.0" 14 | 15 | FAILLINT="${GOBIN}/faillint-v1.5.0" 16 | 17 | GOIMPORTS="${GOBIN}/goimports-v0.0.0-20200519204825-e64124511800" 18 | 19 | GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.26.0" 20 | 21 | MDOX="${GOBIN}/mdox-v0.1.1-0.20201227133330-19093fdd9326" 22 | 23 | MISSPELL="${GOBIN}/misspell-v0.3.4" 24 | 25 | PROXY="${GOBIN}/proxy-v0.10.0" 26 | -------------------------------------------------------------------------------- /pkg/envars/testdata/export.env: -------------------------------------------------------------------------------- 1 | GOBIN=${GOBIN:=$(go env GOBIN)} 2 | 3 | if [ -z "$GOBIN" ]; then 4 | GOBIN="$(go env GOPATH)/bin" 5 | fi 6 | 7 | declare -p 8 | 9 | export 10 | 11 | export PROXY="${GOBIN}/proxy-v0.10.0" X="${GOBIN}/proxy-v0.12.0" -------------------------------------------------------------------------------- /pkg/envars/testdata/simple.env: -------------------------------------------------------------------------------- 1 | VAR1="with space 124" 2 | 3 | VAR2="${VAR1}-yolo" 4 | 5 | VAR3="with\n\nnewline" 6 | 7 | 8 | -------------------------------------------------------------------------------- /pkg/makefile/parser.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package makefile 5 | 6 | import ( 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/efficientgo/core/errors" 13 | ) 14 | 15 | // Copied from https://github.com/tj/mmake/blob/b15229aac1a8ea3f0875f064a0864f7250bd7850/parser & improved. 16 | 17 | // Node interface. 18 | type Node interface { 19 | Lines() []int 20 | } 21 | 22 | type node struct { 23 | lines []int 24 | } 25 | 26 | func (n node) Lines() []int { 27 | return n.lines 28 | } 29 | 30 | // Comment node. 31 | type Comment struct { 32 | node 33 | 34 | Target string 35 | Value string 36 | Default bool 37 | } 38 | 39 | // Include node. 40 | type Include struct { 41 | node 42 | 43 | Value string 44 | } 45 | 46 | // Parser is a quick-n-dirty Makefile "parser", not 47 | // really, just comments and a few directives, but 48 | // you'll forgive me. 49 | type Parser struct { 50 | i int 51 | lines []string 52 | nodeBuf node 53 | commentBuf []string 54 | target string 55 | nodes []Node 56 | } 57 | 58 | // Parse the given input reader. 59 | // TODO(bwplotka): Streaming version would be nice. 60 | func (p *Parser) Parse(r io.Reader) ([]Node, error) { 61 | b, err := io.ReadAll(r) 62 | if err != nil { 63 | return nil, errors.Wrap(err, "reading") 64 | } 65 | 66 | p.lines = strings.Split(string(b), "\n") 67 | 68 | if err := p.parse(); err != nil { 69 | return nil, errors.Wrap(err, "parsing") 70 | } 71 | 72 | return p.nodes, nil 73 | } 74 | 75 | // Peek at the next line. 76 | func (p *Parser) peek() string { 77 | return p.lines[p.i] 78 | } 79 | 80 | // Advance the next line. 81 | func (p *Parser) advance() string { 82 | s := p.lines[p.i] 83 | p.i++ 84 | return s 85 | } 86 | 87 | // Buffer comment. 88 | func (p *Parser) bufferComment() { 89 | s := p.advance()[1:] 90 | 91 | if len(s) > 0 { 92 | if s[0] == '-' { 93 | return 94 | } 95 | 96 | // leading space 97 | if s[0] == ' ' { 98 | s = s[1:] 99 | } 100 | } 101 | p.commentBuf = append(p.commentBuf, s) 102 | } 103 | 104 | // Push comment node. 105 | func (p *Parser) pushComment() { 106 | if len(p.commentBuf) == 0 { 107 | return 108 | } 109 | 110 | s := strings.Join(p.commentBuf, "\n") 111 | 112 | p.nodes = append(p.nodes, Comment{ 113 | node: p.nodeBuf, 114 | Target: p.target, 115 | Value: strings.Trim(s, "\n"), 116 | }) 117 | p.nodeBuf = node{} 118 | p.commentBuf = nil 119 | p.target = "" 120 | } 121 | 122 | // Push include node. 123 | func (p *Parser) pushInclude() { 124 | s := strings.Trim(strings.Replace(p.advance(), "include ", "", 1), " ") 125 | p.nodeBuf.lines = append(p.nodeBuf.lines, p.i-1) 126 | p.nodes = append(p.nodes, Include{ 127 | node: p.nodeBuf, 128 | Value: s, 129 | }) 130 | p.nodeBuf = node{} 131 | } 132 | 133 | // Parse the input. 134 | func (p *Parser) parse() error { 135 | for { 136 | switch { 137 | case p.i == len(p.lines)-1: 138 | return nil 139 | case strings.HasPrefix(p.peek(), ".PHONY"): 140 | p.advance() 141 | case len(p.peek()) == 0: 142 | p.pushComment() 143 | p.advance() 144 | case p.peek()[0] == '#': 145 | p.bufferComment() 146 | case strings.HasPrefix(p.peek(), "include "): 147 | p.pushInclude() 148 | case strings.ContainsRune(p.peek(), ':'): 149 | p.target = strings.Split(p.advance(), ":")[0] 150 | p.nodeBuf.lines = append(p.nodeBuf.lines, p.i-1) 151 | p.pushComment() 152 | default: 153 | p.advance() 154 | } 155 | } 156 | } 157 | 158 | // Parse the given input. 159 | func Parse(r io.Reader) ([]Node, error) { 160 | return (&Parser{}).Parse(r) 161 | } 162 | 163 | // ParseRecursive parses the given input recursively 164 | // relative to the given dir such as /usr/local/include. 165 | func ParseRecursive(r io.Reader, dir string) ([]Node, error) { 166 | nodes, err := parseRecursiveHelper(r, dir) 167 | 168 | for i := range nodes { 169 | defaultComment, ok := nodes[i].(Comment) 170 | if !ok { 171 | continue 172 | } 173 | 174 | defaultComment.Default = true 175 | nodes[i] = Comment{ 176 | Target: defaultComment.Target, 177 | Value: defaultComment.Value, 178 | Default: true, 179 | } 180 | break 181 | } 182 | 183 | return nodes, err 184 | } 185 | 186 | func parseRecursiveHelper(r io.Reader, dir string) ([]Node, error) { 187 | nodes, err := Parse(r) 188 | 189 | if err != nil { 190 | return nil, errors.Wrap(err, "parsing") 191 | } 192 | 193 | otherNodes := []Node{} 194 | for _, n := range nodes { 195 | otherNodes = append(otherNodes, n) 196 | 197 | inc, ok := n.(Include) 198 | 199 | if !ok { 200 | continue 201 | } 202 | 203 | path := filepath.Join(dir, inc.Value) 204 | f, err := os.Open(path) 205 | if err != nil { 206 | return nil, errors.Wrapf(err, "opening %q", path) 207 | } 208 | 209 | more, err := parseRecursiveHelper(f, dir) 210 | 211 | if err != nil { 212 | return nil, errors.Wrapf(err, "parsing %q", path) 213 | } 214 | 215 | otherNodes = append(otherNodes, more...) 216 | 217 | f.Close() 218 | } 219 | 220 | return otherNodes, nil 221 | } 222 | -------------------------------------------------------------------------------- /pkg/makefile/parser_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package makefile 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | func ExampleParser_Parse_withComments() { 12 | contents := ` 13 | include github.com/tj/foo 14 | # Stuff here: 15 | # 16 | # :) 17 | # 18 | # Start the dev server. 19 | start: 20 | @gopherjs -m -v serve --http :3000 github.com/tj/docs/client 21 | .PHONY: start 22 | # Start the API server. 23 | api: 24 | @go run server/cmd/api/api.go 25 | .PHONY: api 26 | # Display dependency graph. 27 | deps: 28 | @godepgraph github.com/tj/docs/client | dot -Tsvg | browser 29 | .PHONY: deps 30 | # Display size of dependencies. 31 | # 32 | # - foo 33 | # - bar 34 | # - baz 35 | # 36 | size: 37 | @gopherjs build client/*.go -m -o /tmp/out.js 38 | @du -h /tmp/out.js 39 | @gopher-count /tmp/out.js | sort -nr 40 | .PHONY: size 41 | .PHONY: dummy 42 | # Just a comment. 43 | # Just another comment. 44 | dummy: 45 | @ls 46 | ` 47 | 48 | nodes, err := Parse(strings.NewReader(contents)) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | for _, node := range nodes { 54 | fmt.Printf("%#v\n", node) 55 | } 56 | 57 | // Output: 58 | // makefile.Include{node:makefile.node{lines:[]int{1}}, Value:"github.com/tj/foo"} 59 | // makefile.Comment{node:makefile.node{lines:[]int{7}}, Target:"start", Value:"Stuff here:\n\n :)\n\nStart the dev server.", Default:false} 60 | // makefile.Comment{node:makefile.node{lines:[]int{8, 11}}, Target:"api", Value:"Start the API server.", Default:false} 61 | // makefile.Comment{node:makefile.node{lines:[]int{15}}, Target:"deps", Value:"Display dependency graph.", Default:false} 62 | // makefile.Comment{node:makefile.node{lines:[]int{24}}, Target:"size", Value:"Display size of dependencies.\n\n- foo\n- bar\n- baz", Default:false} 63 | // makefile.Comment{node:makefile.node{lines:[]int{32}}, Target:"dummy", Value:"Just a comment.\nJust another comment.", Default:false} 64 | } 65 | 66 | func ExampleParser_Parse_withoutComments() { 67 | contents := ` 68 | include github.com/tj/foo 69 | include github.com/tj/bar 70 | include github.com/tj/something/here 71 | start: 72 | @gopherjs -m -v serve --http :3000 github.com/tj/docs/client 73 | .PHONY: start 74 | api: 75 | @go run server/cmd/api/api.go 76 | .PHONY: api 77 | deps: 78 | @godepgraph github.com/tj/docs/client | dot -Tsvg | browser 79 | .PHONY: deps 80 | ` 81 | 82 | nodes, err := Parse(strings.NewReader(contents)) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | for _, node := range nodes { 88 | fmt.Printf("%#v\n", node) 89 | } 90 | 91 | // Output: 92 | // makefile.Include{node:makefile.node{lines:[]int{1}}, Value:"github.com/tj/foo"} 93 | // makefile.Include{node:makefile.node{lines:[]int{2}}, Value:"github.com/tj/bar"} 94 | // makefile.Include{node:makefile.node{lines:[]int{3}}, Value:"github.com/tj/something/here"} 95 | } 96 | -------------------------------------------------------------------------------- /pkg/mod/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | // Package mod allows programmatic editions of Go mod file. It wraps `golang.org/x/mod` with easier to use API. 5 | package mod 6 | -------------------------------------------------------------------------------- /pkg/mod/mod.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package mod 5 | 6 | import ( 7 | "io" 8 | "os" 9 | 10 | "github.com/efficientgo/core/errcapture" 11 | "github.com/efficientgo/core/errors" 12 | "golang.org/x/mod/modfile" 13 | "golang.org/x/mod/module" 14 | ) 15 | 16 | // File represents .mod file for Go Module use. 17 | type File struct { 18 | path string 19 | 20 | f *os.File 21 | m *modfile.File 22 | } 23 | 24 | // OpenFile opens mod file for edits in place. 25 | // It's a caller responsibility to Close the file when not using anymore. 26 | func OpenFile(modFile string) (_ *File, err error) { 27 | f, err := os.OpenFile(modFile, os.O_RDWR, os.ModePerm) 28 | if err != nil { 29 | return nil, err 30 | } 31 | defer func() { 32 | if err != nil { 33 | errcapture.Do(&err, f.Close, "close") 34 | } 35 | }() 36 | 37 | mf := &File{f: f, path: modFile} 38 | return mf, mf.Reload() 39 | } 40 | 41 | // OpenFileForRead opens mod file for reads. 42 | // It's a caller responsibility to Close the file when not using anymore. 43 | func OpenFileForRead(modFile string) (_ FileForRead, err error) { 44 | f, err := os.OpenFile(modFile, os.O_RDONLY, os.ModePerm) 45 | if err != nil { 46 | return nil, err 47 | } 48 | defer func() { 49 | if err != nil { 50 | errcapture.Do(&err, f.Close, "close") 51 | } 52 | }() 53 | 54 | mf := &File{f: f, path: modFile} 55 | return mf, mf.Reload() 56 | } 57 | 58 | type FileForRead interface { 59 | Reload() error 60 | Filepath() string 61 | 62 | Module() (path string, comment string) 63 | Comments() (comments []string) 64 | GoVersion() string 65 | RequireDirectives() []RequireDirective 66 | ReplaceDirectives() []ReplaceDirective 67 | ExcludeDirectives() []ExcludeDirective 68 | RetractDirectives() []RetractDirective 69 | 70 | Close() error 71 | } 72 | 73 | // Reload re-parses module file from the latest state on the disk. 74 | func (mf *File) Reload() (err error) { 75 | if _, err := mf.f.Seek(0, 0); err != nil { 76 | return errors.Wrap(err, "seek") 77 | } 78 | 79 | mf.m, err = parseModFileOrReader(mf.path, mf.f) 80 | return err 81 | } 82 | 83 | func (mf *File) Filepath() string { 84 | return mf.path 85 | } 86 | 87 | // Close closes file. 88 | // TODO(bwplotka): Ensure other methods will return error on use after Close. 89 | func (mf *File) Close() error { 90 | return mf.f.Close() 91 | } 92 | 93 | func (mf *File) Module() (path string, comment string) { 94 | if mf.m.Module == nil { 95 | return "", "" 96 | } 97 | if len(mf.m.Module.Syntax.Comment().Suffix) > 0 { 98 | comment = mf.m.Module.Syntax.Comment().Suffix[0].Token[3:] 99 | } 100 | return mf.m.Module.Mod.Path, comment 101 | } 102 | 103 | func (mf *File) SetModule(path string, comment string) error { 104 | if err := mf.m.AddModuleStmt(path); err != nil { 105 | return err 106 | } 107 | 108 | mf.m.Module.Syntax.Suffix = append(mf.m.Module.Syntax.Suffix, modfile.Comment{Suffix: true, Token: "// " + comment}) 109 | 110 | return mf.flush() 111 | } 112 | 113 | func (mf *File) Comments() (comments []string) { 114 | for _, e := range mf.m.Syntax.Stmt { 115 | for _, c := range e.Comment().Before { 116 | comments = append(comments, c.Token[3:]) 117 | } 118 | } 119 | return comments 120 | } 121 | 122 | func (mf *File) AddComment(comment string) error { 123 | mf.m.AddComment("// " + comment) 124 | 125 | return mf.flush() 126 | } 127 | 128 | // GoVersion returns a semver string containing the value of of the go directive. 129 | // For example, it will return "1.2.3" if the go.mod file contains the line "go 1.2.3". 130 | // If no go directive is found, it returns "1.0" because: 131 | // 1. "1.0" is a valid semver string, so it's always safe to parse this value using semver.MustParse(). 132 | // 2. The semantics of the absence of a go directive in a go.mod file means all versions of Go should be able to compile it. 133 | func (mf *File) GoVersion() string { 134 | if mf.m.Go == nil { 135 | return "1.0" 136 | } 137 | return mf.m.Go.Version 138 | } 139 | 140 | func (mf *File) SetGoVersion(version string) error { 141 | if err := mf.m.AddGoStmt(version); err != nil { 142 | return err 143 | } 144 | 145 | return mf.flush() 146 | } 147 | 148 | // Flush saves all changes made to parsed syntax and reloads the parsed file. 149 | func (mf *File) flush() error { 150 | mf.m.Cleanup() 151 | newB := modfile.Format(mf.m.Syntax) 152 | if err := mf.f.Truncate(0); err != nil { 153 | return errors.Wrap(err, "truncate") 154 | } 155 | if _, err := mf.f.Seek(0, 0); err != nil { 156 | return errors.Wrap(err, "seek") 157 | } 158 | if _, err := mf.f.Write(newB); err != nil { 159 | return errors.Wrap(err, "write") 160 | } 161 | // Reload, so syntax gets rebuilt. It might change due to format. 162 | return mf.Reload() 163 | } 164 | 165 | type RequireDirective struct { 166 | Module module.Version 167 | Indirect bool 168 | 169 | // ExtraSuffixComment represents comment (without '// ') after potential indirect comment 170 | // that can contain additional information. 171 | ExtraSuffixComment string 172 | } 173 | 174 | func (mf *File) RequireDirectives() []RequireDirective { 175 | ret := make([]RequireDirective, len(mf.m.Require)) 176 | for i, r := range mf.m.Require { 177 | ret[i] = RequireDirective{ 178 | Module: r.Mod, 179 | Indirect: r.Indirect, 180 | } 181 | if len(r.Syntax.Suffix) > 0 { 182 | ret[i].ExtraSuffixComment = r.Syntax.Suffix[0].Token[3:] 183 | if r.Indirect { 184 | ret[i].ExtraSuffixComment = r.Syntax.Suffix[0].Token[11:] 185 | } 186 | } 187 | } 188 | return ret 189 | } 190 | 191 | // SetRequireDirectives removes all require statements and set to the given ones. 192 | func (mf *File) SetRequireDirectives(directives ...RequireDirective) (err error) { 193 | for _, r := range mf.m.Require { 194 | _ = mf.m.DropRequire(r.Mod.Path) 195 | } 196 | mf.m.Require = mf.m.Require[:0] 197 | 198 | for i, d := range directives { 199 | mf.m.AddNewRequire(d.Module.Path, d.Module.Version, d.Indirect) 200 | 201 | if len(d.ExtraSuffixComment) > 0 { 202 | r := mf.m.Require[i] 203 | // TODO(bwplotka): How it works with indirect on ? 204 | r.Syntax.Suffix = append(r.Syntax.Suffix[:0], modfile.Comment{Suffix: true, Token: "// " + d.ExtraSuffixComment}) 205 | } 206 | } 207 | return mf.flush() 208 | } 209 | 210 | type ReplaceDirective struct { 211 | Old module.Version 212 | New module.Version 213 | } 214 | 215 | func (mf *File) ReplaceDirectives() []ReplaceDirective { 216 | ret := make([]ReplaceDirective, len(mf.m.Replace)) 217 | for i, r := range mf.m.Replace { 218 | ret[i] = ReplaceDirective{ 219 | Old: r.Old, 220 | New: r.New, 221 | } 222 | } 223 | return ret 224 | } 225 | 226 | // SetReplaceDirectives removes all replace statements and set to the given ones. 227 | func (mf *File) SetReplaceDirectives(directives ...ReplaceDirective) (err error) { 228 | for _, r := range mf.m.Replace { 229 | _ = mf.m.DropReplace(r.Old.Path, r.Old.Version) 230 | } 231 | mf.m.Replace = mf.m.Replace[:0] 232 | 233 | // TODO(bwplotka): Backup before malformation? 234 | for _, d := range directives { 235 | if err := mf.m.AddReplace(d.Old.Path, d.Old.Version, d.New.Path, d.New.Version); err != nil { 236 | return err 237 | } 238 | } 239 | return mf.flush() 240 | } 241 | 242 | type ExcludeDirective struct { 243 | Module module.Version 244 | } 245 | 246 | func (mf *File) ExcludeDirectives() []ExcludeDirective { 247 | ret := make([]ExcludeDirective, len(mf.m.Exclude)) 248 | for i, r := range mf.m.Exclude { 249 | ret[i] = ExcludeDirective{ 250 | Module: r.Mod, 251 | } 252 | } 253 | return ret 254 | } 255 | 256 | // SetExcludeDirectives removes all replace statements and set to the given ones. 257 | func (mf *File) SetExcludeDirectives(directives ...ExcludeDirective) (err error) { 258 | for _, r := range mf.m.Exclude { 259 | _ = mf.m.DropExclude(r.Mod.Path, r.Mod.Version) 260 | } 261 | mf.m.Exclude = mf.m.Exclude[:0] 262 | 263 | // TODO(bwplotka): Backup before malformation? 264 | for _, d := range directives { 265 | if err := mf.m.AddExclude(d.Module.Path, d.Module.Version); err != nil { 266 | return err 267 | } 268 | } 269 | return mf.flush() 270 | } 271 | 272 | type VersionInterval = modfile.VersionInterval 273 | 274 | type RetractDirective struct { 275 | VersionInterval 276 | Rationale string 277 | } 278 | 279 | func (mf *File) RetractDirectives() []RetractDirective { 280 | ret := make([]RetractDirective, len(mf.m.Retract)) 281 | for i, r := range mf.m.Retract { 282 | ret[i] = RetractDirective{ 283 | VersionInterval: r.VersionInterval, 284 | Rationale: r.Rationale, 285 | } 286 | } 287 | return ret 288 | } 289 | 290 | // SetRetractDirectives removes all replace statements and set to the given ones. 291 | func (mf *File) SetRetractDirectives(directives ...RetractDirective) (err error) { 292 | for _, r := range mf.m.Retract { 293 | _ = mf.m.DropRetract(r.VersionInterval) 294 | } 295 | mf.m.Retract = mf.m.Retract[:0] 296 | 297 | for _, d := range directives { 298 | if err := mf.m.AddRetract(d.VersionInterval, d.Rationale); err != nil { 299 | return err 300 | } 301 | } 302 | return mf.flush() 303 | } 304 | 305 | // parseModFileOrReader parses any module file or reader allowing to read it's content. 306 | func parseModFileOrReader(modFile string, r io.Reader) (*modfile.File, error) { 307 | b, err := readAllFileOrReader(modFile, r) 308 | if err != nil { 309 | return nil, errors.Wrap(err, "read") 310 | } 311 | 312 | m, err := modfile.Parse(modFile, b, nil) 313 | if err != nil { 314 | return nil, errors.Wrap(err, "parse") 315 | } 316 | return m, nil 317 | } 318 | 319 | func readAllFileOrReader(file string, r io.Reader) (b []byte, err error) { 320 | if r != nil { 321 | return io.ReadAll(r) 322 | } 323 | return os.ReadFile(file) 324 | } 325 | -------------------------------------------------------------------------------- /pkg/mod/mod_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package mod 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "testing" 10 | 11 | "github.com/efficientgo/core/testutil" 12 | "golang.org/x/mod/module" 13 | ) 14 | 15 | func expectContent(t *testing.T, expected string, file string) { 16 | t.Helper() 17 | 18 | b, err := os.ReadFile(file) 19 | testutil.Ok(t, err) 20 | testutil.Equals(t, expected, string(b)) 21 | } 22 | 23 | func TestFile(t *testing.T) { 24 | t.Parallel() 25 | 26 | tmpDir := t.TempDir() 27 | 28 | t.Run("open empty", func(t *testing.T) { 29 | t.Parallel() 30 | 31 | testFile := filepath.Join(tmpDir, "test.mod") 32 | 33 | _, err := OpenFile(testFile) 34 | testutil.NotOk(t, err) 35 | testutil.Equals(t, "open "+testFile+": no such file or directory", err.Error()) 36 | 37 | testutil.Ok(t, os.WriteFile(testFile, []byte(``), os.ModePerm)) 38 | mf, err := OpenFile(testFile) 39 | testutil.Ok(t, err) 40 | 41 | testutil.Equals(t, testFile, mf.Filepath()) 42 | p, comment := mf.Module() 43 | testutil.Equals(t, "", p) 44 | testutil.Equals(t, "", comment) 45 | testutil.Equals(t, []string(nil), mf.Comments()) 46 | testutil.Equals(t, "1.0", mf.GoVersion()) 47 | testutil.Equals(t, 0, len(mf.RequireDirectives())) 48 | testutil.Equals(t, 0, len(mf.ReplaceDirectives())) 49 | testutil.Equals(t, 0, len(mf.ExcludeDirectives())) 50 | testutil.Equals(t, 0, len(mf.RetractDirectives())) 51 | }) 52 | t.Run("open mod file & modify.", func(t *testing.T) { 53 | t.Parallel() 54 | 55 | testFile := filepath.Join(tmpDir, "test2.mod") 56 | 57 | testutil.Ok(t, os.WriteFile(testFile, []byte(`module github.com/bwplotka/bingo 58 | 59 | go 1.17 60 | 61 | // Comment 1. 62 | 63 | require ( 64 | github.com/prometheus/prometheus v2.4.3+incompatible // cmd/prometheus yolo 65 | github.com/Masterminds/semver v1.5.0 66 | github.com/efficientgo/core v1.0.0-rc.0 67 | github.com/oklog/run v1.1.0 68 | golang.org/x/mod v0.5.1 69 | mvdan.cc/sh/v3 v3.4.3 70 | ) 71 | 72 | // Comment 2. 73 | 74 | require ( 75 | github.com/davecgh/go-spew v1.1.1 // indirect 76 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 77 | golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect 78 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 79 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect 80 | ) 81 | 82 | replace mvdan.cc/sh/v3 => mvdan.cc/sh/v3 v3.2 83 | 84 | retract ( 85 | // Wrongly formatted. 86 | v1.0.0 87 | ) 88 | 89 | exclude mvdan.cc/sh/v3 v3.4.4 90 | 91 | // Comment 3. 92 | 93 | 94 | `), os.ModePerm)) 95 | 96 | mf, err := OpenFile(testFile) 97 | testutil.Ok(t, err) 98 | 99 | p, comment := mf.Module() 100 | testutil.Equals(t, "github.com/bwplotka/bingo", p) 101 | testutil.Equals(t, "", comment) 102 | 103 | testutil.Equals(t, []string{"Comment 1.", "Comment 2.", "Comment 3."}, mf.Comments()) 104 | testutil.Equals(t, "1.17", mf.GoVersion()) 105 | 106 | reqDirectives := mf.RequireDirectives() 107 | testutil.Equals(t, 11, len(reqDirectives)) 108 | testutil.Equals(t, "github.com/prometheus/prometheus", reqDirectives[0].Module.Path) 109 | testutil.Equals(t, "v2.4.3+incompatible", reqDirectives[0].Module.Version) 110 | testutil.Equals(t, "cmd/prometheus yolo", reqDirectives[0].ExtraSuffixComment) 111 | testutil.Equals(t, false, reqDirectives[0].Indirect) 112 | 113 | testutil.Equals(t, "github.com/Masterminds/semver", reqDirectives[1].Module.Path) 114 | testutil.Equals(t, "v1.5.0", reqDirectives[1].Module.Version) 115 | testutil.Equals(t, "", reqDirectives[1].ExtraSuffixComment) 116 | testutil.Equals(t, false, reqDirectives[1].Indirect) 117 | 118 | testutil.Equals(t, "github.com/davecgh/go-spew", reqDirectives[6].Module.Path) 119 | testutil.Equals(t, "v1.1.1", reqDirectives[6].Module.Version) 120 | testutil.Equals(t, "", reqDirectives[6].ExtraSuffixComment) 121 | testutil.Equals(t, true, reqDirectives[6].Indirect) 122 | 123 | replDirectives := mf.ReplaceDirectives() 124 | testutil.Equals(t, 1, len(mf.ReplaceDirectives())) 125 | testutil.Equals(t, "mvdan.cc/sh/v3", replDirectives[0].Old.String()) 126 | testutil.Equals(t, "mvdan.cc/sh/v3@v3.2.0", replDirectives[0].New.String()) 127 | 128 | excludeDirectives := mf.ExcludeDirectives() 129 | testutil.Equals(t, 1, len(excludeDirectives)) 130 | testutil.Equals(t, "mvdan.cc/sh/v3@v3.4.4", excludeDirectives[0].Module.String()) 131 | 132 | retractDirectives := mf.RetractDirectives() 133 | testutil.Equals(t, 1, len(retractDirectives)) 134 | testutil.Equals(t, "v1.0.0", retractDirectives[0].VersionInterval.High) 135 | testutil.Equals(t, "v1.0.0", retractDirectives[0].VersionInterval.Low) 136 | testutil.Equals(t, "Wrongly formatted.", retractDirectives[0].Rationale) 137 | 138 | // Modify. 139 | testutil.Ok(t, mf.SetModule("_", "yolo")) 140 | testutil.Ok(t, mf.AddComment("Let's go!")) 141 | testutil.Ok(t, mf.SetGoVersion("1.18")) 142 | testutil.Ok(t, mf.SetRequireDirectives(RequireDirective{Module: module.Version{Path: "my/module", Version: "v1.0.0"}, ExtraSuffixComment: "yolo"})) 143 | testutil.Ok(t, mf.SetReplaceDirectives(ReplaceDirective{Old: module.Version{Path: "my/module", Version: "v1.0.0"}, New: module.Version{Path: "my/module", Version: "v1.2.2"}})) 144 | testutil.Ok(t, mf.SetExcludeDirectives(ExcludeDirective{Module: module.Version{Path: "my/module", Version: "v1.1.0"}})) 145 | testutil.Ok(t, mf.SetRetractDirectives(RetractDirective{VersionInterval: VersionInterval{High: "v1.0.0", Low: "v0.9.0"}, Rationale: "I don't know"})) 146 | 147 | expectContent(t, `module _ // yolo 148 | 149 | go 1.18 150 | 151 | // Comment 1. 152 | 153 | // Comment 2. 154 | 155 | require my/module v1.0.0 // yolo 156 | 157 | // Comment 3. 158 | 159 | // Let's go! 160 | 161 | replace my/module v1.0.0 => my/module v1.2.2 162 | 163 | exclude my/module v1.1.0 164 | 165 | // I don't know 166 | retract [v0.9.0, v1.0.0] 167 | `, testFile) 168 | p, comment = mf.Module() 169 | testutil.Equals(t, "_", p) 170 | testutil.Equals(t, "yolo", comment) 171 | 172 | testutil.Equals(t, []string{"Comment 1.", "Comment 2.", "Comment 3.", "Let's go!", "I don't know"}, mf.Comments()) 173 | testutil.Equals(t, "1.18", mf.GoVersion()) 174 | 175 | reqDirectives = mf.RequireDirectives() 176 | testutil.Equals(t, 1, len(reqDirectives)) 177 | testutil.Equals(t, "my/module", reqDirectives[0].Module.Path) 178 | testutil.Equals(t, "v1.0.0", reqDirectives[0].Module.Version) 179 | testutil.Equals(t, "yolo", reqDirectives[0].ExtraSuffixComment) 180 | testutil.Equals(t, false, reqDirectives[0].Indirect) 181 | 182 | replDirectives = mf.ReplaceDirectives() 183 | testutil.Equals(t, 1, len(mf.ReplaceDirectives())) 184 | testutil.Equals(t, "my/module@v1.0.0", replDirectives[0].Old.String()) 185 | testutil.Equals(t, "my/module@v1.2.2", replDirectives[0].New.String()) 186 | 187 | excludeDirectives = mf.ExcludeDirectives() 188 | testutil.Equals(t, 1, len(excludeDirectives)) 189 | testutil.Equals(t, "my/module@v1.1.0", excludeDirectives[0].Module.String()) 190 | 191 | retractDirectives = mf.RetractDirectives() 192 | testutil.Equals(t, 1, len(retractDirectives)) 193 | testutil.Equals(t, "v1.0.0", retractDirectives[0].VersionInterval.High) 194 | testutil.Equals(t, "v0.9.0", retractDirectives[0].VersionInterval.Low) 195 | testutil.Equals(t, "I don't know", retractDirectives[0].Rationale) 196 | }) 197 | } 198 | -------------------------------------------------------------------------------- /pkg/runner/runner.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package runner 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "regexp" 16 | "strings" 17 | 18 | "github.com/Masterminds/semver" 19 | "github.com/bwplotka/bingo/pkg/envars" 20 | "github.com/bwplotka/bingo/pkg/version" 21 | "github.com/efficientgo/core/errors" 22 | ) 23 | 24 | // Runner allows to run certain commands against module aware Go CLI. 25 | type Runner struct { 26 | goCmd string 27 | insecure bool 28 | 29 | verbose bool 30 | goVersion *semver.Version 31 | 32 | logger *log.Logger 33 | } 34 | 35 | var versionRegexp = regexp.MustCompile(`^go version.* go((?:[0-9]+)(?:\.[0-9]+)?(?:\.[0-9]+)?)`) 36 | 37 | // parseGoVersion ignores pre-release identifiers immediately following the 38 | // patch version since we don't expect goVersionOutput to be SemVer-compliant. 39 | func parseGoVersion(goVersionOutput string) (*semver.Version, error) { 40 | goVersion := versionRegexp.FindStringSubmatch(goVersionOutput) 41 | if goVersion == nil { 42 | return nil, errors.Newf("unexpected go version output; expected 'go version go ...; found %v", strings.TrimRight(goVersionOutput, "\n")) 43 | } 44 | return semver.NewVersion(goVersion[1]) 45 | } 46 | 47 | func isSupportedVersion(v *semver.Version) error { 48 | if !v.LessThan(version.Go114) { 49 | return nil 50 | } 51 | return errors.Newf("found unsupported go version: %v; requires go 1.14.x or higher", v.String()) 52 | } 53 | 54 | // NewRunner checks Go version compatibility then returns Runner. 55 | func NewRunner(ctx context.Context, logger *log.Logger, insecure bool, goCmd string) (*Runner, error) { 56 | output := &bytes.Buffer{} 57 | r := &Runner{ 58 | goCmd: goCmd, 59 | insecure: insecure, 60 | logger: logger, 61 | } 62 | 63 | if err := r.execGo(ctx, output, nil, "", "", "version"); err != nil { 64 | return nil, errors.Wrap(err, "exec go to detect the version") 65 | } 66 | 67 | goVersion, err := parseGoVersion(output.String()) 68 | if err != nil { 69 | return nil, errors.Wrap(err, "parse go version") 70 | } 71 | 72 | r.goVersion = goVersion 73 | return r, isSupportedVersion(r.goVersion) 74 | } 75 | 76 | func (r *Runner) GoVersion() *semver.Version { 77 | return r.goVersion 78 | } 79 | 80 | func (r *Runner) Verbose() { 81 | r.verbose = true 82 | } 83 | 84 | var cmdsSupportingModFileArg = map[string]struct{}{ 85 | "init": {}, 86 | "get": {}, 87 | "install": {}, 88 | "list": {}, 89 | "build": {}, 90 | } 91 | 92 | func (r *Runner) execGo(ctx context.Context, output io.Writer, e envars.EnvSlice, cd string, modFile string, args ...string) error { 93 | if modFile != "" { 94 | for i, arg := range args { 95 | if _, ok := cmdsSupportingModFileArg[arg]; ok { 96 | if i == len(args)-1 { 97 | args = append(args, fmt.Sprintf("-modfile=%s", modFile)) 98 | break 99 | } 100 | args = append(args[:i+1], append([]string{fmt.Sprintf("-modfile=%s", modFile)}, args[i+1:]...)...) 101 | break 102 | } 103 | } 104 | } 105 | return r.exec(ctx, output, e, cd, r.goCmd, args...) 106 | } 107 | 108 | func (r *Runner) exec(ctx context.Context, output io.Writer, e envars.EnvSlice, cd string, command string, args ...string) error { 109 | cmd := exec.CommandContext(ctx, command, args...) 110 | cmd.Dir = filepath.Join(cmd.Dir, cd) 111 | // TODO(bwplotka): Might be surprising, let's return err when this env variable is altered. 112 | e = envars.MergeEnvSlices(os.Environ(), e...) 113 | e.Set("GO111MODULE=on") 114 | e.Set("GOWORK=off") 115 | cmd.Env = e 116 | cmd.Stdout = output 117 | cmd.Stderr = output 118 | if err := cmd.Run(); err != nil { 119 | if _, ok := err.(*exec.ExitError); ok { 120 | if r.verbose { 121 | return errors.Newf("error while running command '%s %s'; err: %v", command, strings.Join(args, " "), err) 122 | } 123 | return errors.New("exit 1") 124 | } 125 | return errors.Newf("error while running command '%s %s'; err: %v", command, strings.Join(args, " "), err) 126 | } 127 | if r.verbose { 128 | r.logger.Printf("exec '%s %s'\n", command, strings.Join(args, " ")) 129 | } 130 | return nil 131 | } 132 | 133 | type Runnable interface { 134 | GoVersion() *semver.Version 135 | List(args ...string) (string, error) 136 | GetD(packages ...string) (string, error) 137 | Build(pkg, out string, args ...string) error 138 | GoEnv(args ...string) (string, error) 139 | ModDownload(args ...string) error 140 | } 141 | 142 | type runnable struct { 143 | r *Runner 144 | 145 | ctx context.Context 146 | modFile string 147 | dir string 148 | extraEnvVars envars.EnvSlice 149 | } 150 | 151 | // ModInit runs `go mod init` against separate go modules files if any. 152 | func (r *Runner) ModInit(ctx context.Context, cd, modFile, moduleName string) error { 153 | out := &bytes.Buffer{} 154 | if err := r.execGo(ctx, out, nil, cd, modFile, append([]string{"mod", "init"}, moduleName)...); err != nil { 155 | return errors.Wrap(err, out.String()) 156 | } 157 | return nil 158 | } 159 | 160 | // With returns runner that will be ran against give modFile (if any), in given directory (if any), with given extraEnvVars on top of Environ. 161 | func (r *Runner) With(ctx context.Context, modFile string, dir string, extraEnvVars envars.EnvSlice) Runnable { 162 | ru := &runnable{ 163 | r: r, 164 | modFile: modFile, 165 | dir: dir, 166 | extraEnvVars: extraEnvVars, 167 | ctx: ctx, 168 | } 169 | return ru 170 | } 171 | 172 | func (r *runnable) GoVersion() *semver.Version { 173 | return r.r.GoVersion() 174 | } 175 | 176 | // List runs `go list` against separate go modules files if any. 177 | func (r *runnable) List(args ...string) (string, error) { 178 | a := []string{"list"} 179 | out := &bytes.Buffer{} 180 | if err := r.r.execGo(r.ctx, out, r.extraEnvVars, r.dir, r.modFile, append(a, args...)...); err != nil { 181 | return "", errors.Wrap(err, out.String()) 182 | } 183 | return strings.Trim(out.String(), "\n"), nil 184 | } 185 | 186 | // GoEnv runs `go env` with given args. 187 | func (r *runnable) GoEnv(args ...string) (string, error) { 188 | out := &bytes.Buffer{} 189 | if err := r.r.execGo(r.ctx, out, r.extraEnvVars, r.dir, "", append([]string{"env"}, args...)...); err != nil { 190 | return "", errors.Wrap(err, out.String()) 191 | } 192 | return strings.Trim(out.String(), "\n"), nil 193 | } 194 | 195 | // GetD runs 'go get -d' against separate go modules file with given arguments. 196 | func (r *runnable) GetD(packages ...string) (string, error) { 197 | args := []string{"get", "-d"} 198 | if r.r.insecure { 199 | args = append(args, "-insecure") 200 | } 201 | 202 | out := &bytes.Buffer{} 203 | if err := r.r.execGo(r.ctx, out, r.extraEnvVars, r.dir, r.modFile, append(args, packages...)...); err != nil { 204 | return "", errors.Wrap(err, out.String()) 205 | } 206 | return strings.Trim(out.String(), "\n"), nil 207 | } 208 | 209 | // Build runs 'go build' against separate go modules file with given packages. 210 | func (r *runnable) Build(pkg, out string, args ...string) error { 211 | args = append([]string{"build", "-o=" + out}, args...) 212 | output := &bytes.Buffer{} 213 | if err := r.r.execGo(r.ctx, output, r.extraEnvVars, r.dir, r.modFile, append(args, pkg)...); err != nil { 214 | return errors.Wrap(err, output.String()) 215 | } 216 | 217 | trimmed := strings.TrimSpace(output.String()) 218 | if r.r.verbose && trimmed != "" { 219 | r.r.logger.Println(trimmed) 220 | } 221 | return nil 222 | } 223 | 224 | // ModDownload runs 'go mod download' against separate go modules file. 225 | func (r *runnable) ModDownload(args ...string) error { 226 | a := []string{"mod", "download"} 227 | if r.r.verbose { 228 | a = append(a, "-x") 229 | } 230 | a = append(a, fmt.Sprintf("-modfile=%s", r.modFile)) 231 | 232 | out := &bytes.Buffer{} 233 | if err := r.r.execGo(r.ctx, out, r.extraEnvVars, r.dir, r.modFile, append(a, args...)...); err != nil { 234 | return errors.Wrap(err, out.String()) 235 | } 236 | 237 | trimmed := strings.TrimSpace(out.String()) 238 | if r.r.verbose && trimmed != "" { 239 | r.r.logger.Println(trimmed) 240 | } 241 | return nil 242 | } 243 | -------------------------------------------------------------------------------- /pkg/runner/runner_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package runner 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/efficientgo/core/errors" 10 | "github.com/efficientgo/core/merrors" 11 | "github.com/efficientgo/core/testutil" 12 | ) 13 | 14 | func TestParseAndIsSupportedVersion(t *testing.T) { 15 | for _, tcase := range []struct { 16 | output string 17 | errs error 18 | }{ 19 | {output: "", errs: errors.New("unexpected go version output; expected 'go version go ...; found ")}, 20 | {output: "go version go1.1 linux/amd64", errs: errors.New("found unsupported go version: 1.1.0; requires go 1.14.x or higher")}, 21 | {output: "go version go1 linux/amd64", errs: errors.New("found unsupported go version: 1.0.0; requires go 1.14.x or higher")}, 22 | {output: "go version go1.1.2 linux/amd64", errs: errors.New("found unsupported go version: 1.1.2; requires go 1.14.x or higher")}, 23 | {output: "go version go1.12rc1 linux/amd64", errs: errors.New("found unsupported go version: 1.12.0; requires go 1.14.x or higher")}, 24 | {output: "go version go1.12 linux/amd64", errs: errors.New("found unsupported go version: 1.12.0; requires go 1.14.x or higher")}, 25 | {output: "go version go1.13 linux/amd64", errs: errors.New("found unsupported go version: 1.13.0; requires go 1.14.x or higher")}, 26 | {output: "go version go1.13.2 linux/amd64", errs: errors.New("found unsupported go version: 1.13.2; requires go 1.14.x or higher")}, 27 | {output: "go version go1.14 linux/amd64"}, 28 | {output: "go version go1.14.2 linux/amd64"}, 29 | {output: "go version go1.15 linux/amd64"}, 30 | {output: "go version go1.15.44 linux/amd64"}, 31 | {output: "go version go1.16beta1 linux/amd64"}, 32 | {output: "go version go1.16rc1 linux/amd64"}, 33 | {output: "go version go2 linux/amd64"}, 34 | {output: "go version go2.1 linux/amd64"}, 35 | {output: "go version devel go1.21-02d8ebda83 Mon Feb 6 22:13:07 2023 +0000 linux/amd64"}, 36 | } { 37 | t.Run(tcase.output, func(t *testing.T) { 38 | errs := merrors.New() 39 | v, err := parseGoVersion(tcase.output) 40 | if err != nil { 41 | errs.Add(err) 42 | } 43 | 44 | if v != nil { 45 | if err := isSupportedVersion(v); err != nil { 46 | errs.Add(err) 47 | } 48 | } 49 | if tcase.errs != nil { 50 | testutil.NotOk(t, errs.Err()) 51 | testutil.Equals(t, tcase.errs.Error(), errs.Err().Error()) 52 | return 53 | } 54 | testutil.Ok(t, errs.Err()) 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package version 5 | 6 | import "github.com/Masterminds/semver" 7 | 8 | // Version returns 'bingo' version. 9 | const Version = "v0.9" 10 | 11 | var ( 12 | Go114 = semver.MustParse("1.14") 13 | Go116 = semver.MustParse("1.16") 14 | Go121 = semver.MustParse("1.21") 15 | ) 16 | -------------------------------------------------------------------------------- /scripts/build-check-comments.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | # Reliable cross-platform comment checker for go files. 6 | # Uses docker and stable linux distro for reliable grep regex and 7 | # to make sure we work on Linux and OS X and oh even Windows! :) 8 | # Main issue is minor incompatibies between grep on various platforms. 9 | # 10 | # Linux: set USE_DOCKER=yes or USE_DOCKER=false to force run in docker 11 | # All other platforms: uses docker by default. Set USE_DOCKER=no or USE_DOCKER=false to override. 12 | # 13 | # Checks Go code comments if they have trailing period (excludes protobuffers and vendor files). 14 | # Comments with more than 3 spaces at beginning are omitted from the check, example: '// - foo'. 15 | # This does not include top-level commments for funcs and types. 16 | # 17 | # Example: 18 | # func main() { 19 | # // comment without period, will trigger check 20 | # //comment without leading space, will trigger check. 21 | # // comment without trailing space, will trigger check. 22 | # // good comment, leading space, ends with period, no trailing space. 23 | # // - more than 3 leading spaces, will pass 24 | # app := kingpin.New(filepath.Base(os.Args[0]), "A block storage based long-term storage for Prometheus") 25 | # } 26 | 27 | 28 | # Abs path to project dir and this script, should work on all OS's 29 | declare ROOT_DIR="$(cd $(dirname "${BASH_SOURCE}")/.. && pwd)" 30 | declare THIS_SCRIPT="$(cd $(dirname "${BASH_SOURCE}") && pwd)/$(basename "${BASH_SOURCE}")" 31 | 32 | # Image to use if we do docker-based commands. NB: busybox is no good for this. 33 | declare IMAGE="debian:9-slim" 34 | 35 | # User can explicitly ask to run in docker 36 | declare USE_DOCKER=${USE_DOCKER:=""} 37 | 38 | # For OS X, always use Docker as we have nasty 39 | # compat GNU/BSG issues with grep. 40 | if test "Linux" != "$(uname || true)" 41 | then 42 | # Allow overriding for non-linux platforms 43 | if test "no" != "${USE_DOCKER}" && test "false" != "${USE_DOCKER}" 44 | then 45 | USE_DOCKER="yes" 46 | fi 47 | fi 48 | 49 | if test "yes" == "${USE_DOCKER}" || test "true" == "${USE_DOCKER}" 50 | then 51 | # Make sure we only attach TTY if we have it, CI builds won't have it. 52 | declare TTY_FLAG="" 53 | if [ -t 1 ] 54 | then 55 | TTY_FLAG="-t" 56 | fi 57 | 58 | # Annoying issue with ownership of files in mapped volumes. 59 | # Need to run with same UID and GID in container as we do 60 | # on the machine, otherwise all output will be owned by root. 61 | # Doesn't happen on OS X but does on Linux. So we will do 62 | # UID and GID for Linux only (this won't work on OS X anyway). 63 | declare USER_FLAG="" 64 | if test "Linux" == "$(uname || true)" 65 | then 66 | USER_FLAG="-u $(id -u):$(id -g)" 67 | fi 68 | 69 | printf "\n\n\n This will run in Docker. \n If you get an error, ensure Docker is installed. \n\n\n" 70 | ( 71 | set -x 72 | docker run \ 73 | -i \ 74 | ${TTY_FLAG} \ 75 | ${USER_FLAG} \ 76 | --rm \ 77 | -v "${ROOT_DIR}":"${ROOT_DIR}":cached \ 78 | -w "${ROOT_DIR}" \ 79 | "${IMAGE}" \ 80 | "${THIS_SCRIPT}" 81 | ) 82 | exit 0 83 | fi 84 | 85 | function check_comments { 86 | # no bombing out on errors with grep 87 | set +e 88 | 89 | # This is quite mad but don't fear the https://regex101.com/ helps a lot. 90 | grep -Przo --color --include \*.go --exclude \*.pb.go --exclude bindata.go --exclude-dir vendor \ 91 | '\n.*\s+//(\s{0,3}[^\s^+][^\n]+[^.?!:]{2}|[^\s].*)\n[ \t]*[^/\s].*\n' ./ 92 | res=$? 93 | set -e 94 | 95 | # man grep: Normally, the exit status is 0 if selected lines are found and 1 otherwise. 96 | # But the exit status is 2 if an error occurred, unless the -q or --quiet or --silent 97 | # option is used and a selected line is found. 98 | if test "0" == "${res}" # found something 99 | then 100 | printf "\n\n\n Error: Found comments without trailing period. Comments has to be full sentences.\n\n\n" 101 | exit 1 102 | elif test "1" == "${res}" # nothing found, all clear 103 | then 104 | printf "\n\n\n All comment formatting is good, Spartan.\n\n\n" 105 | exit 0 106 | else # grep error 107 | printf "\n\n\n Hmmm something didn't work, issues with grep?.\n\n\n" 108 | exit 2 109 | fi 110 | } 111 | 112 | check_comments 113 | -------------------------------------------------------------------------------- /testdata/Makefile: -------------------------------------------------------------------------------- 1 | include .bingo/Variables.mk 2 | 3 | buildable-v2-exists: $(BUILDABLE_V2) 4 | @echo "checking buildable-v2" 5 | @test $(BUILDABLE_V2) || (echo "no var" && exit 1) 6 | @test -f $(BUILDABLE_V2) || (echo "no file" && exit 1) 7 | 8 | buildable-withreplace-exists: $(BUILDABLE_WITHREPLACE) 9 | @echo "checking buildable-with-replace" 10 | @test $(BUILDABLE_WITHREPLACE) || (echo "no var" && exit 1) 11 | @test -f $(BUILDABLE_WITHREPLACE) || (echo "no file" && exit 1) 12 | -------------------------------------------------------------------------------- /testdata/README.md: -------------------------------------------------------------------------------- 1 | # Bingo testdata 2 | 3 | This directory holds data for automatic test in [../get_e2e_test.go](../get_e2e_test.go) 4 | 5 | ## How to add one? 6 | 7 | * Run `make test` on the version you want to add to include it (version is taken from `version.Version`). -------------------------------------------------------------------------------- /testdata/go.mod: -------------------------------------------------------------------------------- 1 | module testproject 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/efficientgo/core v1.0.0-rc.0 7 | github.com/oklog/run v1.1.0 8 | ) 9 | -------------------------------------------------------------------------------- /testdata/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 3 | github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 6 | -------------------------------------------------------------------------------- /testdata/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Bartłomiej Płotka @bwplotka 2 | // Licensed under the Apache License 2.0. 3 | 4 | package testdata 5 | 6 | import ( 7 | _ "github.com/efficientgo/core/errors" 8 | _ "github.com/oklog/run" 9 | ) 10 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore everything 3 | * 4 | 5 | # But not these files: 6 | !.gitignore 7 | !*.mod 8 | !*.sum 9 | !README.md 10 | !Variables.mk 11 | !variables.env 12 | 13 | *tmp.mod 14 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/README.md: -------------------------------------------------------------------------------- 1 | # Project Development Dependencies. 2 | 3 | This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. 4 | 5 | * Run `bingo get` to install all tools having each own module file in this directory. 6 | * Run `bingo get ` to install that have own module file in this directory. 7 | * For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. 8 | * For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. 9 | * For go: Import `.bingo/variables.go` to for variable names. 10 | * See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. 11 | 12 | ## Requirements 13 | 14 | * Go 1.14+ 15 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/Variables.mk: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 4 | GOPATH ?= $(shell go env GOPATH) 5 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 6 | GO ?= $(shell which go) 7 | 8 | # Below generated variables ensure that every time a tool under each variable is invoked, the correct version 9 | # will be used; reinstalling only if needed. 10 | # For example for buildable-v2 variable: 11 | # 12 | # In your main Makefile (for non array binaries): 13 | # 14 | #include .bingo/Variables.mk # Assuming -dir was set to .bingo . 15 | # 16 | #command: $(BUILDABLE_V2) 17 | # @echo "Running buildable-v2" 18 | # @$(BUILDABLE_V2) 19 | # 20 | BUILDABLE_V2 := $(GOBIN)/buildable-v2-v2.0.0 21 | $(BUILDABLE_V2): $(BINGO_DIR)/buildable-v2.mod 22 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 23 | @echo "(re)installing $(GOBIN)/buildable-v2-v2.0.0" 24 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable-v2.mod -o=$(GOBIN)/buildable-v2-v2.0.0 "github.com/bwplotka/bingo-testmodule/v2/buildable" 25 | 26 | BUILDABLE_WITHREPLACE := $(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92 27 | $(BUILDABLE_WITHREPLACE): $(BINGO_DIR)/buildable-withReplace.mod 28 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 29 | @echo "(re)installing $(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92" 30 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable-withReplace.mod -o=$(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92 "github.com/bwplotka/bingo-testmodule/buildable" 31 | 32 | BUILDABLE_ARRAY := $(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e $(GOBIN)/buildable-v1.0.0 $(GOBIN)/buildable-v1.1.0 33 | $(BUILDABLE_ARRAY): $(BINGO_DIR)/buildable.mod $(BINGO_DIR)/buildable.1.mod $(BINGO_DIR)/buildable.2.mod 34 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 35 | @echo "(re)installing $(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e" 36 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.mod -o=$(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e "github.com/bwplotka/bingo-testmodule/buildable" 37 | @echo "(re)installing $(GOBIN)/buildable-v1.0.0" 38 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.1.mod -o=$(GOBIN)/buildable-v1.0.0 "github.com/bwplotka/bingo-testmodule/buildable" 39 | @echo "(re)installing $(GOBIN)/buildable-v1.1.0" 40 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.2.mod -o=$(GOBIN)/buildable-v1.1.0 "github.com/bwplotka/bingo-testmodule/buildable" 41 | 42 | BUILDABLE2 := $(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5 43 | $(BUILDABLE2): $(BINGO_DIR)/buildable2.mod 44 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 45 | @echo "(re)installing $(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5" 46 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable2.mod -o=$(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5 "github.com/bwplotka/bingo-testmodule/buildable2" 47 | 48 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable-v2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule/v2 v2.0.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable-v2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule/v2 v2.0.0 h1:Q398jJ6nKh8G0C3Pbqn8x+3h89aJl6kW6QGIv0Y0xpY= 2 | github.com/bwplotka/bingo-testmodule/v2 v2.0.0/go.mod h1:M/nPnLLSsuHrLMFOiaINFMOitFBIPEvGXs8C+6LpHDM= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable-withReplace.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | replace github.com/efficientgo/core => github.com/efficientgo/core v1.0.0-rc.0.0.20220828074345-1974bfa191ce 6 | 7 | replace github.com/oklog/run-v100 => github.com/oklog/run v1.0.0 8 | 9 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92 // buildable 10 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable-withReplace.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92 h1:sfBAuIKrtFQnq3zQESivtCrRgcexoiPH2fTQSZQn5+o= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92/go.mod h1:vtWaeKhKaAJtEWbufYcFpHicH91st0taVI/i6SJZi0o= 3 | github.com/efficientgo/core v1.0.0-rc.0.0.20220828074345-1974bfa191ce h1:L9uxGBYRmr+TPmpB5B0vUApuvyfeuYs/46qIJrbASaU= 4 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.1.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v1.0.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.1.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v1.0.0 h1:x0awJHWvozd+3oJgv9GMlhXWEEQakG/QFU/HYQl1Uq0= 2 | github.com/bwplotka/bingo-testmodule v1.0.0/go.mod h1:Mzqh1sG01xz1ikTyHwyc8sPG6NZTzkDmBpQkh/RcJy4= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v1.1.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v1.1.0 h1:0Zn+iM1MKyxxxotMBLmoM+EuBMu92VIHUvqDvt2vAew= 2 | github.com/bwplotka/bingo-testmodule v1.1.0/go.mod h1:Mzqh1sG01xz1ikTyHwyc8sPG6NZTzkDmBpQkh/RcJy4= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e h1:otoYOGKfDf7LK+2Rvr9WkRqog02dWl73AKrQeqiJLAM= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e/go.mod h1:zIO4Ofu7pH/+CpIjnrLwjCavsML3RJD0/ycK5hLZxGc= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 5 | github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= 6 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 7 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 8 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5 // buildable2 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/buildable2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5 h1:zertI+l2UVNXae3Pn8nYNRpwAR+M40SQbtIqJmNhH2Q= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5/go.mod h1:JchNF1WhrxR2C7CdF2e9m2rzmK1W+9qDbvcu8+2/stg= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/go.mod: -------------------------------------------------------------------------------- 1 | module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files. -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_6/.bingo/variables.env: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 4 | GOBIN=${GOBIN:=$(go env GOBIN)} 5 | 6 | if [ -z "$GOBIN" ]; then 7 | GOBIN="$(go env GOPATH)/bin" 8 | fi 9 | 10 | 11 | BUILDABLE_V2="${GOBIN}/buildable-v2-v2.0.0" 12 | 13 | BUILDABLE_WITHREPLACE="${GOBIN}/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92" 14 | 15 | BUILDABLE_ARRAY="${GOBIN}/buildable-v0.0.0-20221007091146-39a7f0ae0b1e ${GOBIN}/buildable-v1.0.0 ${GOBIN}/buildable-v1.1.0" 16 | 17 | BUILDABLE2="${GOBIN}/buildable2-v0.0.0-20221007091238-9d83f47b84c5" 18 | 19 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore everything 3 | * 4 | 5 | # But not these files: 6 | !.gitignore 7 | !*.mod 8 | !*.sum 9 | !README.md 10 | !Variables.mk 11 | !variables.env 12 | 13 | *tmp.mod 14 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/README.md: -------------------------------------------------------------------------------- 1 | # Project Development Dependencies. 2 | 3 | This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. 4 | 5 | * Run `bingo get` to install all tools having each own module file in this directory. 6 | * Run `bingo get ` to install that have own module file in this directory. 7 | * For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. 8 | * For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. 9 | * For go: Import `.bingo/variables.go` to for variable names. 10 | * See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. 11 | 12 | ## Requirements 13 | 14 | * Go 1.14+ 15 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/Variables.mk: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.7. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 4 | GOPATH ?= $(shell go env GOPATH) 5 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 6 | GO ?= $(shell which go) 7 | 8 | # Below generated variables ensure that every time a tool under each variable is invoked, the correct version 9 | # will be used; reinstalling only if needed. 10 | # For example for buildable-v2 variable: 11 | # 12 | # In your main Makefile (for non array binaries): 13 | # 14 | #include .bingo/Variables.mk # Assuming -dir was set to .bingo . 15 | # 16 | #command: $(BUILDABLE_V2) 17 | # @echo "Running buildable-v2" 18 | # @$(BUILDABLE_V2) 19 | # 20 | BUILDABLE_V2 := $(GOBIN)/buildable-v2-v2.0.0 21 | $(BUILDABLE_V2): $(BINGO_DIR)/buildable-v2.mod 22 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 23 | @echo "(re)installing $(GOBIN)/buildable-v2-v2.0.0" 24 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable-v2.mod -o=$(GOBIN)/buildable-v2-v2.0.0 "github.com/bwplotka/bingo-testmodule/v2/buildable" 25 | 26 | BUILDABLE_WITHREPLACE := $(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92 27 | $(BUILDABLE_WITHREPLACE): $(BINGO_DIR)/buildable-withReplace.mod 28 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 29 | @echo "(re)installing $(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92" 30 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable-withReplace.mod -o=$(GOBIN)/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92 "github.com/bwplotka/bingo-testmodule/buildable" 31 | 32 | BUILDABLE_ARRAY := $(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e $(GOBIN)/buildable-v1.0.0 $(GOBIN)/buildable-v1.1.0 33 | $(BUILDABLE_ARRAY): $(BINGO_DIR)/buildable.mod $(BINGO_DIR)/buildable.1.mod $(BINGO_DIR)/buildable.2.mod 34 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 35 | @echo "(re)installing $(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e" 36 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.mod -o=$(GOBIN)/buildable-v0.0.0-20221007091146-39a7f0ae0b1e "github.com/bwplotka/bingo-testmodule/buildable" 37 | @echo "(re)installing $(GOBIN)/buildable-v1.0.0" 38 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.1.mod -o=$(GOBIN)/buildable-v1.0.0 "github.com/bwplotka/bingo-testmodule/buildable" 39 | @echo "(re)installing $(GOBIN)/buildable-v1.1.0" 40 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable.2.mod -o=$(GOBIN)/buildable-v1.1.0 "github.com/bwplotka/bingo-testmodule/buildable" 41 | 42 | BUILDABLE2 := $(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5 43 | $(BUILDABLE2): $(BINGO_DIR)/buildable2.mod 44 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 45 | @echo "(re)installing $(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5" 46 | @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buildable2.mod -o=$(GOBIN)/buildable2-v0.0.0-20221007091238-9d83f47b84c5 "github.com/bwplotka/bingo-testmodule/buildable2" 47 | 48 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable-v2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule/v2 v2.0.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable-v2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule/v2 v2.0.0 h1:Q398jJ6nKh8G0C3Pbqn8x+3h89aJl6kW6QGIv0Y0xpY= 2 | github.com/bwplotka/bingo-testmodule/v2 v2.0.0/go.mod h1:M/nPnLLSsuHrLMFOiaINFMOitFBIPEvGXs8C+6LpHDM= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable-withReplace.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | replace github.com/efficientgo/core => github.com/efficientgo/core v1.0.0-rc.0.0.20220828074345-1974bfa191ce 6 | 7 | replace github.com/oklog/run-v100 => github.com/oklog/run v1.0.0 8 | 9 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92 // buildable 10 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable-withReplace.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92 h1:sfBAuIKrtFQnq3zQESivtCrRgcexoiPH2fTQSZQn5+o= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091003-fe4d42a37d92/go.mod h1:vtWaeKhKaAJtEWbufYcFpHicH91st0taVI/i6SJZi0o= 3 | github.com/efficientgo/core v1.0.0-rc.0.0.20220828074345-1974bfa191ce h1:L9uxGBYRmr+TPmpB5B0vUApuvyfeuYs/46qIJrbASaU= 4 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.1.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v1.0.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.1.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v1.0.0 h1:x0awJHWvozd+3oJgv9GMlhXWEEQakG/QFU/HYQl1Uq0= 2 | github.com/bwplotka/bingo-testmodule v1.0.0/go.mod h1:Mzqh1sG01xz1ikTyHwyc8sPG6NZTzkDmBpQkh/RcJy4= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v1.1.0 // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v1.1.0 h1:0Zn+iM1MKyxxxotMBLmoM+EuBMu92VIHUvqDvt2vAew= 2 | github.com/bwplotka/bingo-testmodule v1.1.0/go.mod h1:Mzqh1sG01xz1ikTyHwyc8sPG6NZTzkDmBpQkh/RcJy4= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e // buildable 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e h1:otoYOGKfDf7LK+2Rvr9WkRqog02dWl73AKrQeqiJLAM= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091146-39a7f0ae0b1e/go.mod h1:zIO4Ofu7pH/+CpIjnrLwjCavsML3RJD0/ycK5hLZxGc= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 5 | github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= 6 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 7 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 8 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable2.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.19 4 | 5 | require github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5 // buildable2 6 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/buildable2.sum: -------------------------------------------------------------------------------- 1 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5 h1:zertI+l2UVNXae3Pn8nYNRpwAR+M40SQbtIqJmNhH2Q= 2 | github.com/bwplotka/bingo-testmodule v0.0.0-20221007091238-9d83f47b84c5/go.mod h1:JchNF1WhrxR2C7CdF2e9m2rzmK1W+9qDbvcu8+2/stg= 3 | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= 4 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 5 | -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/go.mod: -------------------------------------------------------------------------------- 1 | module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files. -------------------------------------------------------------------------------- /testdata/testproject_with_bingo_v0_7/.bingo/variables.env: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.7. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 4 | GOBIN=${GOBIN:=$(go env GOBIN)} 5 | 6 | if [ -z "$GOBIN" ]; then 7 | GOBIN="$(go env GOPATH)/bin" 8 | fi 9 | 10 | 11 | BUILDABLE_V2="${GOBIN}/buildable-v2-v2.0.0" 12 | 13 | BUILDABLE_WITHREPLACE="${GOBIN}/buildable-withReplace-v0.0.0-20221007091003-fe4d42a37d92" 14 | 15 | BUILDABLE_ARRAY="${GOBIN}/buildable-v0.0.0-20221007091146-39a7f0ae0b1e ${GOBIN}/buildable-v1.0.0 ${GOBIN}/buildable-v1.1.0" 16 | 17 | BUILDABLE2="${GOBIN}/buildable2-v0.0.0-20221007091238-9d83f47b84c5" 18 | 19 | --------------------------------------------------------------------------------