diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5829aa2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,41 @@ +# This code is provided by github.com/bool64/dev. +name: test +on: + push: + branches: + - master + pull_request: +# Cancel the workflow in progress in newer build is about to start. +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true +jobs: + test: + strategy: + matrix: + go-version: [1.17.x] + runs-on: ubuntu-latest + steps: + - name: Install Go stable + if: matrix.go-version != 'tip' + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v3 + - name: Go cache + uses: actions/cache@v3 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-cache-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go-cache + - name: Test + id: test + run: | + make test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e923a0 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +-include makefiles/help.mk + +.PHONY: test + +## Run tests +test: + @echo "Running tests" + @bash test.sh \ No newline at end of file diff --git a/README.md b/README.md index daa6a29..b2261b1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Add `Makefile` to your module with includes standard targets. ```Makefile #GOLANGCI_LINT_VERSION := "v1.61.0" # Optional configuration to pinpoint golangci-lint version. +# MODULES is a list of dev modules (mk) to be included in the project. +MODULES := \ + DEVGO_PATH=github.com/bool64/dev + # The head of Makefile determines location of dev-go to include standard targets. GO ?= go export GO111MODULE = on @@ -36,24 +40,31 @@ ifneq "$(GOFLAGS)" "" $(info GOFLAGS: ${GOFLAGS}) endif -ifneq "$(wildcard ./vendor )" "" - $(info Using vendor) - modVendor = -mod=vendor +# Use vendored dependencies if available. +ifneq ($(wildcard ./vendor),) + modVendor := -mod=vendor ifeq (,$(findstring -mod,$(GOFLAGS))) export GOFLAGS := ${GOFLAGS} ${modVendor} endif - ifneq "$(wildcard ./vendor/github.com/bool64/dev)" "" - DEVGO_PATH := ./vendor/github.com/bool64/dev - endif endif -ifeq ($(DEVGO_PATH),) - DEVGO_PATH := $(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m github.com/bool64/dev) - ifeq ($(DEVGO_PATH),) - $(info Module github.com/bool64/dev not found, downloading.) - DEVGO_PATH := $(shell export GO111MODULE=on && $(GO) get github.com/bool64/dev && $(GO) list -f '{{.Dir}}' -m github.com/bool64/dev) - endif -endif +# Set dev module paths or download them. +$(foreach module,$(MODULES), \ + $(eval key=$(word 1,$(subst =, ,$(module)))); \ + $(eval value=$(word 2,$(subst =, ,$(module)))); \ + \ + $(if $(wildcard ./vendor/$(value)), \ + $(eval export $(key)=./vendor/$(value)); \ + ) \ + \ + $(if $(strip $($(key))), , \ + $(eval export $(key)=$(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m $(value))); \ + $(if $(strip $($(key))), \ + $(info Module $(value) not found, downloading.); \ + $(eval export $(key)=$(shell export GO111MODULE=on && $(GO) get $(value) && $(GO) list -f '{{.Dir}}' -m $(value))); \ + ) \ + ) \ +) -include $(DEVGO_PATH)/makefiles/main.mk -include $(DEVGO_PATH)/makefiles/lint.mk @@ -87,3 +98,71 @@ Usage You can include `$(DEVGO_PATH)/makefiles/build.mk` to add automated versioning of build artifacts. Make will configure `ldflags` to set up [version info](./version/info.go), then you can access it in runtime with `version.Info()` or expose with [HTTP handler](./version/handler.go). Version information also includes versions of dependencies. + +## Create Plugins + +You can add plugins for your project and include them in the Makefile. For example, you can add a plugin +to run `protoc` targets which is place it in `github///makefiles/protoc.mk`. + +Then include it in the Makefile: + +```Makefile +# MODULES is a list of dev modules (mk) to be included in the project. +MODULES := \ + DEVGO_PATH=github.com/bool64/dev \ + DEVGRPCGO_PATH=github///makefiles/protoc.mk + + ... + +-include $(DEVGO_PATH)/makefiles/bench.mk +-include $(DEVGO_PATH)/makefiles/reset-ci.mk + +-include $(DEVGRPCGO_PATH)/makefiles/protoc.mk +``` + +Then add `github//` to your module with unused import defined in `dev_test.go`: + +```go +package mymodule_test + +import ( + _ "github.com/bool64/dev" // Include development helpers to project. + _ "github.com//" // Include custom plugins to project. +) +``` + +`DEVGRPCGO_PATH` is a path to the plugin module. It will be the path to the remote repository. + +### Plugin Structure + +The plugin should have the following structure: + +```makefile +GO ?= go + +PWD ?= $(shell pwd) + +DEVGRPCGO_PATH ?= $(PWD)/vendor/github.com// +DEVGRPCGO_SCRIPTS ?= $(DEVGRPCGO_PATH)/scripts # In case there is a scripts directory. + +## Check/install protoc tool +protoc-cli: + @bash $(DEVGRPC_SCRIPTS)/protoc-gen-cli.sh + + +.PHONY: protoc-cli +``` + +Then `make` will have these targets: + +``` +Usage + test: Run tests + test-unit: Run unit tests + test-unit-multi: Run unit tests multiple times + lint: Check with golangci-lint + fix-lint: Apply goimports and gofmt + bench: Run benchmark, iterations count controlled by BENCH_COUNT, default 5. + github-actions: Replace GitHub Actions from template + protoc-cli: Check/install protoc tool +``` \ No newline at end of file diff --git a/makefiles/base.mk b/makefiles/base.mk index d1ea9ce..c4dabdf 100644 --- a/makefiles/base.mk +++ b/makefiles/base.mk @@ -1,5 +1,9 @@ #GOLANGCI_LINT_VERSION := "v1.61.0" # Optional configuration to pinpoint golangci-lint version. +# MODULES is a list of dev modules (mk) to be included in the project. +MODULES := \ + DEVGO_PATH=github.com/bool64/dev + # The head of Makefile determines location of dev-go to include standard targets. GO ?= go export GO111MODULE = on @@ -8,24 +12,31 @@ ifneq "$(GOFLAGS)" "" $(info GOFLAGS: ${GOFLAGS}) endif -ifneq "$(wildcard ./vendor )" "" - $(info Using vendor) - modVendor = -mod=vendor +# Use vendored dependencies if available. +ifneq ($(wildcard ./vendor),) + modVendor := -mod=vendor ifeq (,$(findstring -mod,$(GOFLAGS))) export GOFLAGS := ${GOFLAGS} ${modVendor} endif - ifneq "$(wildcard ./vendor/github.com/bool64/dev)" "" - DEVGO_PATH := ./vendor/github.com/bool64/dev - endif endif -ifeq ($(DEVGO_PATH),) - DEVGO_PATH := $(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m github.com/bool64/dev) - ifeq ($(DEVGO_PATH),) - $(info Module github.com/bool64/dev not found, downloading.) - DEVGO_PATH := $(shell export GO111MODULE=on && $(GO) get github.com/bool64/dev && $(GO) list -f '{{.Dir}}' -m github.com/bool64/dev) - endif -endif +# Set dev module paths or download them. +$(foreach module,$(MODULES), \ + $(eval key=$(word 1,$(subst =, ,$(module)))); \ + $(eval value=$(word 2,$(subst =, ,$(module)))); \ + \ + $(if $(wildcard ./vendor/$(value)), \ + $(eval export $(key)=./vendor/$(value)); \ + ) \ + \ + $(if $(strip $($(key))), , \ + $(eval export $(key)=$(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m $(value))); \ + $(if $(strip $($(key))), \ + $(info Module $(value) not found, downloading.); \ + $(eval export $(key)=$(shell export GO111MODULE=on && $(GO) get $(value) && $(GO) list -f '{{.Dir}}' -m $(value))); \ + ) \ + ) \ +) -include $(DEVGO_PATH)/makefiles/main.mk -include $(DEVGO_PATH)/makefiles/lint.mk diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..ee6e319 --- /dev/null +++ b/test.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +PWD=$(pwd) + +TESTDATA_PATH="$PWD/testdata" +OUTPUT="$PWD/testdata/make.output" +TEST_OUTPUT="make.out" + +# tmake is the base command to run make +# Every timme the command runs, it runs in a new shell with the local env +# avoiding to use the env from the upstream runner +tmake="make" + + +strip_output() { + # Regular expression to match both error message formats and extract "Error 1" + error_pattern='make(\\[[0-9]+\\])?:.*Error 1' + + cat "$TEST_OUTPUT" | \ + grep -v 'Entering directory' | \ + grep -v 'Leaving directory' | \ + grep -v 'awk: warning: command line argument .* is a directory: skipped' | \ + awk -v pattern="$error_pattern" '{ while (match($0, pattern)) { $0 = substr($0, 1, RSTART-1) "Error 1" substr($0, RSTART+RLENGTH); } } 1' \ + > "$TEST_OUTPUT.tmp" && mv "$TEST_OUTPUT.tmp" "$TEST_OUTPUT" +} + +check_output() { +# cat "$1" > "$2" + # Checking the output + diff "$1" "$2" + if [ $? -ne 0 ]; then + if [ -n "$TEST_FILE" ]; then + echo "Error in $TEST_FILE:${BASH_LINENO[0]}: make output is not the same" + fi + exit 1 + fi +} + +# Record the start time +start_time=$(date +%s) + +# Running test +printf "Test make -> " +cd "$TESTDATA_PATH" && PWD="$TESTDATA_PATH" +$tmake > "$TEST_OUTPUT" 2>/dev/null +if [ $? -ne 0 ]; then + echo "make failed" + exit 1 +fi + +# Removing the lines that are not part of the output but are appended by github actions +strip_output +# Checking the output +check_output "$TEST_OUTPUT" "$OUTPUT" + +# Record the end time +end_time=$(date +%s.%N) + +# Calculate the elapsed time with two decimal places +elapsed_time=$(echo "$end_time - $start_time" | bc -l | xargs printf "%.2f\n") + +echo "[OK] ${elapsed_time}s" \ No newline at end of file diff --git a/testdata/Makefile b/testdata/Makefile new file mode 100644 index 0000000..998a585 --- /dev/null +++ b/testdata/Makefile @@ -0,0 +1,51 @@ +#GOLANGCI_LINT_VERSION := "v1.61.0" # Optional configuration to pinpoint golangci-lint version. + +# MODULES is a list of dev modules (mk) to be included in the project. +MODULES := \ + DEVGO_PATH=github.com/bool64/dev + + +# The head of Makefile determines the location of dev-go to include standard targets. +GO ?= go +export GO111MODULE = on + +ifneq "$(GOFLAGS)" "" + $(info GOFLAGS: ${GOFLAGS}) +endif + +# Use vendored dependencies if available. +ifneq ($(wildcard ./vendor),) + modVendor := -mod=vendor + ifeq (,$(findstring -mod,$(GOFLAGS))) + export GOFLAGS := ${GOFLAGS} ${modVendor} + endif +endif + +# Set dev module paths or download them. +$(foreach module,$(MODULES), \ + $(eval key=$(word 1,$(subst =, ,$(module)))); \ + $(eval value=$(word 2,$(subst =, ,$(module)))); \ + \ + $(if $(wildcard ./vendor/$(value)), \ + $(eval export $(key)=./vendor/$(value)); \ + ) \ + \ + $(if $(strip $($(key))), , \ + $(eval export $(key)=$(shell GO111MODULE=on $(GO) list ${modVendor} -f '{{.Dir}}' -m $(value))); \ + $(if $(strip $($(key))), \ + $(info Module $(value) not found, downloading.); \ + $(eval export $(key)=$(shell export GO111MODULE=on && $(GO) get $(value) && $(GO) list -f '{{.Dir}}' -m $(value))); \ + ) \ + ) \ +) + +-include $(DEVGO_PATH)/makefiles/main.mk +-include $(DEVGO_PATH)/makefiles/lint.mk +-include $(DEVGO_PATH)/makefiles/test-unit.mk +-include $(DEVGO_PATH)/makefiles/bench.mk +-include $(DEVGO_PATH)/makefiles/reset-ci.mk + +# Add your custom targets here. + +## Run tests +test: test-unit diff --git a/testdata/dev.go b/testdata/dev.go new file mode 100644 index 0000000..349bcca --- /dev/null +++ b/testdata/dev.go @@ -0,0 +1,6 @@ +//go:build never +// +build never + +package noprune + +import _ "github.com/bool64/dev" // Include CI/Dev scripts to project. diff --git a/testdata/go.mod b/testdata/go.mod new file mode 100644 index 0000000..fc91029 --- /dev/null +++ b/testdata/go.mod @@ -0,0 +1,7 @@ +module testenv + +go 1.17 + +replace github.com/bool64/dev v0.2.36 => ./../ + +require github.com/bool64/dev v0.2.36 // indirect diff --git a/testdata/make.output b/testdata/make.output new file mode 100644 index 0000000..4907eb3 --- /dev/null +++ b/testdata/make.output @@ -0,0 +1,13 @@ +Module github.com/bool64/dev not found, downloading. +Usage + test:  Run tests + lint:  Check with golangci-lint + fix-lint:  Apply goimports and gofmt + test-unit:  Run unit tests + test-unit-multi:  Run unit tests multiple times, use `UNIT_TEST_COUNT=10 make test-unit-multi` to control count + bench:  Run benchmark and show result stats, iterations count controlled by BENCH_COUNT, default 5. + bench-run:  Run benchmark, iterations count controlled by BENCH_COUNT, default 5. + bench-stat-diff:  Show benchmark comparison with base branch. + bench-stat:  Show result of benchmark. + reset-ci:  Reset CI files from bool64/dev templates, make sure to review changes before committing. + github-actions:  Replace GitHub Actions from template