Files
2026-08-31 19:31:51 +03:00

711 lines
35 KiB
Makefile
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BookFlow Metadata Engine Makefile
# Работает под Windows (PowerShell), Linux и macOS
APP_NAME := bookflow-metadata
BUILD_DIR := build
CMD_PATH := ./cmd/bookflow-metadata
GO := go
GOFLAGS := -trimpath
# Директория результатов (для clean-results)
OUTPUT_DIR := ./result
# ── Параметры Gitea для CI/CD-команд ─────────────────────────────────────────
# Переопределить через env или CLI: make release-tag GITEA_URL=https://...
GITEA_URL ?= https://github.dfv24.com
GITEA_REPO ?= fofanov.dmitry/bookflow-metadata
GIT_TOKEN ?=
GITEA_TOKEN ?=
# ============================================================================
# Определяем оболочку по платформе
# ============================================================================
ifeq ($(OS),Windows_NT)
SHELL = powershell.exe
.SHELLFLAGS = -NoProfile -ExecutionPolicy Bypass -Command
GIT_VERSION := $(shell try { git describe --tags --abbrev=0 2>$$null } catch { '' })
BUILD_DATE := $(shell Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')
GIT_COMMIT := $(shell try { git rev-parse --short HEAD 2>$$null } catch { 'unknown' })
GO_VERSION := $(shell try { (go env GOVERSION) -replace '^go','' } catch { 'unknown' })
else
SHELL := /bin/sh
GIT_VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null)
BUILD_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || echo unknown)
GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
GO_VERSION := $(shell go env GOVERSION 2>/dev/null | sed 's/^go//' || echo unknown)
endif
VERSION := $(if $(GIT_VERSION),$(patsubst v%,%,$(GIT_VERSION)),dev)
LDFLAGS := -s -w -X main.version=$(VERSION)
# ============================================================================
# Основные команды
# ============================================================================
.PHONY: all build run clean test lint deps tidy fmt vet help clean-results ensure-build-dir wiki-update clean-env
.PHONY: release release-prepare release-tag release-tag-delete ci-release ci-check release-local
## Сборка + тесты (цель по умолчанию при явном вызове make all)
all: build vet
# ============================================================================
# Вспомогательная цель: создать build/ если не существует
# ============================================================================
ifeq ($(OS),Windows_NT)
ensure-build-dir:
New-Item -ItemType Directory -Force '$(BUILD_DIR)' | Out-Null
else
ensure-build-dir:
@mkdir -p $(BUILD_DIR)
endif
## Сброс переменных кросс-компиляции (GOOS/GOARCH/GOARM/GOMIPS)
## Запускать после ручной кросс-компиляции в PowerShell: make clean-env
ifeq ($(OS),Windows_NT)
clean-env:
Remove-Item Env:GOOS,Env:GOARCH,Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; Write-Host '>>> Переменные кросс-компиляции сброшены (GOOS/GOARCH/GOARM/GOMIPS)'
else
clean-env:
@unset GOOS GOARCH GOARM GOMIPS; echo '>>> Переменные кросс-компиляции сброшены (GOOS/GOARCH/GOARM/GOMIPS)'
endif
## Сборка для текущей платформы
ifeq ($(OS),Windows_NT)
build: ensure-build-dir
$(info >>> Сборка $(APP_NAME) для текущей платформы...)
$$env:GOOS=''; $$env:GOARCH=''; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME).exe $(CMD_PATH)
$(info >>> Готово: $(BUILD_DIR)/$(APP_NAME).exe)
else
build: ensure-build-dir
$(info >>> Сборка $(APP_NAME) для текущей платформы...)
$(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME) $(CMD_PATH)
$(info >>> Готово: $(BUILD_DIR)/$(APP_NAME))
endif
## Сборка для Windows
ifeq ($(OS),Windows_NT)
build-windows-current:
$(info >>> Сборка $(APP_NAME).exe для Windows...)
$$env:GOOS='windows'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(APP_NAME).exe $(CMD_PATH)
$(info >>> Готово: $(APP_NAME).exe)
else
build-windows-current:
$(info >>> Сборка $(APP_NAME).exe для Windows...)
$(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(APP_NAME).exe $(CMD_PATH)
$(info >>> Готово: $(APP_NAME).exe)
endif
## Запуск без сборки (с параметрами по умолчанию)
ifeq ($(OS),Windows_NT)
run:
$(info >>> Запуск $(APP_NAME)...)
$(GO) run $(CMD_PATH) -workers 8 -timeout 60m
else
run:
$(info >>> Запуск $(APP_NAME)...)
$(GO) run $(CMD_PATH) -workers 8 -timeout 60m
endif
## Запуск с собственными параметрами (пример: make run-custom ARGS="-workers 4 -timeout 30")
ifeq ($(OS),Windows_NT)
run-custom:
$(info >>> Запуск с параметрами: $(ARGS)...)
$$env:GOOS=''; $$env:GOARCH=''; $(GO) run $(CMD_PATH) $(ARGS)
else
run-custom:
$(info >>> Запуск с параметрами: $(ARGS)...)
$(GO) run $(CMD_PATH) $(ARGS)
endif
## Запуск unit-тестов
ifeq ($(OS),Windows_NT)
test:
$(info >>> Запуск тестов...)
$$env:GOOS=''; $$env:GOARCH=''; $(GO) test ./... -v -race -count=1
else
test:
$(info >>> Запуск тестов...)
$(GO) test ./... -v -race -count=1
endif
## Запуск тестов с покрытием
ifeq ($(OS),Windows_NT)
test-cover:
$(info >>> Запуск тестов с покрытием...)
$$env:GOOS=''; $$env:GOARCH=''; $(GO) test ./... -v -race -coverprofile=coverage.out
$(GO) tool cover -html=coverage.out -o coverage.html
$(info >>> Отчёт: coverage.html)
else
test-cover:
$(info >>> Запуск тестов с покрытием...)
$(GO) test ./... -v -race -coverprofile=coverage.out
$(GO) tool cover -html=coverage.out -o coverage.html
$(info >>> Отчёт: coverage.html)
endif
## Установка зависимостей
deps:
$(info >>> Установка зависимостей...)
$(GO) mod download
## Очистка и обновление go.mod
tidy:
$(GO) mod tidy
## Форматирование кода
fmt:
$(info >>> Форматирование кода...)
$(GO) fmt ./...
## Статический анализ
vet:
$(info >>> Статический анализ...)
$(GO) vet ./...
## Линтер (требуется golangci-lint)
ifeq ($(OS),Windows_NT)
lint:
$(info >>> Линтинг...)
@if (Get-Command golangci-lint -ErrorAction SilentlyContinue) { golangci-lint run ./... } else { $(info golangci-lint не установлен: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest) }
else
lint:
$(info >>> Линтинг...)
@command -v golangci-lint >/dev/null 2>&1 && golangci-lint run ./... || echo "golangci-lint не установлен: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
endif
## Очистка артефактов сборки
ifeq ($(OS),Windows_NT)
clean:
$(info >>> Очистка артефактов сборки...)
-Remove-Item -Recurse -Force '$(BUILD_DIR)' -ErrorAction SilentlyContinue
-Remove-Item -Force '$(APP_NAME).exe' -ErrorAction SilentlyContinue
-Remove-Item -Force 'coverage.out','coverage.html' -ErrorAction SilentlyContinue
$(info >>> Готово)
else
clean:
$(info >>> Очистка артефактов сборки...)
@rm -rf $(BUILD_DIR) coverage.out coverage.html $(APP_NAME)
$(info >>> Готово)
endif
## Очистка результатов работы программы
ifeq ($(OS),Windows_NT)
clean-results:
$(info >>> Очистка директории результатов...)
-Remove-Item -Recurse -Force '$(OUTPUT_DIR)' -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force '$(OUTPUT_DIR)' | Out-Null
$(info >>> Готово)
else
clean-results:
$(info >>> Очистка директории результатов...)
@rm -rf $(OUTPUT_DIR) && mkdir -p $(OUTPUT_DIR)
$(info >>> Готово)
endif
## Полная очистка (артефакты + результаты)
clean-all: clean clean-results
# ============================================================================
# Кросс-компиляция: все платформы
# ============================================================================
.PHONY: build-all build-linux build-darwin build-windows build-arm build-freebsd build-openbsd build-netbsd
## Сборка под ВСЕ платформы
build-all: clean ensure-build-dir build-linux build-darwin build-windows build-arm build-freebsd build-openbsd build-netbsd
$(info )
$(info =========================================)
$(info Сборка завершена для всех платформ)
$(info =========================================)
$(info )
# ============================================================================
# Linux
# ============================================================================
build-linux: build-linux-amd64 build-linux-386 build-linux-arm64 build-linux-arm7
ifeq ($(OS),Windows_NT)
build-linux-amd64:
$(info >>> Linux amd64...)
$$env:GOOS='linux'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(CMD_PATH)
else
build-linux-amd64:
$(info >>> Linux amd64...)
GOOS=linux GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-linux-386:
$(info >>> Linux 386...)
$$env:GOOS='linux'; $$env:GOARCH='386'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-386 $(CMD_PATH)
else
build-linux-386:
$(info >>> Linux 386...)
GOOS=linux GOARCH=386 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-386 $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-linux-arm64:
$(info >>> Linux arm64...)
$$env:GOOS='linux'; $$env:GOARCH='arm64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-arm64 $(CMD_PATH)
else
build-linux-arm64:
$(info >>> Linux arm64...)
GOOS=linux GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-arm64 $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-linux-arm7:
$(info >>> Linux armv7...)
$$env:GOOS='linux'; $$env:GOARCH='arm'; $$env:GOARM='7'; Remove-Item Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-armv7 $(CMD_PATH)
else
build-linux-arm7:
$(info >>> Linux armv7...)
GOOS=linux GOARCH=arm GOARM=7 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-armv7 $(CMD_PATH)
endif
# ============================================================================
# macOS
# ============================================================================
build-darwin: build-darwin-amd64 build-darwin-arm64
ifeq ($(OS),Windows_NT)
build-darwin-amd64:
$(info >>> macOS amd64 (Intel)...)
$$env:GOOS='darwin'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 $(CMD_PATH)
else
build-darwin-amd64:
$(info >>> macOS amd64 (Intel)...)
GOOS=darwin GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-darwin-arm64:
$(info >>> macOS arm64 (Apple Silicon)...)
$$env:GOOS='darwin'; $$env:GOARCH='arm64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(CMD_PATH)
else
build-darwin-arm64:
$(info >>> macOS arm64 (Apple Silicon)...)
GOOS=darwin GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(CMD_PATH)
endif
# ============================================================================
# Windows
# ============================================================================
build-windows: build-windows-amd64 build-windows-386 build-windows-arm64
ifeq ($(OS),Windows_NT)
build-windows-amd64:
$(info >>> Windows amd64...)
$$env:GOOS='windows'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe $(CMD_PATH)
else
build-windows-amd64:
$(info >>> Windows amd64...)
GOOS=windows GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-windows-386:
$(info >>> Windows 386...)
$$env:GOOS='windows'; $$env:GOARCH='386'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-windows-386.exe $(CMD_PATH)
else
build-windows-386:
$(info >>> Windows 386...)
GOOS=windows GOARCH=386 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-windows-386.exe $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-windows-arm64:
$(info >>> Windows arm64...)
$$env:GOOS='windows'; $$env:GOARCH='arm64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-windows-arm64.exe $(CMD_PATH)
else
build-windows-arm64:
$(info >>> Windows arm64...)
GOOS=windows GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-windows-arm64.exe $(CMD_PATH)
endif
# ============================================================================
# ARM / Embedded
# ============================================================================
build-arm: build-arm-mips build-arm-mipsle build-arm-riscv64
ifeq ($(OS),Windows_NT)
build-arm-mips:
$(info >>> MIPS (big-endian)...)
$$env:GOOS='linux'; $$env:GOARCH='mips'; $$env:GOMIPS='softfloat'; Remove-Item Env:GOARM -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-mips $(CMD_PATH)
else
build-arm-mips:
$(info >>> MIPS (big-endian)...)
GOOS=linux GOARCH=mips GOMIPS=softfloat $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-mips $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-arm-mipsle:
$(info >>> MIPS (little-endian)...)
$$env:GOOS='linux'; $$env:GOARCH='mipsle'; $$env:GOMIPS='softfloat'; Remove-Item Env:GOARM -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-mipsle $(CMD_PATH)
else
build-arm-mipsle:
$(info >>> MIPS (little-endian)...)
GOOS=linux GOARCH=mipsle GOMIPS=softfloat $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-mipsle $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-arm-riscv64:
$(info >>> RISC-V 64...)
$$env:GOOS='linux'; $$env:GOARCH='riscv64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-linux-riscv64 $(CMD_PATH)
else
build-arm-riscv64:
$(info >>> RISC-V 64...)
GOOS=linux GOARCH=riscv64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-riscv64 $(CMD_PATH)
endif
# ============================================================================
# FreeBSD
# ============================================================================
build-freebsd: build-freebsd-amd64 build-freebsd-arm64
ifeq ($(OS),Windows_NT)
build-freebsd-amd64:
$(info >>> FreeBSD amd64...)
$$env:GOOS='freebsd'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-freebsd-amd64 $(CMD_PATH)
else
build-freebsd-amd64:
$(info >>> FreeBSD amd64...)
GOOS=freebsd GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-freebsd-amd64 $(CMD_PATH)
endif
ifeq ($(OS),Windows_NT)
build-freebsd-arm64:
$(info >>> FreeBSD arm64...)
$$env:GOOS='freebsd'; $$env:GOARCH='arm64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-freebsd-arm64 $(CMD_PATH)
else
build-freebsd-arm64:
$(info >>> FreeBSD arm64...)
GOOS=freebsd GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-freebsd-arm64 $(CMD_PATH)
endif
# ============================================================================
# OpenBSD
# ============================================================================
build-openbsd: build-openbsd-amd64
ifeq ($(OS),Windows_NT)
build-openbsd-amd64:
$(info >>> OpenBSD amd64...)
$$env:GOOS='openbsd'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-openbsd-amd64 $(CMD_PATH)
else
build-openbsd-amd64:
$(info >>> OpenBSD amd64...)
GOOS=openbsd GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-openbsd-amd64 $(CMD_PATH)
endif
# ============================================================================
# NetBSD
# ============================================================================
build-netbsd: build-netbsd-amd64
ifeq ($(OS),Windows_NT)
build-netbsd-amd64:
$(info >>> NetBSD amd64...)
$$env:GOOS='netbsd'; $$env:GOARCH='amd64'; Remove-Item Env:GOARM,Env:GOMIPS -ErrorAction SilentlyContinue; $(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BUILD_DIR)/$(APP_NAME)-netbsd-amd64 $(CMD_PATH)
else
build-netbsd-amd64:
$(info >>> NetBSD amd64...)
GOOS=netbsd GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-netbsd-amd64 $(CMD_PATH)
endif
# ============================================================================
# Релиз
# ============================================================================
.PHONY: release checksum
## Копирование бинарников в build/release/ (внутренняя цель)
ifeq ($(OS),Windows_NT)
release-prepare: build-all
$(info >>> Копирование бинарников в release/...)
New-Item -ItemType Directory -Force '$(BUILD_DIR)\release' | Out-Null
Get-ChildItem '$(BUILD_DIR)' -File | Where-Object { $$_.Name -ne '.env' } | Copy-Item -Destination '$(BUILD_DIR)\release\'
$(info >>> Готово: $(BUILD_DIR)/release/)
else
release-prepare: build-all
$(info >>> Копирование бинарников в release/...)
@mkdir -p $(BUILD_DIR)/release
@find $(BUILD_DIR) -maxdepth 1 -type f -not -name '.env' -exec cp {} $(BUILD_DIR)/release/ \;
$(info >>> Готово: $(BUILD_DIR)/release/)
endif
## Генерация контрольных сумм SHA256
ifeq ($(OS),Windows_NT)
checksum: release-prepare
$(info >>> Генерация SHA256...)
Remove-Item -ErrorAction SilentlyContinue '$(BUILD_DIR)\release\checksums-sha256.txt'; Get-ChildItem '$(BUILD_DIR)\release\' -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { "$$($$_.Hash) $$(Split-Path $$_.Path -Leaf)" } | Out-File -Encoding UTF8 '$(BUILD_DIR)\release\checksums-sha256.txt'
$(info >>> Готово: $(BUILD_DIR)/release/checksums-sha256.txt)
else
checksum: release-prepare
$(info >>> Генерация SHA256...)
@cd $(BUILD_DIR)/release && sha256sum $(APP_NAME)-* > checksums-sha256.txt
$(info >>> Готово: $(BUILD_DIR)/release/checksums-sha256.txt)
endif
# ============================================================================
# CI/CD — Управление релизами через Gitea
#
# Пайплайн описан в .gitea/workflows/release.yml
# Запускать ТОЛЬКО через команды make ниже — не вручную.
# ============================================================================
## Полный релиз: сборка + архивы + тег + создание Gitea Release + загрузка файлов
## Использование: make release VERSION=2.1.0 GIT_TOKEN=<токен>
## Если GIT_TOKEN не указан — соберёт локально и создаст тег, но не загрузит в Gitea
ifeq ($(OS),Windows_NT)
release:
@powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\release.ps1 -Version '$(VERSION)' -Token '$(or $(GIT_TOKEN),$(GITEA_TOKEN))' -AppName '$(APP_NAME)' -BuildDir '$(BUILD_DIR)' -GiteaUrl '$(GITEA_URL)' -GiteaRepo '$(GITEA_REPO)' -GoVersion '$(GO_VERSION)'
else
release:
@v="$(VERSION)"; tag="v$$v"; token="$(GIT_TOKEN)"; [ -z "$$token" ] && token="$(GITEA_TOKEN)"; \
if [ "$$v" = "dev" ] || [ -z "$$v" ]; then echo "!!! VERSION не указана. Использование: make release VERSION=X.Y.Z"; exit 1; fi; \
if [ -n "$$(git status --porcelain)" ]; then echo "!!! Есть незакоммиченные изменения."; git status --short; exit 1; fi; \
echo ""; \
echo "══════════════════════════════════════════════"; \
echo " Создание релиза $$tag"; \
echo "══════════════════════════════════════════════"; \
echo ""; \
echo ">>> [1/6] Кросс-компиляция..."; \
make build-all VERSION=$$v; \
echo ""; \
echo ">>> [2/6] Создание архивов..."; \
mkdir -p $(BUILD_DIR)/archives; \
cd $(BUILD_DIR) && for f in *; do \
[ -f "$$f" ] || continue; \
case "$$f" in \
*.exe) arcname="$${f%.exe}.zip"; echo " $$f → $$arcname"; zip "archives/$$arcname" "$$f" ;; \
$(APP_NAME)-*) arcname="$$f.tar.gz"; echo " $$f → $$arcname"; tar -czf "archives/$$arcname" "$$f" ;; \
esac; \
done; cd ..; \
echo ""; \
echo ">>> [3/6] SHA256..."; \
cd $(BUILD_DIR)/archives && sha256sum * > checksums-sha256.txt && cd ../..; \
echo ""; \
echo ">>> [4/6] Создание тега $$tag..."; \
if git tag -l "$$tag" | grep -q .; then echo " Тег $$tag уже существует, пропускаю"; \
else git tag -a "$$tag" -m "Release $$tag"; fi; \
git push origin "$$tag" 2>/dev/null || true; \
git push origin master 2>/dev/null || true; \
echo ""; \
if [ -z "$$token" ]; then \
echo "══════════════════════════════════════════════"; \
echo " Локальная сборка завершена."; \
echo " Для загрузки в Gitea укажите GIT_TOKEN:"; \
echo " make release VERSION=$$v GIT_TOKEN=<токен>"; \
echo " Архивы: $(BUILD_DIR)/archives/"; \
echo "══════════════════════════════════════════════"; \
exit 0; \
fi; \
echo ">>> [5/6] Создание релиза в Gitea..."; \
GITEA_URL="$(GITEA_URL)"; REPO="$(GITEA_REPO)"; \
COMMIT=$$(git rev-parse HEAD); \
DATE=$$(date -u '+%d.%m.%Y %H:%M UTC'); \
PREV_TAG=$$(git describe --tags --abbrev=0 "$$tag^" 2>/dev/null || echo ""); \
if [ -n "$$PREV_TAG" ]; then RAW_LOG=$$(git log --pretty=format:"%s" "$$PREV_TAG..$$tag" --no-merges 2>/dev/null); \
else RAW_LOG=$$(git log --pretty=format:"%s" --no-merges | head -50); fi; \
FEATS=""; FIXES=""; REFAC=""; OTHER=""; \
echo "$$RAW_LOG" | while IFS= read -r line; do \
[ -z "$$line" ] && continue; \
low=$$(echo "$$line" | tr '[:upper:]' '[:lower:]'); \
case "$$low" in \
функция:*|feat:*|feature:*|добавлен*|реализован*|новое:*) FEATS="$$FEATS\n- $$line" ;; \
исправлен*|fix:*|bugfix:*) FIXES="$$FIXES\n- $$line" ;; \
рефакторинг:*|refactor:*|оптимизац*|улучшен*) REFAC="$$REFAC\n- $$line" ;; \
*) OTHER="$$OTHER\n- $$line" ;; \
esac; \
done; \
BODY="## BookFlow Metadata Engine $$tag\n\n"; \
[ -f RELEASE_NOTES.md ] && BODY="$$BODY$$(cat RELEASE_NOTES.md)\n\n"; \
BODY="$$BODY---\n\n### Информация о сборке\n\n| Параметр | Значение |\n|---|---|\n| Коммит | \`$$COMMIT\` |\n| Дата | $$DATE |\n"; \
BODY_JSON=$$(printf '%s' "$$BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'); \
RELEASE_JSON=$$(curl -sf -X POST \
-H "Authorization: token $$token" \
-H "Content-Type: application/json" \
"$$GITEA_URL/api/v1/repos/$$REPO/releases" \
-d "{\"tag_name\":\"$$tag\",\"target_commitish\":\"$$COMMIT\",\"name\":\"$(APP_NAME) $$tag\",\"body\":$$BODY_JSON,\"draft\":false,\"prerelease\":false}"); \
RELEASE_ID=$$(echo "$$RELEASE_JSON" | python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null); \
if [ -z "$$RELEASE_ID" ] || [ "$$RELEASE_ID" = "None" ]; then echo "ОШИБКА: не удалось создать релиз!"; echo "$$RELEASE_JSON"; exit 1; fi; \
echo " Релиз создан: ID=$$RELEASE_ID"; \
echo ""; \
echo ">>> [6/6] Загрузка файлов..."; \
OK=0; FAIL=0; \
for FILE in $(BUILD_DIR)/archives/*; do \
[ -f "$$FILE" ] || continue; \
FNAME=$$(basename "$$FILE"); FSIZE=$$(du -sh "$$FILE" | cut -f1); \
printf " %-55s [%5s] " "$$FNAME" "$$FSIZE"; \
HTTP=$$(curl -s -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token $$token" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$$FILE" \
"$$GITEA_URL/api/v1/repos/$$REPO/releases/$$RELEASE_ID/assets?name=$$FNAME"); \
if [ "$$HTTP" -ge 200 ] && [ "$$HTTP" -lt 300 ]; then echo "OK"; OK=$$((OK+1)); \
else echo "ОШИБКА (HTTP $$HTTP)"; FAIL=$$((FAIL+1)); fi; \
done; \
echo ""; \
echo "══════════════════════════════════════════════"; \
echo " Загружено: $$OK | Ошибок: $$FAIL"; \
echo " Релиз: $$GITEA_URL/$$REPO/releases/tag/$$tag"; \
echo "══════════════════════════════════════════════"; \
[ "$$FAIL" -gt 0 ] && exit 1; true
endif
## Проверить git-статус перед созданием тега
ci-check:
$(info >>> Состояние рабочего дерева:)
git status --short
$(info >>> Последние коммиты:)
git log --oneline -5
$(info >>> Убедитесь что все изменения закоммичены перед релизом.)
## Создать git-тег v$(VERSION) и запушить в origin — запускает CI/CD автоматически
## Использование: make release-tag VERSION=2.1.0
release-tag:
ifeq ($(VERSION),2.0.0)
$(info !!! Версия не изменена ($(VERSION)). Укажите: make release-tag VERSION=X.Y.Z)
endif
$(info >>> Создание тега v$(VERSION)...)
git tag -a "v$(VERSION)" -m "Release v$(VERSION)"
git push origin "v$(VERSION)"
$(info >>> Тег v$(VERSION) запушен. CI/CD pipeline запущен автоматически в Gitea.)
$(info >>> Статус: $(GITEA_URL)/$(GITEA_REPO)/actions)
## Удалить ошибочный тег (локально и в origin)
## Использование: make release-tag-delete VERSION=2.1.0
release-tag-delete:
$(info >>> Удаление тега v$(VERSION)...)
-git tag -d "v$(VERSION)"
-git push origin --delete "v$(VERSION)"
$(info >>> Тег v$(VERSION) удалён.)
## Запустить CI/CD workflow вручную через Gitea API (без создания тега)
## Использование: make ci-release VERSION=2.1.0 GIT_TOKEN=<токен>
ifeq ($(OS),Windows_NT)
ci-release:
$(if $(or $(GIT_TOKEN),$(GITEA_TOKEN)),,$(error GIT_TOKEN (или GITEA_TOKEN) не задан. Используйте: make ci-release GIT_TOKEN=<токен>))
@Write-Host ">>> Запуск CI/CD workflow для версии $(VERSION)..."; Invoke-RestMethod -Method Post -Uri '$(GITEA_URL)/api/v1/repos/$(GITEA_REPO)/actions/workflows/release.yml/dispatches' -Headers @{ Authorization = 'token $(if $(GIT_TOKEN),$(GIT_TOKEN),$(GITEA_TOKEN))'; 'Content-Type' = 'application/json' } -Body '{"ref": "master", "inputs": {"version": "$(VERSION)"}}'; Write-Host ">>> Workflow запущен. Статус: $(GITEA_URL)/$(GITEA_REPO)/actions"
else
ci-release:
$(if $(or $(GIT_TOKEN),$(GITEA_TOKEN)),,$(error GIT_TOKEN (или GITEA_TOKEN) не задан. Используйте: make ci-release GIT_TOKEN=<токен>))
@echo ">>> Запуск CI/CD workflow для версии $(VERSION)..."
@curl -sf -X POST \
-H "Authorization: token $(if $(GIT_TOKEN),$(GIT_TOKEN),$(GITEA_TOKEN))" \
-H "Content-Type: application/json" \
"$(GITEA_URL)/api/v1/repos/$(GITEA_REPO)/actions/workflows/release.yml/dispatches" \
-d '{"ref": "master", "inputs": {"version": "$(VERSION)"}}' \
&& echo "Workflow запущен. Статус: $(GITEA_URL)/$(GITEA_REPO)/actions"
endif
## Полная локальная сборка + архивы + SHA256 (без загрузки в Gitea)
## Результат в build/release/
release-local: checksum
$(info >>> Локальный релиз готов в $(BUILD_DIR)/release/)
# ============================================================================
# Wiki — синхронизация локальной папки wiki/ в Gitea Wiki
# ============================================================================
## Синхронизировать папку wiki/ в удалённый wiki-репозиторий (<origin>.wiki.git)
ifeq ($(OS),Windows_NT)
wiki-update:
$(info >>> Синхронизация wiki в Gitea...)
@$$ErrorActionPreference = "Stop"; if (-Not (Test-Path "wiki")) { throw "wiki/ folder not found." }; $$origin = git remote get-url origin; if (-not $$origin) { throw "origin remote not found." }; if ($$origin -match "\.git$$") { $$wikiUrl = $$origin -replace "\.git$$", ".wiki.git" } else { $$wikiUrl = "$$origin.wiki.git" }; $$tmp = "$(BUILD_DIR)\\.wiki-sync"; Remove-Item -Recurse -Force $$tmp -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Force "$(BUILD_DIR)" | Out-Null; git clone --depth 1 $$wikiUrl $$tmp; robocopy wiki $$tmp /MIR /XD .git | Out-Null; if ($$LASTEXITCODE -gt 7) { throw "robocopy failed while syncing wiki." }; Push-Location $$tmp; git add -A; git diff --cached --quiet; if ($$LASTEXITCODE -eq 0) { Write-Host "No wiki changes."; Pop-Location; exit 0 }; git config user.name "wiki-sync-bot"; git config user.email "wiki-sync@local"; git commit -m "docs(wiki): sync from main repo"; git push origin HEAD; Pop-Location; Write-Host "Wiki synced successfully."
else
wiki-update:
$(info >>> Синхронизация wiki в Gitea...)
@test -d wiki || (echo "Папка wiki/ не найдена." && exit 1)
@ORIGIN=$$(git remote get-url origin); \
if [ -z "$$ORIGIN" ]; then echo "Не найден remote origin."; exit 1; fi; \
if echo "$$ORIGIN" | grep -q '\.git$$'; then WIKI_URL=$$(echo "$$ORIGIN" | sed 's/\.git$$/.wiki.git/'); else WIKI_URL="$$ORIGIN.wiki.git"; fi; \
TMP_DIR="$(BUILD_DIR)/.wiki-sync"; \
rm -rf "$$TMP_DIR"; mkdir -p "$(BUILD_DIR)"; \
git clone --depth 1 "$$WIKI_URL" "$$TMP_DIR"; \
rsync -a --delete --exclude '.git/' wiki/ "$$TMP_DIR"/; \
cd "$$TMP_DIR"; \
git add -A; \
if git diff --cached --quiet; then echo ">>> Изменений в wiki нет."; exit 0; fi; \
git config user.name "wiki-sync-bot"; \
git config user.email "wiki-sync@local"; \
git commit -m "docs(wiki): sync from main repo"; \
git push origin HEAD; \
echo ">>> Wiki успешно обновлена."
endif
# ============================================================================
# Справка
# ============================================================================
## Показать справку
help:
$(info )
$(info BookFlow Metadata Engine v$(VERSION) — Makefile)
$(info ─────────────────────────────────────────)
$(info )
$(info Основные:)
$(info make build Сборка для текущей платформы)
$(info make build-windows-current Сборка bookflow-metadata.exe)
$(info make run Запуск без сборки (workers=8, timeout=60m))
$(info make run-custom ARGS="…" Запуск с параметрами)
$(info make clean Очистка артефактов сборки)
$(info make clean-results Очистка директории результатов)
$(info make clean-all Полная очистка)
$(info )
$(info Тестирование:)
$(info make test Unit-тесты)
$(info make test-cover Тесты с покрытием кода)
$(info )
$(info Качество кода:)
$(info make fmt Форматирование (go fmt))
$(info make vet Статический анализ (go vet))
$(info make lint Линтер (golangci-lint))
$(info make tidy Обновление go.mod/go.sum)
$(info )
$(info Кросс-компиляция:)
$(info make build-all Все платформы (16 бинарников))
$(info make build-linux Linux: amd64, 386, arm64, armv7)
$(info make build-darwin macOS: amd64 (Intel), arm64 (Apple Silicon))
$(info make build-windows Windows: amd64, 386, arm64)
$(info make build-arm Embedded: MIPS, MIPSle, RISC-V 64)
$(info make build-freebsd FreeBSD: amd64, arm64)
$(info make build-openbsd OpenBSD: amd64)
$(info make build-netbsd NetBSD: amd64)
$(info )
$(info Релиз:)
$(info make release VERSION=X.Y.Z GIT_TOKEN=xxx Полный релиз: сборка + тег + публикация в Gitea)
$(info make release VERSION=X.Y.Z Только локальная сборка без загрузки)
$(info make release-tag-delete VERSION=X.Y.Z Удалить ошибочный тег)
$(info make ci-check Проверить git-статус перед релизом)
$(info )
$(info Дополнительно:)
$(info make release-local Все платформы + архивы + SHA256 → build/release/)
$(info make checksum Сборка + SHA256 контрольные суммы)
$(info )
$(info Wiki:)
$(info make wiki-update Синхронизировать локальную папку wiki/ в Gitea Wiki)
$(info )
.DEFAULT_GOAL := help