feat(deploy): установщик, docker-скелет, шаблоны Caddy/LiveKit и CLI
- deploy/install.sh: идемпотентная установка, режимы встроенного и внешнего прокси (§10.13), --dry-run/--skip-tls/--reconfigure, auto-детект профиля (§10.10) - deploy/compose.yaml.tpl: app, livekit, caddy с лимитами, healthcheck, ротацией логов, cap_drop и no-new-privileges (§4.3) - deploy/caddy: Caddyfile.tpl (встроенный) и external-proxy.conf.tpl (наш стенд) - deploy/livekit: конфиг с TURN, dynacast и лимитами комнат (§10.6) - deploy/backup.sh + restore.sh: sqlite .backup, tar, age-шифрование (§10.8) - deploy/update.sh: бэкап → pull/сборка → up → миграции → smoke (§9.3) - deploy/smoke.sh: проверки §10.9 (healthz/readyz/meta, порты, UFW, fail2ban, бэкап) - deploy/security-check.sh: аудит SSH, UFW, fail2ban, прав .env и ключей - deploy/ufw-docker.sh, nftables и fail2ban-конфиги (sshd, glchat-auth, recidive) - deploy/glchat-cli.sh: команды §10.7, systemd-юнит glchat.service - scripts/test-install.sh: прогон установщика в чистом Ubuntu 24.04 (systemd-контейнер) - Dockerfile: multi-stage (node → golang CGO → debian-slim), non-root, healthcheck - Makefile: lint/test/build/dev/generate/security/smoke и фиксация версий инструментов
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
SHELL := /usr/bin/env bash
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
APP := glchat
|
||||
MODULE := glchat
|
||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none)
|
||||
BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
ROOT := $(CURDIR)
|
||||
CACHE_DIR := $(ROOT)/.cache
|
||||
BIN_DIR := $(CACHE_DIR)/bin
|
||||
BUILD_DIR := $(ROOT)/build
|
||||
export GOCACHE := $(CACHE_DIR)/go-build
|
||||
export GOMODCACHE:= $(CACHE_DIR)/go-mod
|
||||
export GOPATH := $(CACHE_DIR)/gopath
|
||||
export GOTOOLCHAIN := local
|
||||
export CGO_ENABLED ?= 1
|
||||
|
||||
GOFLAGS_LOCAL := -trimpath
|
||||
GO_LDFLAGS := -s -w \
|
||||
-X main.version=$(VERSION) \
|
||||
-X main.commit=$(COMMIT) \
|
||||
-X main.buildDate=$(BUILD_DATE)
|
||||
|
||||
TOOLS := $(BIN_DIR)/golangci-lint $(BIN_DIR)/gofumpt $(BIN_DIR)/sqlc $(BIN_DIR)/goose $(BIN_DIR)/govulncheck $(BIN_DIR)/gitleaks
|
||||
export PATH := $(BIN_DIR):$(PATH)
|
||||
|
||||
GO_PACKAGES := ./cmd/... ./internal/...
|
||||
GO_LINT_TIMEOUT ?= 5m
|
||||
|
||||
WEB_DIR := $(ROOT)/web
|
||||
DOCKER_IMAGE ?= glchat
|
||||
DOCKER_TAG ?= $(VERSION)
|
||||
SKIP_TLS ?= 0
|
||||
|
||||
.PHONY: help
|
||||
help: ## Показать список целей
|
||||
@printf 'glchat — доступные цели:\n\n'
|
||||
@grep -hE '^[a-zA-Z0-9_.-]+:.*?## ' $(MAKEFILE_LIST) | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-22s\033[0m %s\n", $$1, $$2}'
|
||||
@printf '\nПрофиль: %s | Кэш: %s\n' "$${PROFILE:-auto}" "$(CACHE_DIR)"
|
||||
|
||||
.PHONY: tools
|
||||
tools: $(TOOLS) ## Установить зафиксированные dev-инструменты в .cache/bin
|
||||
|
||||
$(CACHE_DIR):
|
||||
@mkdir -p $(CACHE_DIR) $(BIN_DIR)
|
||||
|
||||
$(BIN_DIR)/golangci-lint: | $(CACHE_DIR)
|
||||
@echo "==> install golangci-lint v2.13.2"
|
||||
@GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2
|
||||
|
||||
$(BIN_DIR)/gofumpt: | $(CACHE_DIR)
|
||||
@echo "==> install gofumpt"
|
||||
@GOBIN=$(BIN_DIR) go install mvdan.cc/gofumpt@v0.11.0
|
||||
|
||||
$(BIN_DIR)/sqlc: | $(CACHE_DIR)
|
||||
@echo "==> install sqlc"
|
||||
@GOBIN=$(BIN_DIR) go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.30.0
|
||||
|
||||
$(BIN_DIR)/goose: | $(CACHE_DIR)
|
||||
@echo "==> install goose"
|
||||
@GOBIN=$(BIN_DIR) go install github.com/pressly/goose/v3/cmd/goose@v3.28.0
|
||||
|
||||
$(BIN_DIR)/govulncheck: | $(CACHE_DIR)
|
||||
@echo "==> install govulncheck"
|
||||
@GOBIN=$(BIN_DIR) go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
||||
$(BIN_DIR)/gitleaks: | $(CACHE_DIR)
|
||||
@echo "==> install gitleaks"
|
||||
@GOBIN=$(BIN_DIR) go install github.com/zricethezav/gitleaks/v8@latest
|
||||
|
||||
.PHONY: dev
|
||||
dev: ## Запустить сервер локально (данные в ./build/dev-data)
|
||||
@mkdir -p $(BUILD_DIR)/dev-data/db
|
||||
DATA_DIR=$(BUILD_DIR)/dev-data \
|
||||
LISTEN_ADDR=127.0.0.1:8080 \
|
||||
DOMAIN=localhost \
|
||||
LOG_FORMAT=text \
|
||||
LOG_LEVEL=debug \
|
||||
go run ./cmd/glchat
|
||||
|
||||
.PHONY: build
|
||||
build: ## Собрать бинарник сервера в ./build/glchat
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
go build $(GOFLAGS_LOCAL) -ldflags '$(GO_LDFLAGS)' -o $(BUILD_DIR)/$(APP) ./cmd/glchat
|
||||
@ls -lh $(BUILD_DIR)/$(APP)
|
||||
|
||||
.PHONY: test
|
||||
test: ## Тесты Go с race-детектором
|
||||
go test -race -count=1 $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-cover
|
||||
test-cover: ## Тесты с отчётом покрытия (./build/coverage.out)
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
go test -race -count=1 -coverprofile=$(BUILD_DIR)/coverage.out $(GO_PACKAGES)
|
||||
go tool cover -func=$(BUILD_DIR)/coverage.out | tail -1
|
||||
|
||||
.PHONY: lint
|
||||
lint: tools go-lint web-lint ## Полный линт (Go + web)
|
||||
|
||||
.PHONY: go-lint
|
||||
go-lint: tools ## Линт Go: формат, vet, golangci-lint
|
||||
@echo "==> gofmt check"
|
||||
@unformatted=$$(gofumpt -l . | grep -v '^\.cache/' || true); \
|
||||
if [ -n "$$unformatted" ]; then echo "$$unformatted"; echo "run: make fmt"; exit 1; fi
|
||||
@echo "==> go vet"
|
||||
@go vet $(GO_PACKAGES)
|
||||
@echo "==> golangci-lint"
|
||||
@golangci-lint run --timeout $(GO_LINT_TIMEOUT) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: tools ## Форматировать Go-код
|
||||
gofumpt -w .
|
||||
|
||||
.PHONY: generate
|
||||
generate: tools ## Проверить сгенерированные артефакты (OpenAPI, sqlc — по мере появления)
|
||||
@echo "==> openapi: обслуживается из internal/server/openapi.go (/api/v1/openapi.json)"
|
||||
@test -s internal/server/openapi.go || { echo "openapi.go отсутствует"; exit 1; }
|
||||
@if [ -d queries ] && [ -n "$$(ls -A queries 2>/dev/null | grep -v '^.gitkeep$$')" ]; then \
|
||||
echo "==> sqlc generate"; sqlc generate; \
|
||||
else echo "==> sqlc: запросов пока нет, пропускаем"; fi
|
||||
|
||||
.PHONY: security
|
||||
security: tools ## AppSec: govulncheck, gitleaks, trivy (по возможности)
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@echo "==> govulncheck"
|
||||
@govulncheck ./cmd/... ./internal/... || { echo "govulncheck нашёл уязвимости"; exit 1; }
|
||||
@echo "==> gitleaks"
|
||||
@$(ROOT)/scripts/gitleaks-scan.sh
|
||||
@echo "==> trivy filesystem scan"
|
||||
@if command -v trivy >/dev/null 2>&1; then \
|
||||
trivy fs --quiet --scanners vuln,secret,misconfig --exit-code 1 \
|
||||
--skip-dirs .cache --skip-dirs .git --skip-dirs web/node_modules .; \
|
||||
else echo "trivy not installed — skipped"; fi
|
||||
|
||||
.PHONY: secrets-scan
|
||||
secrets-scan: tools ## Только проверка на утечки секретов
|
||||
@$(ROOT)/scripts/gitleaks-scan.sh
|
||||
|
||||
.PHONY: web-install
|
||||
web-install: ## Установить зависимости web-клиента
|
||||
cd $(WEB_DIR) && npm ci
|
||||
|
||||
.PHONY: web-lint
|
||||
web-lint: ## Линт и типизация web-клиента
|
||||
cd $(WEB_DIR) && npm run lint
|
||||
cd $(WEB_DIR) && npm run check
|
||||
|
||||
.PHONY: web-test
|
||||
web-test: ## Тесты web-клиента (vitest)
|
||||
cd $(WEB_DIR) && npm test
|
||||
|
||||
.PHONY: web-build
|
||||
web-build: ## Сборка web-клиента в web/dist
|
||||
cd $(WEB_DIR) && npm run build
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Собрать образ приложения
|
||||
docker build \
|
||||
--build-arg VERSION=$(VERSION) \
|
||||
--build-arg COMMIT=$(COMMIT) \
|
||||
--build-arg BUILD_DATE=$(BUILD_DATE) \
|
||||
-t $(DOCKER_IMAGE):$(DOCKER_TAG) .
|
||||
|
||||
.PHONY: install-dry-run
|
||||
install-dry-run: ## Проверить установщик без изменений системы
|
||||
bash deploy/install.sh --dry-run --domain gl.mhspx.su --email admin@gl.mhspx.su --yes
|
||||
|
||||
.PHONY: install-test
|
||||
install-test: ## Прогон install.sh в чистом контейнере Ubuntu 24.04
|
||||
bash scripts/test-install.sh
|
||||
|
||||
.PHONY: clean
|
||||
clean: ## Удалить артефакты сборки
|
||||
rm -rf $(BUILD_DIR)
|
||||
Reference in New Issue
Block a user