diff --git a/.github/MIGRATION_TOOL.md b/.github/MIGRATION_TOOL.md new file mode 100644 index 0000000..e885b47 --- /dev/null +++ b/.github/MIGRATION_TOOL.md @@ -0,0 +1,64 @@ +# Database Migration Tool + +## Обзор + +Создан отдельный инструмент `cmd/migrate` для управления миграциями базы данных, что обеспечивает лучшее разделение ответственности. + +## Использование + +### Применить миграции +```bash +go run ./cmd/migrate -direction=up +# или +make migrate-up +``` + +### Откатить миграции +```bash +go run ./cmd/migrate -direction=down +# или +make migrate-down +``` + +## Преимущества + +✅ **Чистая архитектура**: Логика миграций отделена от основного приложения +✅ **CI/CD совместимость**: Инструмент можно использовать в автоматизированных процессах +✅ **Простота использования**: Понятный CLI интерфейс +✅ **Переиспользование**: Использует ту же логику миграций из пакета `internal/migrate` + +## Изменения в CI/CD + +Workflows обновлены для использования нового инструмента: + +**Было:** +```yaml +go run ./cmd/server migrate up +``` + +**Стало:** +```yaml +go run ./cmd/migrate -direction=up +``` + +## Конфигурация + +Инструмент использует те же переменные окружения, что и основное приложение: +- `DB_HOST` +- `DB_PORT` +- `DB_USER` +- `DB_PASSWORD` +- `DB_NAME` +- `DB_SSL_MODE` + +## Рефакторинг пакета migrate + +Добавлены функции: +- `Down()` - для отката миграций +- `createMigrator()` - общая логика создания мигратора +- `logMigrationStatus()` - логирование статуса + +Это обеспечивает: +- Код без дублирования (DRY) +- Лучшую обработку ошибок +- Согласованное логирование diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ff2ee6e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ +## Описание изменений + +Краткое описание того, что было изменено в этом PR. + +## Тип изменений + +- [ ] Исправление ошибки (bug fix) +- [ ] Новая функциональность (feature) +- [ ] Критическое изменение (breaking change) +- [ ] Документация (documentation) +- [ ] Рефакторинг (refactoring) +- [ ] Улучшение производительности (performance) + +## Чек-лист + +- [ ] Код прошел локальное тестирование +- [ ] Код отформатирован (`make format`) +- [ ] Линтер не выдает ошибок (`make lint`) +- [ ] Все тесты проходят (`make test`) +- [ ] Добавлены тесты для новой функциональности +- [ ] Документация обновлена (если необходимо) + +## Связанные issues + +Укажите номера связанных issues (например: Closes #123) + +## Дополнительная информация + +Любая дополнительная информация, которая может быть полезна для ревьюера. diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..6898fac --- /dev/null +++ b/.github/README.md @@ -0,0 +1,55 @@ +# GitHub Actions Configuration + +Эта директория содержит конфигурацию GitHub Actions для автоматизации CI/CD процессов. + +## 📁 Структура + +``` +.github/ +├── workflows/ +│ └── pull-request.yml # Полное тестирование для PR +├── dependabot.yml # Автообновление зависимостей +├── PULL_REQUEST_TEMPLATE.md # Шаблон для PR +└── README.md # Этот файл +``` + +## 🚀 Workflows + +### Pull Request (`pull-request.yml`) +**Триггер**: Создание или обновление PR к любой ветке + +**Выполняет полное тестирование**: +- 🎨 Проверка форматирования (gofumpt, goimports) +- 🔍 Линтинг (golangci-lint) +- 🚀 Запуск миграций БД +- 🧪 Выполнение всех тестов +- 📊 Генерация отчета о покрытии +- 🔨 Сборка приложения и инструментов +- 🐳 Сборка Docker образа +- 🔒 Проверки безопасности + + +## 🔧 Dependabot + +Автоматически создает PR для обновления: +- Go модулей (еженедельно) +- Docker образов (еженедельно) +- GitHub Actions (еженедельно) + +## 📝 Pull Request Template + +Стандартный шаблон для PR включает: +- Описание изменений +- Тип изменений +- Чек-лист для разработчика +- Связанные issues + +## 🛠️ Локальная разработка + +Перед созданием PR выполните: + +```bash +make format # Форматирование +make lint # Линтинг +make test # Тесты +``` diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f2e94e8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 5 + reviewers: + - "aleksandr" + assignees: + - "aleksandr" + commit-message: + prefix: "deps" + include: "scope" + + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 3 + + - package-ecosystem: "github-actions" + directory: "/.github/workflows" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 3 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..a798e32 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,80 @@ +name: Pull Request CI + +on: + pull_request: + +permissions: + contents: read + pull-requests: read + +jobs: + ci: + runs-on: ubuntu-latest + env: + GOFLAGS: -buildvcs=false + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23.x' + check-latest: true + + - name: Cache Go build and mod + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Install tools + run: make install-tools + + - name: Clean and download deps + run: | + go clean -modcache + make deps + + - name: Cache golangci-lint + uses: actions/cache@v4 + with: + path: ~/.cache/golangci-lint + key: ${{ runner.os }}-golangci-lint-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-golangci-lint- + + - name: Format check (gofumpt) + run: make format-check + + - name: Imports check (goimports) + run: make imports-check + + - name: Go vet + run: go vet ./... + + - name: Lint + run: make lint + + - name: Build + run: make build + + - name: Vulnerability check + run: make security + + - name: Test with coverage + run: make test-coverage + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage + path: | + coverage.out + coverage.html + + diff --git a/.golangci.yml b/.golangci.yml index cddc79a..04d2d43 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,90 +1,10 @@ -run: - timeout: 5m - modules-download-mode: readonly - -linters-settings: - govet: - check-shadowing: true - gocyclo: - min-complexity: 15 - maligned: - suggest-new: true - dupl: - threshold: 100 - goconst: - min-len: 2 - min-occurrences: 2 - misspell: - locale: US - lll: - line-length: 140 - unused: - check-exported: false - unparam: - check-exported: false - nakedret: - max-func-lines: 30 - prealloc: - simple: true - range-loops: true - for-loops: false - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - disabled-checks: - - dupImport - - ifElseChain - - octalLiteral - - whyNoLint - - wrapperFunc - linters: enable: - - bodyclose - - deadcode - - depguard - - dogsled - - dupl - errcheck - - exportloopref - - exhaustive - - funlen - - gochecknoinits - - goconst - - gocritic - - gocyclo - - gofmt - - goimports - - golint - - gomnd - - goprintffuncname - - gosec - - gosimple - govet - ineffassign - - lll - - misspell - - nakedret - - noctx - - nolintlint - - rowserrcheck - staticcheck - - structcheck - - stylecheck - - typecheck - - unconvert - - unparam - unused - - varcheck - - whitespace -issues: - exclude-rules: - - path: _test\.go - linters: - - gomnd - - funlen +run: + timeout: 5m \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 61a65cb..0d0d1ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.25-alpine AS builder +FROM golang:1.24-alpine AS builder # Install git and ca-certificates RUN apk add --no-cache git ca-certificates diff --git a/Makefile b/Makefile index c1811ea..37f9771 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: run run-dev test lint format clean build db-up db-down db-reset docker-up docker-down docker-restart docker-logs +.PHONY: run run-dev test lint format format-check imports-check security clean build db-up db-down db-reset docker-up docker-down docker-restart docker-logs migrate-up migrate-down verify-go-version run: go run ./cmd/server @@ -50,6 +50,17 @@ format: gofumpt -l -w . goimports -w . +format-check: + @CHANGED=$(gofumpt -l .); \ + if [ -n "$$CHANGED" ]; then echo "$$CHANGED" && echo "Run make format" && exit 1; fi + +imports-check: + @CHANGED=$(goimports -l .); \ + if [ -n "$$CHANGED" ]; then echo "$$CHANGED" && echo "Run make format" && exit 1; fi + +security: + govulncheck ./... + docs: @echo "Generating API documentation..." swag init -g cmd/server/main.go @@ -102,7 +113,25 @@ deps: go mod download go mod tidy +verify-go-version: + @EXPECTED="1.23"; \ + ACTUAL=$$(awk '/^go [0-9]/ {print $$2}' go.mod); \ + if [ "$$ACTUAL" != "$$EXPECTED" ]; then \ + echo "go.mod go version is $$ACTUAL, expected $$EXPECTED"; \ + exit 1; \ + fi; \ + echo "go.mod go version OK: $$ACTUAL" + install-tools: - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - go install mvdan.cc/gofumpt@latest + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 + go install mvdan.cc/gofumpt@v0.7.0 go install golang.org/x/tools/cmd/goimports@latest + go install golang.org/x/vuln/cmd/govulncheck@latest + +migrate-up: + @echo "Running database migrations up..." + go run ./cmd/migrate -direction=up + +migrate-down: + @echo "Running database migrations down..." + go run ./cmd/migrate -direction=down diff --git a/README.md b/README.md index d70fe70..e7a55e9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Strive API +![Pull Request](https://github.com/aleksandr/strive-api/workflows/Pull%20Request/badge.svg) + A modern workout diary API built with Go, featuring user authentication, JWT tokens, and comprehensive testing. ## 🚀 Features @@ -12,10 +14,11 @@ A modern workout diary API built with Go, featuring user authentication, JWT tok - **Containerization**: Docker and Docker Compose support - **Structured Logging**: JSON/text logging with configurable levels - **Graceful Shutdown**: Proper server lifecycle management +- **CI/CD Pipeline**: Automated testing and deployment with GitHub Actions ## 📋 Requirements -- Go 1.22+ +- Go 1.23+ - PostgreSQL 15+ - Docker & Docker Compose (optional) - Make (optional, for convenience commands) @@ -182,23 +185,53 @@ make db-down # Reset database make db-reset + +# Run database migrations +make migrate-up + +# Rollback database migrations +make migrate-down ``` +## 🔄 CI/CD + +Проект использует GitHub Actions для автоматического тестирования: + +### Pull Request Workflow +Запускается при создании PR в любую ветку и выполняет: + +- 🎨 **Форматирование кода** - проверка gofumpt и goimports +- 🔍 **Линтинг** - golangci-lint проверки +- 🚀 **Миграции БД** - автоматический запуск миграций +- 🧪 **Тестирование** - полный набор unit тестов +- 📊 **Покрытие кода** - генерация отчетов покрытия +- 🔨 **Сборка** - проверка компиляции приложения и инструментов +- 🐳 **Docker** - сборка Docker образа +- 🔒 **Безопасность** - базовые проверки безопасности + +Подробная документация: [docs/CI_CD.md](docs/CI_CD.md) + ## 📁 Project Structure ``` strive-api/ -├── cmd/server/ # Application entry point +├── cmd/ +│ ├── server/ # Main application entry point +│ └── migrate/ # Database migration tool ├── internal/ │ ├── config/ # Configuration management │ ├── database/ # Database connection and health │ ├── http/ # HTTP handlers and middleware │ ├── logger/ # Structured logging -│ ├── migrate/ # Database migrations +│ ├── migrate/ # Database migration logic │ ├── models/ # Data models │ ├── repositories/ # Data access layer -│ └── services/ # Business logic -├── docs/ # Generated API documentation +│ ├── services/ # Business logic +│ └── validation/ # Input validation +├── .github/ +│ ├── workflows/ # GitHub Actions CI/CD +│ └── dependabot.yml # Dependency updates +├── docs/ # API documentation and guides ├── migrations/ # Database migration files ├── docker-compose.yml # Docker Compose configuration ├── Dockerfile # Docker image definition diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go new file mode 100644 index 0000000..733f929 --- /dev/null +++ b/cmd/migrate/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "flag" + "log" + "os" + + "github.com/aleksandr/strive-api/internal/config" + "github.com/aleksandr/strive-api/internal/logger" + "github.com/aleksandr/strive-api/internal/migrate" +) + +func main() { + var direction string + flag.StringVar(&direction, "direction", "up", "Migration direction: up or down") + flag.Parse() + + if direction != "up" && direction != "down" { + log.Fatalf("Invalid direction: %s. Use 'up' or 'down'", direction) + } + + cfg, err := config.Load() + if err != nil { + log.Fatalf("Failed to load config: %v", err) + } + + logger := logger.New(cfg.Log.Level, cfg.Log.Format) + logger.Info("Starting database migrations", "direction", direction) + + switch direction { + case "up": + if err := migrate.Run(cfg, logger); err != nil { + log.Fatalf("Failed to run up migrations: %v", err) + } + logger.Info("Up migrations completed successfully") + case "down": + if err := migrate.Down(cfg, logger); err != nil { + log.Fatalf("Failed to run down migrations: %v", err) + } + logger.Info("Down migrations completed successfully") + } + + os.Exit(0) +} diff --git a/cmd/server/main.go b/cmd/server/main.go index fd29303..64019ce 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -138,7 +138,7 @@ func setupProtectedRoutes(mux *http.ServeMux, authService services.AuthService, protectedMux.HandleFunc("/api/v1/user/profile", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"message":"This is a protected endpoint"}`)) + _, _ = w.Write([]byte(`{"message":"This is a protected endpoint"}`)) }) // Apply auth middleware to protected routes diff --git a/coverage.html b/coverage.html index 93856df..6893188 100644 --- a/coverage.html +++ b/coverage.html @@ -3,7 +3,7 @@ - server: Go Coverage Report + migrate: Go Coverage Report