Files
glchat/Makefile
T
grendervill 3151327f6e feat(ops): offsite-бэкапы и проверки профилей
- deploy/backup.sh: BACKUP_OFFSITE_TARGET (rsync или rclone:remote:path)
  выгружает готовый архив после локальной ротации; неудача выгрузки только
  предупреждает. Ротация на приёмнике — опционально
  (BACKUP_OFFSITE_KEEP_DAYS, rclone delete --min-age). Имя архива получает
  суффикс, если бэкап запущен дважды в одну секунду.
- deploy/install.sh: offsite-настройки в .env (BACKUP_OFFSITE_TARGET,
  BACKUP_OFFSITE_KEEP_DAYS) отдельным разделом; детектор незаполненных
  плейсхолдеров учитывает цифры в именах (FAIL2BAN_ENABLED); dry-run больше не
  требует прав на /opt/glchat — явность GLCHAT_ETC_DIR фиксируется до
  подключения common.sh, иначе ветка временного каталога была недостижима.
- scripts/test-profiles.sh: сверка таблицы профилей 10.10 и одинакового набора
  PROFILE_* (46 проверок), make profiles-test / ops-test.
- scripts/test-backup.sh: бэкап и offsite на синтетическом инстансе — локальный
  rsync-приёмник, шим rclone, недоступный хост, нечисловой срок (12 проверок),
  make backup-test.
- scripts/test-install.sh: проверка offsite-ключей в .env после установки.
2026-09-22 18:44:50 +03:00

210 lines
8.2 KiB
Makefile

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 GOLANGCI_LINT_CACHE := $(CACHE_DIR)/golangci-lint
# Сети без доступа к storage.googleapis.com (например, домашний сервер) не могут
# скачать модули через proxy.golang.org; goproxy.io работает как fallback.
export GOPROXY ?= https://proxy.golang.org,direct
export GOSUMDB ?= sum.golang.org
export CGO_ENABLED ?= 1
# sqlite_fts5 включает полнотекстовый поиск в mattn/go-sqlite3 (AGENT.md 7.15):
# без тега поиск по сообщениям не работает, поэтому он обязателен для всех сборок.
GO_TAGS ?= sqlite_fts5
GOFLAGS_LOCAL := -trimpath -tags $(GO_TAGS)
GO_LDFLAGS := -s -w \
-X main.version=$(VERSION) \
-X main.commit=$(COMMIT) \
-X main.buildDate=$(BUILD_DATE)
# Инструмент берётся из .cache/bin; если он уже установлен в системе
# (PATH), повторная установка не нужна — это важно для CI и серверов,
# где сборка инструментов из исходников недоступна.
SYSTEM_TOOLS := golangci-lint gofumpt sqlc goose govulncheck 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: ## Установить dev-инструменты в .cache/bin (уже установленные в системе пропускаются)
@mkdir -p $(CACHE_DIR) $(BIN_DIR)
@for tool in $(SYSTEM_TOOLS); do \
if command -v $$tool >/dev/null 2>&1; then \
printf ' ok %-16s %s\n' "$$tool" "$$(command -v $$tool)"; \
else \
$(MAKE) --no-print-directory $(BIN_DIR)/$$tool || exit 1; \
fi; \
done
$(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.12.0
$(BIN_DIR)/sqlc: | $(CACHE_DIR)
@echo "==> install sqlc"
@GOBIN=$(BIN_DIR) go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.31.1
$(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@v1.1.4
$(BIN_DIR)/gitleaks: | $(CACHE_DIR)
@echo "==> install gitleaks"
@GOBIN=$(BIN_DIR) go install github.com/zricethezav/gitleaks/v8@v8.30.0
.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 -tags $(GO_TAGS) -race -count=1 $(GO_PACKAGES)
.PHONY: test-cover
test-cover: ## Тесты с отчётом покрытия (./build/coverage.out)
@mkdir -p $(BUILD_DIR)
go test -tags $(GO_TAGS) -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 "==> формат (gofumpt + golangci-lint fmt)"
@out=$$(gofumpt -l ./cmd ./internal ./scripts 2>/dev/null || true); \
if [ -n "$$out" ]; then printf '%s\n' "$$out"; echo "run: make fmt"; exit 1; fi
@out=$$(golangci-lint fmt --diff $(GO_PACKAGES)); \
if [ -n "$$out" ]; then printf '%s\n' "$$out"; 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 ./cmd ./internal
golangci-lint fmt $(GO_PACKAGES)
.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) \
--build-arg GOPROXY=$(GOPROXY) \
--build-arg GOSUMDB=$(GOSUMDB) \
-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: profiles-test
profiles-test: ## Проверить таблицу профилей производительности (AGENT.md 10.10)
bash scripts/test-profiles.sh
.PHONY: backup-test
backup-test: ## Проверить бэкап и offsite-выгрузку на синтетическом инстансе
bash scripts/test-backup.sh
.PHONY: ops-test
ops-test: profiles-test backup-test ## Проверки эксплуатационных скриптов
.PHONY: clean
clean: ## Удалить артефакты сборки
rm -rf $(BUILD_DIR)