go.sdls.io/sin@v0.0.9/Makefile (about)

     1  #      @ SUDOLESS SRL <contact@sudoless.org>
     2  #      This Source Code Form is subject to the
     3  #      terms of the Mozilla Public License, v.
     4  #      2.0. If a copy of the MPL was not
     5  #      distributed with this file, You can
     6  #      obtain one at
     7  #      http://mozilla.org/MPL/2.0/.
     8  
     9  THIS_MAKEFILE_VERSION = v0.1.7
    10  THIS_MAKEFILE_UPDATE = master
    11  THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
    12  THIS_MAKEFILE_URL := https://raw.githubusercontent.com/sudoless/make/$(THIS_MAKEFILE_UPDATE)/golang.mk
    13  
    14  
    15  # PATH
    16  export PATH := $(abspath bin/):${PATH}
    17  
    18  # META
    19  ifneq ("$(wildcard go.mod/)","") # check go.mod exists
    20  PROJECT_MOD_NAME := $(shell go list -m -mod=readonly)
    21  PROJECT_NAME := $(notdir $(PROJECT_MOD_NAME))
    22  endif
    23  
    24  # META - FMT
    25  FMT_MISC := \033[90;1m
    26  FMT_INFO := \033[94;1m
    27  FMT_OK   := \033[92;1m
    28  FMT_WARN := \033[33;1m
    29  FMT_END  := \033[0m
    30  FMT_PRFX := $(FMT_MISC)=>$(FMT_END)
    31  
    32  # GO
    33  export CGO_ENABLED ?= 0
    34  GO ?= GO111MODULE=on go
    35  GO_TAGS ?= timetzdata
    36  
    37  # OUTPUT
    38  DIR_OUT   := out
    39  FILE_COV  := $(DIR_OUT)/cover.out
    40  
    41  # GIT
    42  ifneq ("$(wildcard .git/)","") # check .git/ exists
    43  GIT_TAG_HASH := $(shell git rev-list --abbrev-commit --tags --max-count=1)
    44  GIT_TAG := $(shell git describe --abbrev=0 --tags ${GIT_TAG_HASH} 2>/dev/null || true)
    45  GIT_VERSION := $(GIT_TAG)
    46  GIT_LATEST_HASH := $(shell git rev-parse --short HEAD)
    47  GIT_LATEST_COMMIT_DATE := $(shell git log -1 --format=%cd --date=format:"%Y%m%d")
    48  GIT_CHANGES := $(shell git rev-list $(GIT_TAG)..HEAD --count)
    49  
    50  ifneq ($(GIT_LATEST_HASH),$(GIT_TAG_HASH))
    51  	GIT_VERSION := $(GIT_VERSION)-wip$(GIT_CHANGES).$(GIT_LATEST_HASH)
    52  endif
    53  ifeq ($(GIT_VERSION),)
    54  	GIT_VERSION := -new.$(GIT_LATEST_HASH).$(GIT_LATEST_COMMIT_DATE)
    55  endif
    56  ifneq ($(shell git status --porcelain),)
    57  	GIT_VERSION := $(GIT_VERSION)-dirty.$(GIT_LATEST_COMMIT_DATE).$$(whoami)
    58  endif
    59  endif
    60  
    61  # BUILD
    62  BUILD_HASH ?= $(GIT_LATEST_HASH)
    63  BUILD_TIME ?= $$(date +%s)
    64  BUILD_VERSION ?= $(GIT_VERSION)
    65  
    66  # SOURCE
    67  SOURCE_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor)
    68  
    69  # DEV - EXTERNAL TOOLS
    70  DEV_EXTERNAL_TOOLS=\
    71  	github.com/golangci/golangci-lint/cmd/golangci-lint@v1.39.0 \
    72  	github.com/securego/gosec/v2/cmd/gosec@v2.7.0 \
    73  	github.com/client9/misspell/cmd/misspell@v0.3.4 \
    74  	github.com/fzipp/gocyclo/cmd/gocyclo@v0.3.1 \
    75  	github.com/jstemmer/go-junit-report@v0.9.1 \
    76  	golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@v0.1.0 \
    77  	mvdan.cc/gofumpt@v0.1.1 \
    78  	gotest.tools/gotestsum@v1.6.4
    79  
    80  # DOCKER
    81  THIS_DOCKER_BUILD_FLAGS ?=
    82  THIS_DOCKER_DIR ?= ./deployment/docker
    83  THIS_DOCKER_FILE ?= $(THIS_DOCKER_DIR)/Dockerfile
    84  THIS_DOCKER_TAG ?= $(BUILD_VERSION)
    85  THIS_DOCKER_IMAGE ?= $(PROJECT_MOD_NAME)/$*
    86  THIS_DOCKER_ARTIFACT ?= $(THIS_DOCKER_IMAGE):$(THIS_DOCKER_TAG)
    87  
    88  
    89  all: clean align spelling check lint test
    90  
    91  
    92  .PHONY: info
    93  info: ## display project information
    94  	@printf "$(FMT_PRFX) printing info\n"
    95  	@printf "$(FMT_PRFX) project mod name $(FMT_INFO)$(PROJECT_MOD_NAME)$(FMT_END)\n"
    96  	@printf "$(FMT_PRFX) project name $(FMT_INFO)$(PROJECT_NAME)$(FMT_END)\n"
    97  	@printf "$(FMT_PRFX) makefile $(FMT_INFO)$(THIS_MAKEFILE)$(FMT_END)\n"
    98  	@printf "$(FMT_PRFX) makefile version $(FMT_INFO)$(THIS_MAKEFILE_VERSION)$(FMT_END)\n"
    99  	@printf "$(FMT_PRFX) build hash $(FMT_INFO)$(BUILD_HASH)$(FMT_END)\n"
   100  	@printf "$(FMT_PRFX) build current version $(FMT_INFO)$(BUILD_VERSION)$(FMT_END)\n"
   101  	@printf "$(FMT_PRFX) git tag commit $(FMT_INFO)$(GIT_TAG_HASH)$(FMT_END)\n"
   102  	@printf "$(FMT_PRFX) git tag $(FMT_INFO)$(GIT_TAG)$(FMT_END)\n"
   103  	@printf "$(FMT_PRFX) git version $(FMT_INFO)$(GIT_VERSION)$(FMT_END)\n"
   104  	@printf "$(FMT_PRFX) git latest commit $(FMT_INFO)$(GIT_LATEST_HASH)$(FMT_END)\n"
   105  	@printf "$(FMT_PRFX) git latest commit date $(FMT_INFO)$(GIT_LATEST_COMMIT_DATE)$(FMT_END)\n"
   106  	@printf "$(FMT_PRFX) git commit changes $(FMT_INFO)$(GIT_CHANGES)$(FMT_END)\n"
   107  
   108  .PHONY: init
   109  init: ## setup a barebones Go project
   110  	@mkdir -p cmd/ pkg/ docs/ scripts/ data/ deployment/ internal/
   111  	@touch deployment/.netrc
   112  	@echo "deployment/.netrc\n**/.env.*" > .gitignore
   113  
   114  .PHONY: info-version
   115  info-version: ## prints the BUILD_VERSION and nothing else
   116  	@printf "$(BUILD_VERSION)"
   117  
   118  .PHONY: run-%
   119  run-%: build-% ## run the specified target
   120  	@printf "$(FMT_PRFX) running $(FMT_INFO)$*$(FMT_END) from $(FMT_INFO)$(DIR_OUT)/dist/$*_$$(go env GOOS)_$$(go env GOARCH)$(FMT_END)\n"
   121  	@$(DIR_OUT)/dist/$*_$$(go env GOOS)_$$(go env GOARCH)
   122  
   123  .PHONY: build-%
   124  build-%: APP_OUT ?= $*_$$(go env GOOS)_$$(go env GOARCH)
   125  build-%: ## build a specific cmd/$(TARGET)/... into $(DIR_OUT)/dist/$(TARGET)...
   126  	@printf "$(FMT_PRFX) building $(FMT_INFO)$*$(FMT_END) version=$(FMT_INFO)$(BUILD_VERSION)$(FMT_END)\
   127   buildhash=$(FMT_INFO)$(BUILD_HASH)$(FMT_END)\n"
   128  	@printf "$(FMT_PRFX) using $(FMT_INFO)$$(go version)$(FMT_END)\n"
   129  	@$(GO) build -trimpath -tags "$(GO_TAGS)" \
   130  		-ldflags="-w -s \
   131  			-X main._serviceName=$*           \
   132  			-X main._version=$(BUILD_VERSION) \
   133  			-X main._buildTime=$(BUILD_TIME)  \
   134  			-X main._buildHash=$(BUILD_HASH)" \
   135  		-o $(DIR_OUT)/dist/$(APP_OUT) \
   136  		./cmd/$*/...
   137  	@printf "$(FMT_PRFX) built binary $(FMT_INFO)$(DIR_OUT)/dist/$(APP_OUT)$(FMT_END)\n"
   138  
   139  .PHONY: install-%
   140  install-%: APP_OUT ?= $*_$$(go env GOOS)_$$(go env GOARCH)
   141  install-%: GOBIN ?= $(GOPATH)/bin
   142  install-%: build-% ## install the built binary to $GOBIN
   143  	@printf "$(FMT_PRFX) installing $(FMT_INFO)$*$(FMT_END) ($(FMT_WARN)$(APP_OUT)$(FMT_END)) at $(FMT_INFO)$(GOBIN)/$*$(FMT_END)\n"
   144  	@cp $(DIR_OUT)/dist/$(APP_OUT) $(GOBIN)/$*
   145  	@printf "$(FMT_PRFX) $(FMT_OK)ok$(FMT_END) (which=$(FMT_INFO)$(shell which $*)$(FMT_END))\n"
   146  
   147  .PHONY: clean
   148  clean: ## remove build time generated files
   149  	@printf "$(FMT_PRFX) removing output directory\n"
   150  	@rm -rf $(DIR_OUT)/
   151  
   152  .PHONY: purge
   153  purge: clean ## remove everything that could cause environment issues
   154  	@printf "$(FMT_PRFX) deleting system32\n"
   155  	$(GO) clean -cache
   156  	$(GO) clean -testcache
   157  	$(GO) clean -modcache
   158  
   159  $(DIR_OUT):
   160  	@mkdir -p $(DIR_OUT)
   161  
   162  .PHONY: test
   163  test: export CGO_ENABLED=1
   164  test: $(DIR_OUT) ## run unit tests
   165  	@printf "$(FMT_PRFX) running tests\n"
   166  	@gotestsum \
   167  		--junitfile $(FILE_COV).xml \
   168  		--format short -- \
   169  		-race \
   170  		-timeout=30s -parallel=20 -failfast \
   171  		-covermode=atomic -coverpkg=./... -coverprofile=$(FILE_COV).txt \
   172  		./...
   173  
   174  .PHONY: test-deps
   175  test-deps: ## run tests with dependencies
   176  	@printf "$(FMT_PRFX) running all tests\n"
   177  	$(GO) test all
   178  
   179  .PHONY: bench
   180  bench: ## run benchmarks
   181  	@printf "$(FMT_PRFX) running benchmarks\n"
   182  	$(GO) test -exclude-dir=vendor -exclude-dir=.cache -bench=. -benchmem -benchtime=10s ./...
   183  
   184  .PHONY: cover
   185  cover: ## open coverage file in browser
   186  	@printf "$(FMT_PRFX) opening coverage file in browser\n"
   187  	$(GO) tool cover -html=$(FILE_COV).txt
   188  
   189  .PHONY: tidy
   190  tidy: ## tidy and verify go modules
   191  	@printf "$(FMT_PRFX) tidying go modules\n"
   192  	$(GO) mod tidy
   193  	$(GO) mod verify
   194  
   195  .PHONY: download
   196  download: ## download go modules
   197  	@printf "$(FMT_PRFX) downloading dependencies as modules\n"
   198  	@$(GO) mod $(GO_MOD) download -x
   199  
   200  .PHONY: vendor
   201  vendor: ## tidy, vendor and verify dependencies
   202  	@printf "$(FMT_PRFX) downloading and creating vendor dependencies\n"
   203  	$(GO) mod tidy -v
   204  	$(GO) mod vendor -v
   205  	$(GO) mod verify
   206  
   207  .PHONY: updates
   208  updates: ## display outdated direct dependencies
   209  	@printf "$(FMT_PRFX) checking for direct dependencies updates\n"
   210  	@$(GO) list -u -m -mod=readonly -json all | go-mod-outdated -direct
   211  
   212  .PHONY: lint
   213  lint: ## run golangci linter
   214  	@printf "$(FMT_PRFX) running golangci-lint\n"
   215  	@golangci-lint run -v --timeout 10m --skip-dirs=".cache/|vendor/|scripts/|docs/|deployment/"  ./...
   216  
   217  .PHONY: check
   218  check: ## run cyclic, security, performance, etc checks
   219  	@printf "$(FMT_PRFX) running cyclic analysis\n"
   220  	@gocyclo -over 16 -ignore ".cache/|vendor/|scripts/|docs/|deployment/" .
   221  	@printf "$(FMT_PRFX) running static security analysis\n"
   222  	@gosec -tests -fmt=json -quiet -exclude-dir=vendor -exclude-dir=.cache -exclude-dir=scripts -exclude-dir=docs -exclude-dir=deployment ./...
   223  
   224  .PHONY: align
   225  align: ## align struct fields to use less memory
   226  	@printf "$(FMT_PRFX) checking struct field memory alignment\n"
   227  	@$(GO) list -f '{{.Dir}}' ./... | grep -v /vendor/ | \
   228  		xargs fieldalignment ; if [[ $$? -eq 1 ]]; then  \
   229  			printf "$(FMT_PRFX) $(FMT_WARN)unaligned struct fields detected$(FMT_END), check above output\n"; \
   230  			printf "$(FMT_PRFX) to auto-fix run $(FMT_INFO)make align-fix$(FMT_END)\n"; \
   231  		fi
   232  		@printf "$(FMT_PRFX) $(FMT_OK)ok$(FMT_END)\n"; \
   233  
   234  .PHONY: align-fix
   235  align-fix: ## autofix misaligned struct fields
   236  		@printf "$(FMT_PRFX) fixing struct field memory alignment\n"
   237  		@$(GO) list -f '{{.Dir}}' ./... | grep -v /vendor/ | xargs fieldalignment -fix || exit 0;
   238  		@printf "$(FMT_PRFX) aligned above files\n"
   239  		@printf "$(FMT_PRFX) re-running $(FMT_INFO)make align$(FMT_END) to check for stragglers\n"
   240  		@make align
   241  
   242  .PHONY: fmt
   243  fmt: ## format source files using gofumpt
   244  	@printf "$(FMT_PRFX) formatting go files\n"
   245  	@gofumpt -w $(SOURCE_FILES)
   246  
   247  .PHONY: spelling
   248  spelling: ## run misspell check
   249  	@printf "$(FMT_PRFX) checking for spelling errors\n"
   250  	@misspell -error pkg/
   251  	@misspell -error cmd/
   252  
   253  .PHONY: dev-deps
   254  dev-deps: ## pull developer/ci dependencies
   255  	@printf "$(FMT_PRFX) pulling development/CI dependencies\n"
   256  	@for tool in  $(DEV_EXTERNAL_TOOLS) ; do \
   257  		printf "$(FMT_PRFX) installing/updating: $(FMT_INFO)$$tool$(FMT_END)\n" ; \
   258  		$(GO) install $$tool; \
   259  	done
   260  
   261  .PHONY: docker-list
   262  docker-list: ## list docker images for the current project
   263  	@printf "$(FMT_PRF) listing images for $(FMT_INFO)$(PROJECT_NAME)$(FMT_END) project\n"
   264  	@docker images -f label=project=$(PROJECT_NAME)
   265  
   266  .PHONY: docker-build-%
   267  docker-build-%: ## build docker image
   268  	@printf "$(FMT_PRFX) building with docker $(FMT_INFO)$$(docker version -f 'server: {{.Server.Version}}, client: {{.Client.Version}}')$(FMT_END)\n"
   269  	@printf "$(FMT_PRFX) docker on host $(FMT_WARN)$(DOCKER_HOST)$(FMT_END)\n"
   270  	@printf "$(FMT_PRFX) docker file $(FMT_INFO)$(THIS_DOCKER_FILE)$(FMT_END)\n"
   271  	@printf "$(FMT_PRFX) docker artifact output $(FMT_INFO)$(THIS_DOCKER_ARTIFACT)$(FMT_END)\n"
   272  	@DOCKER_BUILDKIT=1 docker build $(THIS_DOCKER_BUILD_FLAGS) \
   273  		--secret id=netrc,src=./deployment/.netrc \
   274  		--build-arg APP_NAME=$* \
   275  		--build-arg BUILD_VERSION=$(BUILD_VERSION) \
   276  		--build-arg BUILD_HASH=$(BUILD_HASH) \
   277  		-f $(THIS_DOCKER_FILE) -t $(THIS_DOCKER_ARTIFACT) \
   278  		--label "project=$(PROJECT_NAME)" \
   279  		--label "build_hash=$(BUILD_HASH)" \
   280  		--label "build_time=$(BUILD_TIME)" \
   281  		--label "build_machine=$$(whoami)@$$(hostname)" .
   282  	@printf "$(FMT_PRFX) docker artifact output $(FMT_INFO)$(THIS_DOCKER_ARTIFACT)$(FMT_END)\n"
   283  	@printf "$(FMT_PRFX) run $(FMT_INFO)docker tag $(THIS_DOCKER_ARTIFACT) ...$(FMT_END) to change name\n"
   284  
   285  .PHONY: docker-tag-%
   286  docker-tag-%: ## tags the last built docker image for the given package using its version and $IMAGE_BASE
   287  	@printf "$(FMT_PRFX) tagging $(FMT_INFO)$(THIS_DOCKER_ARTIFACT)$(FMT_END)\n"
   288  	@printf "$(FMT_PRFX) as      $(FMT_INFO)$(IMAGE_BASE)/$*:$(THIS_DOCKER_TAG)$(FMT_END)\n"
   289  	@docker tag $(THIS_DOCKER_ARTIFACT) $(IMAGE_BASE)/$*:$(THIS_DOCKER_TAG)
   290  
   291  .PHONY: docker-push-%
   292  docker-push-%: ## pushes the last tagged docker image for the given package using its version and $IMAGE_BASE
   293  	@printf "$(FMT_PRFX) pushing $(FMT_INFO)$(IMAGE_BASE)/$*:$(THIS_DOCKER_TAG)$(FMT_END)\n"
   294  	@docker push $(IMAGE_BASE)/$*:$(THIS_DOCKER_TAG)
   295  
   296  .PHONY: mk-update
   297  mk-update: ## update this Makefile, use THIS_MAKEFILE_UPDATE=... to specify version
   298  	@printf "$(FMT_PRFX) updating this makefile from $(FMT_INFO)$(THIS_MAKEFILE_VERSION)$(FMT_END) to $(FMT_INFO)$(THIS_MAKEFILE_UPDATE)$(FMT_END)\n"
   299  	@curl -s $(THIS_MAKEFILE_URL) > $(THIS_MAKEFILE).new
   300  	@awk '/^#### CUSTOM/,0' Makefile | tail -n +2 >> $(THIS_MAKEFILE).new
   301  	@mv -f Makefile.new Makefile
   302  
   303  .PHONY: help
   304  help:
   305  	@grep -h -E '^[a-zA-Z_-]+%?:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
   306  
   307  
   308  #### CUSTOM # Anything under the CUSTOM line is migrated by the mk-update command to the new Makefile version