diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..06af3b6 --- /dev/null +++ b/.env.dist @@ -0,0 +1,6 @@ +COMPOSE_PROJECT_NAME=h-skills +ALPINE_VER=3.19 +GOVERTER_VER=v1.4.0 +GOLANGCI_LINT_VER=v1.56.2 +POSTGRES_VER=16.2 +AIR_VER=1.52.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1146c3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.task +/.env +/bin/env +/tmp/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..9851b72 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,2 @@ +# golangci-lint configuration file. +# Read more at: https://github.com/golangci/golangci-lint#config-file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..358e3ee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +ARG POSTGRES_VER +ARG GO_VER +ARG ALPINE_VER + +FROM postgres:${POSTGRES_VER} AS postgres + +COPY ./db/* /docker-entrypoint-initdb.d/ + +RUN set -ex \ + && chmod 1777 /tmp + +FROM golang:${GO_VER}-alpine${ALPINE_VER} AS golang + +ARG AIR_VER +RUN set -ex \ + && chmod 1777 /tmp \ + && apk add --no-cache \ + git \ + tzdata \ + && go install github.com/cosmtrek/air@v${AIR_VER} diff --git a/Taskfile.go.yaml b/Taskfile.go.yaml new file mode 100644 index 0000000..e9e9a56 --- /dev/null +++ b/Taskfile.go.yaml @@ -0,0 +1,129 @@ +version: '3' + +vars: + GOFUMPT_VER: '{{.GOFUMPT_VER | default "latest"}}' + GOIMPORTS_LOCAL: '{{.GOIMPORTS_LOCAL | default nil}}' + GOIMPORT_VER: '{{.GOIMPORT_VER | default "latest"}}' + GOLANGCI_LINT_VER: '{{.GOLANGCI_LINT_VER | default "latest"}}' + GOVULNCHECK_VER: '{{.GOVULNCHECK_VER | default "latest"}}' + PROTOLINT_VER: '{{.PROTOLINT_VER | default "latest"}}' + STATICCHECK_VER: '{{.STATICCHECK_VER | default "latest"}}' + TMP_DIR: '{{.TMP_DIR | default "./tmp"}}' + +tasks: + # ======= + # HELPERS + # ======= + + lint: + desc: golangci-lint + protolint. + cmds: + - task: govulncheck + - task: staticcheck + - task: golangci-lint + - task: protolint + + cs: + desc: fmt + goimports. + cmds: + - task: fmt + - task: goimports + + # =============== + # QUALITY CONTROL + # =============== + + fmt: + desc: format code using gofumpt. + cmds: + - >- + go run mvdan.cc/gofumpt@{{.GOFUMPT_VER}} -l -w . + sources: + - ./**/*.go + + goimports: + desc: format imports using goimports. + cmds: + - >- + go run golang.org/x/tools/cmd/goimports@{{.GOIMPORT_VER}} + {{if .GOIMPORTS_LOCAL}} -local="{{.GOIMPORTS_LOCAL}}"{{end}} + -w . + sources: + - ./**/*.go + + tidy: + desc: go mod tidy + go mod verify. + cmds: + - go mod tidy -v + - go mod verify + sources: + - ./**/*.go + - go.mod + - go.sum + + golangci-lint: + desc: golangci-lint. + cmds: + - go run github.com/golangci/golangci-lint/cmd/golangci-lint@{{.GOLANGCI_LINT_VER}} run -v ./... + sources: + - ./**/*.go + + govulncheck: + desc: govulncheck. + cmds: + - go run golang.org/x/vuln/cmd/govulncheck@{{.GOVULNCHECK_VER}} ./... + sources: + - ./**/*.go + + staticcheck: + desc: staticcheck. + cmds: + - go run honnef.co/go/tools/cmd/staticcheck@{{.STATICCHECK_VER}} -checks=all,-ST1000 ./... + sources: + - ./**/*.go + + protolint: + desc: protolint -fix. + cmds: + - cmd: go run github.com/yoheimuta/protolint/cmd/protolint@{{.PROTOLINT_VER}} lint -fix . + ignore_error: true + sources: &protolint_sources + - ./**/*.proto + + protolint:check: + desc: protolint check. + cmds: + - go run github.com/yoheimuta/protolint/cmd/protolint@{{.PROTOLINT_VER}} lint . + sources: *protolint_sources + + # =========== + # DEVELOPMENT + # =========== + + generate: + desc: go generate + cmds: + - go generate ./... + sources: + - ./**/*.go + + vendor: + desc: go mod vendor. + cmds: + - go mod vendor -v + sources: + - go.mod + - go.sum + generates: + - vendor + + test: + desc: go tests ./... + cmds: + - go test -race -buildvcs -coverprofile={{.TMP_DIR}}/coverage.out -vet=off ./... + + test:cover: + desc: tests and display coverage. + deps: [ test ] + cmds: + - go tool cover -html={{.TMP_DIR}}/coverage.out diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..cc19078 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,181 @@ +version: '3' + +includes: + go: + taskfile: ./Taskfile.go.yaml + +output: prefixed + +silent: true +interval: 1s + +dotenv: +- ./.env + +vars: + NEWLINE_EXCLUDE: >- + -not -path "./.git/*" + -not -path "./.idea/*" + -not -path "./vendor/*" + -not -path "./tmp/*" + -not -name "*.ico" + -not -name "*.png" + -not -name "*.svg" + +tasks: + # ======= + # HELPERS + # ======= + + all: + desc: generate + tidy + lint + test. + cmds: + - task: generate + - task: go:tidy + - task: go:lint + - task: go:test + + generate: + desc: all generators + cs. + cmds: + - task: goverter + - task: go:generate + - task: cs + + cs: + desc: go:cs + newline. + cmds: + - task: go:cs + - task: newline + + # =============== + # QUALITY CONTROL + # =============== + + newline: + desc: add newline at end file. + cmds: + - >- + find . + {{.NEWLINE_EXCLUDE}} + -type f -exec grep -Iq . {} \; -and -print0 + | xargs -0 -n 1 sh -c 'test -z "$(tail -c 1 "$0")" + || (echo "" >> $0 && echo $0)' + || exit 1 + sources: &newline_sources + - ./**/* + - exclude: "*.ico" + - exclude: "*.png" + - exclude: "*.svg" + + newline:check: + desc: check newline at end file. + cmds: + - find . + {{.NEWLINE_EXCLUDE}} + -type f -exec grep -Iq . {} \; -and -print0 + | xargs -0 -n 1 sh -c 'test -z "$(tail -c 1 "$0")" + || (echo "No new line at end of $0" && exit 1)' + || exit 1 + sources: *newline_sources + + # =========== + # DEVELOPMENT + # =========== + + dotenv:install: + internal: true + cmds: + - mkdir -p bin + - curl -o bin/env https://raw.githubusercontent.com/bashup/dotenv/master/dotenv + - chmod +x bin/env + status: + - test -f bin/env + + dotenv: + deps: [ dotenv:install ] + desc: fill .env file. + vars: + UID: + sh: id -u + GID: + sh: id -g + GO_VER: + sh: go mod edit -json | jq -r .Go + GOIMPORTS_LOCAL: + sh: go list -m | awk -F/ '{print $1}' + ENV_DIST: + sh: cat .env.dist|tr '\n' ' ' + GOPATH: + sh: go env GOPATH + cmds: + - bin/env set GROUP_ID={{.UID}} USER_ID={{.GID}} GO_VER={{.GO_VER}} GOPATH={{.GOPATH}} {{.ENV_DIST}} + sources: + - .env.dist + - Taskfile.yaml + generates: + - .env + + goverter: + desc: go run goverter + deps: [ dotenv ] + cmds: + - go run github.com/jmattheis/goverter/cmd/goverter@{{.GOVERTER_VER}} gen ./... + sources: + - ./**/*.go + + # ========= + # OPERATION + # ========= + + up: + desc: up all services. + cmds: + - task: postgres:up + - task: api:up + + down: + desc: docker compose down. + deps: [ dotenv ] + cmds: + - docker compose down -v --remove-orphans + + postgres:build: + internal: true + desc: docker compose build postgres. + deps: [ dotenv ] + cmds: + - docker compose build postgres + sources: + - db/**/* + - Dockerfile + - docker-compose.yml + + postgres:up: + desc: docker compose up postgres. + deps: [ dotenv, postgres:build ] + cmds: + - docker compose up -d --force-recreate postgres + + postgres:push: + desc: docker compose push postgres. + deps: [ dotenv, postgres:build ] + cmds: + - docker compose push postgres + + api:build: + internal: true + desc: docker compose build api image. + deps: [ dotenv ] + cmds: + - docker compose build api + sources: + - .env + - Dockerfile + - docker-compose.yml + + api:up: + desc: docker compose up api. + deps: [ dotenv, api:build ] + cmds: + - docker compose up -d --force-recreate api diff --git a/db/.gitkeep b/db/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6eb6368 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +services: + postgres: + build: + target: postgres + context: . + args: &args + AIR_VER: ${AIR_VER} + ALPINE_VER: ${ALPINE_VER} + GO_VER: ${GO_VER} + POSTGRES_VER: ${POSTGRES_VER} + image: harbor.grachevko.ru/grachevko/h-skills/postgres:dev + labels: + ru.grachevko.dhu: 'postgres.h-skills.local' + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + PGDATABASE: postgres + PGUSER: postgres + PGPASSWORD: postgres + volumes: + - type: tmpfs + target: /var/lib/postgresql/data + tmpfs: + size: 4294967296 + + api: + build: + target: golang + args: *args + command: air --build.cmd "go build -o ./tmp/api ./cmd/api/" --build.bin "./tmp/api" + labels: + ru.grachevko.dhu: 'api.h-skills.local' + environment: + GOPATH: ${GOPATH} + working_dir: /app + volumes: + - ./:/app + - ${GOPATH}:${GOPATH} + - ${HOME}/.cache/go-build:/.cache/go-build + user: ${USER_ID}:${GROUP_ID}