711 lines
35 KiB
Makefile
711 lines
35 KiB
Makefile
# 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
|