From e4a2fccef44e3c3bd713c19f466909a372bbdd6e Mon Sep 17 00:00:00 2001 From: Anton Kuznetcov Date: Mon, 28 Jul 2025 09:56:39 +0600 Subject: [PATCH] init local --- .gitea/workflows/build.yaml | 124 +++ .gitea/workflows/build_build.yaml | 51 + .gitignore | 28 + .vscode/settings.json | 3 + Dockerfile | 51 + Dockerfile.builder | 13 + LICENSE | 18 + README.md | 154 +++ build_image_for_runner.sh | 8 + docs/gitea-actions-guide.md | 1647 +++++++++++++++++++++++++++++ go.mod | 34 + go.sum | 89 ++ main.go | 87 ++ makefile | 25 + scripts/release-interactive.sh | 114 ++ scripts/release.sh | 76 ++ 16 files changed, 2522 insertions(+) create mode 100644 .gitea/workflows/build.yaml create mode 100644 .gitea/workflows/build_build.yaml create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Dockerfile create mode 100644 Dockerfile.builder create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build_image_for_runner.sh create mode 100644 docs/gitea-actions-guide.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 makefile create mode 100755 scripts/release-interactive.sh create mode 100755 scripts/release.sh diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..6bba95b --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,124 @@ +name: Release Build +on: + push: + tags: + - v* + +jobs: + create-docker-image: + runs-on: ubuntu-latest + container: + image: docker:28.3.2-dind + steps: + - name: Checkout repository + run: | + # Install git + apk add --no-cache git + + echo "=== GitHub Variables ===" + echo "github.ref = ${{ github.ref }}" + echo "github.ref_name = ${{ github.ref_name }}" + echo "github.sha = ${{ github.sha }}" + echo "github.repository = ${{ github.repository }}" + echo "========================" + git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea + cd hello_gitea + git checkout ${{ github.ref }} + + - name: Setup Docker Buildx + run: | + # Docker is already installed in docker:dind image + docker --version + + # Setup Docker Buildx for multi-platform builds + docker buildx create --use + docker buildx inspect --bootstrap + + - name: Login to Docker Hub + run: | + echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + + - name: Build multi-platform Docker images + run: | + cd hello_gitea + # Build multi-platform images using buildx + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:${{ github.ref_name }} \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:latest \ + --push \ + . + + create-release: + runs-on: ubuntu-latest + container: + # image: golang:1.21 + image: ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest + needs: create-docker-image + steps: + - name: Checkout repository + run: | + git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea + cd hello_gitea + git checkout ${{ github.ref }} + + - name: Setup Go + run: | + # Install jq for JSON parsing + apt-get update && apt-get install -y jq + git --version + go version + jq --version + + - name: Build all binaries + run: | + cd hello_gitea + mkdir -p bin + + # Build for all platforms + GOOS=linux GOARCH=amd64 go build -o bin/hello-api-linux-amd64 main.go + GOOS=linux GOARCH=arm64 go build -o bin/hello-api-linux-arm64 main.go + GOOS=windows GOARCH=amd64 go build -o bin/hello-api-windows-amd64.exe main.go + GOOS=darwin GOARCH=amd64 go build -o bin/hello-api-darwin-amd64 main.go + GOOS=darwin GOARCH=arm64 go build -o bin/hello-api-darwin-arm64 main.go + + # Create archives + cd bin + tar -czf hello-api-linux-amd64.tar.gz hello-api-linux-amd64 + tar -czf hello-api-linux-arm64.tar.gz hello-api-linux-arm64 + tar -czf hello-api-windows-amd64.tar.gz hello-api-windows-amd64.exe + tar -czf hello-api-darwin-amd64.tar.gz hello-api-darwin-amd64 + tar -czf hello-api-darwin-arm64.tar.gz hello-api-darwin-arm64 + + ls -la + + - name: Create Release + run: | + cd hello_gitea + # Create release using Gitea API + curl -X POST \ + -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + -H "Content-Type: application/json" \ + -d '{ + "tag_name": "${{ github.ref_name }}", + "name": "Release ${{ github.ref_name }}", + "body": "Automated release with multi-platform binaries and Docker image", + "draft": false, + "prerelease": false + }' \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases" + + # Upload assets + RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/tags/${{ github.ref_name }}" | \ + jq -r '.id') + + # Upload all binaries + for file in bin/*.tar.gz; do + echo "Uploading $file..." + curl -X POST \ + -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @$file \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)" + done diff --git a/.gitea/workflows/build_build.yaml b/.gitea/workflows/build_build.yaml new file mode 100644 index 0000000..e38ee3f --- /dev/null +++ b/.gitea/workflows/build_build.yaml @@ -0,0 +1,51 @@ +name: Release Build +on: + push: + tags: + - builder-* + +jobs: + create-builder-docker-image: + runs-on: ubuntu-latest + container: + image: docker:28.3.2-dind + steps: + - name: Checkout repository + run: | + # Install git + apk add --no-cache git + + echo "=== GitHub Variables ===" + echo "github.ref = ${{ github.ref }}" + echo "github.ref_name = ${{ github.ref_name }}" + echo "github.sha = ${{ github.sha }}" + echo "github.repository = ${{ github.repository }}" + echo "========================" + git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea + cd hello_gitea + git checkout ${{ github.ref }} + + - name: Setup Docker Buildx + run: | + # Docker is already installed in docker:dind image + docker --version + # Setup Docker Buildx for multi-platform builds + docker buildx create --use + docker buildx inspect --bootstrap + + - name: Login to Docker Hub + run: | + echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + + - name: Build multi-platform Docker images + run: | + cd hello_gitea + # Build multi-platform images using buildx + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:${{ github.ref_name }} \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest \ + --push \ + -f Dockerfile.builder \ + . + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeb2a1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# ---> Go +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum + +# env file +.env +bin/ +dockerhub_token \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..082b194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "makefile.configureOnOpen": false +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7e377d5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +# Build stage +FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder + +# Install git and ca-certificates +RUN apk --no-cache add git ca-certificates + +WORKDIR /app + +# Copy go mod files +COPY go.mod go.sum ./ + +# Download dependencies +RUN go mod download + +# Copy source code +COPY . . + +# Build the application for target platform +ARG TARGETPLATFORM +ARG BUILDPLATFORM +ARG TARGETOS +ARG TARGETARCH + +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -installsuffix cgo -o hello-api main.go + +# Final stage +FROM alpine:latest + +# Install jq and ca-certificates +RUN apk --no-cache add ca-certificates jq + +# Create non-root user +RUN addgroup -g 1001 -S appgroup && \ + adduser -u 1001 -S appuser -G appgroup + +WORKDIR /app + +# Copy binary from builder stage +COPY --from=builder /app/hello-api . + +# Change ownership to non-root user +RUN chown appuser:appgroup hello-api + +# Switch to non-root user +USER appuser + +# Expose port (if your app uses one) +EXPOSE 8080 + +# Run the application +CMD ["./hello-api"] \ No newline at end of file diff --git a/Dockerfile.builder b/Dockerfile.builder new file mode 100644 index 0000000..5bd8e4d --- /dev/null +++ b/Dockerfile.builder @@ -0,0 +1,13 @@ +# Используем образ Go с поддержкой мультиплатформенности +FROM golang:1.24 + +# Устанавливаем пакеты (одинаково работают на amd64/arm64) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + jq && \ + rm -rf /var/lib/apt/lists/* + +# (Опционально) Можно добавить команду по умолчанию +CMD ["bash"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3aad66d --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2025 GiteaAdmin + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..594bab1 --- /dev/null +++ b/README.md @@ -0,0 +1,154 @@ +# Hello Gitea API + +Простой REST API сервер, построенный на Go с использованием Gin framework. + +## 🚀 Возможности + +- ✅ REST API с JSON ответами +- ✅ Health check endpoint +- ✅ CORS поддержка +- ✅ Мультиплатформенная сборка +- ✅ Docker образы для Linux AMD64/ARM64 +- ✅ Автоматические релизы через Gitea Actions + +## 📦 Установка + +### Из бинарного файла + +1. Скачайте бинарный файл для вашей платформы из [релизов](https://direct-dev.ru/gitea/GiteaAdmin/hello_gitea/releases) +2. Распакуйте архив +3. Запустите: `./hello-api-` + +### Из Docker образа + +```bash +docker pull /hello-api:latest +docker run -p 8080:8080 /hello-api:latest +``` + +### Из исходного кода + +```bash +git clone https://direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git +cd hello_gitea +go mod download +go run main.go +``` + +## 🔧 Конфигурация + +Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения: + +```bash +export PORT=3000 +./hello-api +``` + +## 📡 API Endpoints + +### GET / +Основной endpoint + +**Ответ:** +```json +{ + "message": "Hello, World!", + "version": "1.0.0" +} +``` + +### GET /health +Health check endpoint + +**Ответ:** +```json +{ + "status": "ok", + "version": "1.0.0" +} +``` + +### GET /api/v1/info +Информация о сервисе + +**Ответ:** +```json +{ + "service": "hello-api", + "status": "running", + "version": "1.0.0" +} +``` + +### POST /api/v1/echo +Echo endpoint - возвращает отправленное сообщение + +**Запрос:** +```json +{ + "message": "Hello from client!" +} +``` + +**Ответ:** +```json +{ + "echo": "Hello from client!", + "version": "1.0.0" +} +``` + +## 🛠 Разработка + +### Зависимости + +- Go 1.21+ +- Gin framework + +### Сборка + +```bash +# Для текущей платформы +go build -o hello-api main.go + +# Для Linux AMD64 +GOOS=linux GOARCH=amd64 go build -o hello-api-linux-amd64 main.go + +# Для Linux ARM64 +GOOS=linux GOARCH=arm64 go build -o hello-api-linux-arm64 main.go + +# Для Windows +GOOS=windows GOARCH=amd64 go build -o hello-api-windows-amd64.exe main.go + +# Для macOS +GOOS=darwin GOARCH=amd64 go build -o hello-api-darwin-amd64 main.go +GOOS=darwin GOARCH=arm64 go build -o hello-api-darwin-arm64 main.go +``` + +### Тестирование + +```bash +# Запуск сервера +go run main.go + +# Тестирование API +curl http://localhost:8080/ +curl http://localhost:8080/health +curl http://localhost:8080/api/v1/info +curl -X POST http://localhost:8080/api/v1/echo \ + -H "Content-Type: application/json" \ + -d '{"message":"Hello!"}' +``` + +## 🚀 CI/CD + +При создании тега (например, `v1.1.0`) автоматически: + +1. Собираются бинарники для всех платформ +2. Создается Docker образ для Linux AMD64/ARM64 +3. Образ публикуется в Docker Hub +4. Создается релиз в Gitea с бинарниками + +## 📄 Лицензия + +MIT License \ No newline at end of file diff --git a/build_image_for_runner.sh b/build_image_for_runner.sh new file mode 100644 index 0000000..0a80c85 --- /dev/null +++ b/build_image_for_runner.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest \ + --push \ + -f Dockerfile_for_runner_image \ + . \ No newline at end of file diff --git a/docs/gitea-actions-guide.md b/docs/gitea-actions-guide.md new file mode 100644 index 0000000..9880665 --- /dev/null +++ b/docs/gitea-actions-guide.md @@ -0,0 +1,1647 @@ +# Настройка Gitea Actions для Go проекта: Полное руководство + +--- +**readTime:** 15-20 минут +**date:** 2025-07-27 18:00 +**author:** Direct-Dev(aka Антон Кузнецов) +**level:** Средний +**tags:** #gitea #gitea-actions #ci-cd #go #docker #devops #automation #k3s +**version:** 1.0.0 +--- + +## Содержание + +1. [Введение](#введение) +2. [Подготовка проекта](#подготовка-проекта) +3. [Настройка Gitea Actions](#настройка-gitea-actions) +4. [Создание workflow файла](#создание-workflow-файла) +5. [Настройка секретов](#настройка-секретов) +6. [Тестирование и запуск](#тестирование-и-запуск) +7. [Мониторинг и отладка](#мониторинг-и-отладка) +8. [Заключение](#заключение) +9. [Инфраструктура](#инфраструктура) +10. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s) +11. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере) + +## Введение + +Gitea Actions — это встроенная система непрерывной интеграции и развертывания (CI/CD) в Gitea, которая позволяет автоматизировать процессы сборки, тестирования и развертывания ваших проектов. Большой плюс этой системы в том, что она достаточна не требовательна к ресурсам и может быть развернута в собственном изолированном окружении. + +В этой статье мы рассмотрим пример работы с данной системой, на примере того как настроить Gitea Actions для Go проекта с автоматической сборкой мультиплатформенных бинарников, созданием Docker образов и публикацией релизов. + +### Итак, что мы будем делать + +- Настроим автоматическую сборку Go приложения для разных платформ +- Создадим Docker образы для Linux AMD64 и ARM64 +- Настроим публикацию в Docker Hub +- Автоматизируем создание релизов с бинарниками +- Настроим триггеры на основе Git тегов + +## Подготовка проекта + +### Структура проекта + +Собственно функциональная часть проекта Go не блещет оригинальностью и имеет следующую простую структуру: + +``` +hello_gitea/ +├── main.go # Основной код приложения +├── go.mod # Зависимости Go +├── go.sum # Хеши зависимостей +├── Dockerfile # Docker образ +├── .gitea/ # Конфигурация Gitea Actions + └── workflows/ + └── build.yaml # Workflow для сборки +├── README.md # Документация +└── .gitignore # Исключения Git +``` + +### Анализ кода + +Для упрощения востприятия не применяются сложные архитектурные паттерны и концепции - нам надо просто минимальное Go приложение. Тем не менее это работоспособный http api сервер, который можно расширить парой тройкой эндпойнтов и использовать в других проектах как заглушку или тестовый api. + +Основная задача статьи не в написании api сервера на Go, а в автоматизации процесса его сборки. + +Наше приложение — это простой REST API сервер на Go с использованием Gin framework: + +```go +package main + +import ( + "net/http" + "os" + "github.com/gin-gonic/gin" +) + +const version = "1.0.0" + +func main() { + gin.SetMode(gin.ReleaseMode) + r := gin.Default() + + // CORS middleware + r.Use(func(c *gin.Context) { + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + return + } + c.Next() + }) + + // Endpoints + r.GET("/health", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "status": "ok", + "version": version, + }) + }) + + r.GET("/", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "Hello, World!", + "version": version, + }) + }) + + // API group + api := r.Group("/api/v1") + { + api.GET("/info", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "service": "hello-api", + "version": version, + "status": "running", + }) + }) + + api.POST("/echo", func(c *gin.Context) { + var request struct { + Message string `json:"message"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Invalid JSON", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "echo": request.Message, + "version": version, + }) + }) + } + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + r.Run(":" + port) +} +``` + +### Dockerfile + +Помимо того, что мы будем собирать бинарники для разных платформ, мы также настроим сборку docker image в котором будем +запускать наш сервер api - это может быть полезным для развертывания нашего приложения, если мы настроим такое - например +через ArgoCD или flux (впрочем это тема отдельнйо статьи). + +Итак для контейнеризации используем многоэтапную сборку, что как вещают мудрецы является якобы полезным и правильным. +Не будем спорить поэтому вот: + +```dockerfile +# Build stage +FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder + +RUN apk --no-cache add git ca-certificates +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +ARG TARGETPLATFORM +ARG BUILDPLATFORM +ARG TARGETOS +ARG TARGETARCH + +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -installsuffix cgo -o hello-api main.go + +# Final stage +FROM alpine:latest + +RUN apk --no-cache add ca-certificates jq + +RUN addgroup -g 1001 -S appgroup && \ + adduser -u 1001 -S appuser -G appgroup + +WORKDIR /app +COPY --from=builder /app/hello-api . +RUN chown appuser:appgroup hello-api + +USER appuser +EXPOSE 8080 +CMD ["./hello-api"] +``` + +**Ремарка:** + +Тут хочется немного поразмышлять - по хорошему надо или разобраться в вариантах кэширования gitea или в качестве +базововго образа для каждого из этапов использовать предварительно собранный образ со всем необходимым. +При каждом запуске workflow будет запускаться новый контейнер docker in docker и там запускаться построение образа естественно кэшей не будет или надо как то придумывать как их хранить на ранере и пробрасывать в dind. Ну или руками сделать нужные образы и использоват их в Dockerfile. Правда пулиться они опять же будут походу каждый раз. Короче говоря тут есть чем поразбираться и пооптимизировать ... + + +## Настройка Gitea Actions + +### Включение Actions в Gitea + +1. **Проверьте версию Gitea** + + Gitea Actions доступны начиная с версии 1.17.0. Убедитесь, что ваш сервер Gitea поддерживает Actions. + +2. **Включите Actions в настройках репозитория** + + - Перейдите в настройки репозитория + - Найдите раздел "Actions" + - Включите "Enable Actions" + +### Создание токена доступа + +1. **Создайте токен для Actions** + + - Перейдите в настройки профиля → "Applications" + - Создайте новый токен с правами на репозиторий + - Скопируйте токен (он понадобится позже) + +## Создание workflow файла + +### Структура директории + +Создайте директорию `.gitea/workflows/` в корне вашего проекта: + +```bash +mkdir -p .gitea/workflows +``` + +### Основной workflow файл + +Создайте файл `.gitea/workflows/build.yaml`: + +```yaml +name: Release Build +on: + push: + tags: + - v* + +jobs: + create-docker-image: + runs-on: ubuntu-latest + container: + image: docker:28.3.2-dind + steps: + - name: Checkout repository + run: | + # Install git + apk add --no-cache git + + echo "=== GitHub Variables ===" + echo "github.ref = ${{ github.ref }}" + echo "github.ref_name = ${{ github.ref_name }}" + echo "github.sha = ${{ github.sha }}" + echo "github.repository = ${{ github.repository }}" + echo "========================" + git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea + cd hello_gitea + git checkout ${{ github.ref }} + + - name: Setup Docker Buildx + run: | + # Docker is already installed in docker:dind image + docker --version + + # Setup Docker Buildx for multi-platform builds + docker buildx create --use + docker buildx inspect --bootstrap + + - name: Login to Docker Hub + run: | + echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + + - name: Build multi-platform Docker images + run: | + cd hello_gitea + # Build multi-platform images using buildx + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:${{ github.ref_name }} \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:latest \ + --push \ + . + + create-release: + runs-on: ubuntu-latest + container: + image: golang:1.21 + needs: create-docker-image + steps: + - name: Checkout repository + run: | + git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea + cd hello_gitea + git checkout ${{ github.ref }} + + - name: Setup Go + run: | + # Install jq for JSON parsing + apt-get update && apt-get install -y jq + git --version + go version + jq --version + + - name: Build all binaries + run: | + cd hello_gitea + mkdir -p bin + + # Build for all platforms + GOOS=linux GOARCH=amd64 go build -o bin/hello-api-linux-amd64 main.go + GOOS=linux GOARCH=arm64 go build -o bin/hello-api-linux-arm64 main.go + GOOS=windows GOARCH=amd64 go build -o bin/hello-api-windows-amd64.exe main.go + GOOS=darwin GOARCH=amd64 go build -o bin/hello-api-darwin-amd64 main.go + GOOS=darwin GOARCH=arm64 go build -o bin/hello-api-darwin-arm64 main.go + + # Create archives + cd bin + tar -czf hello-api-linux-amd64.tar.gz hello-api-linux-amd64 + tar -czf hello-api-linux-arm64.tar.gz hello-api-linux-arm64 + tar -czf hello-api-windows-amd64.tar.gz hello-api-windows-amd64.exe + tar -czf hello-api-darwin-amd64.tar.gz hello-api-darwin-amd64 + tar -czf hello-api-darwin-arm64.tar.gz hello-api-darwin-arm64 + + ls -la + + - name: Create Release + run: | + cd hello_gitea + # Create release using Gitea API + curl -X POST \ + -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + -H "Content-Type: application/json" \ + -d '{ + "tag_name": "${{ github.ref_name }}", + "name": "Release ${{ github.ref_name }}", + "body": "Automated release with multi-platform binaries and Docker image", + "draft": false, + "prerelease": false + }' \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases" + + # Upload assets + RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/tags/${{ github.ref_name }}" | \ + jq -r '.id') + + # Upload all binaries + for file in bin/*.tar.gz; do + echo "Uploading $file..." + curl -X POST \ + -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @$file \ + "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)" + done +``` + +### Разберемся как работает workflow + +**Триггеры:** +- Workflow запускается при создании тега, начинающегося с `v*` (например, `v1.0.0`) + +**Jobs:** + +1. **create-docker-image:** - создание образов docker с нашим проектом + - Используем Docker-in-Docker контейнер (image: docker:28.3.2-dind) + - Настраиваем Docker Buildx для мультиплатформенной сборки (docker buildx create --use docker buildx inspect --bootstrap) + - Авторизуемч в Docker Hub (echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin) + - Собирает образы для Linux AMD64 и ARM64 (docker buildx build \ ...) + - Публикует образы с тегом версии и `latest` (--push) + +чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всей gitea в настройках или в настройках конкретного репозитория +я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ... + +2. **create-release:** + - Запускается после успешной сборки Docker образов + - Использует Go контейнер для сборки бинарников (image: golang:1.21) + - Собирает бинарники для всех платформ (Linux, Windows, macOS) + - Создает архивы с бинарниками + - Создает релиз через Gitea API + - Загружает бинарники как assets релиза + +при каждом запуске, как я понимаю, данного job будет скачиваться и устанавливаться в контейнере, созданном на базе golang:1.21 + +```yaml + - name: Setup Go + run: | + # Install jq for JSON parsing + apt-get update && apt-get install -y jq + git --version + go version + jq --version +``` + +решение как говорится в лоб: чтобы убрать эту работу при каждом выполнении workflow надо сделать свой образ + +``` Dockerfile +FROM golang:1.24 + +# Install some packages +RUN apt-get update && apt-get install -y git ca-certificates jq +``` + +собрать его и запушить (желательно с мультиплатформенностью). конечно если вам не надо билдить образы для разных платформ тогда все попроще + +```bash +#!/bin/bash + +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest \ + --push \ + -f Dockerfile_for_runner_image \ + . +``` + + +## Настройка секретов + +### Необходимые секреты + +В настройках репозитория (или инстанса) → "Secrets and variables" → "Actions" добавьте следующие секреты: + +1. **GITEATOKEN** + - Токен доступа к Gitea API + - Используется для клонирования репозитория и создания релизов + +2. **DOCKERHUB_USERNAME** + - Ваше имя пользователя в Docker Hub + - Используется для публикации образов + +3. **DOCKERHUB_TOKEN** + - Токен доступа к Docker Hub + - Используется для авторизации в Docker Hub + +### Создание токенов + +**Gitea Token:** +1. Перейдите в настройки профиля → "Applications" +2. Создайте новый токен с правами на репозиторий +3. Скопируйте токен +4. В разделе Actions репозитория создайте секрет уровня репозитория - я создал с именем GITEATOKEN (такой не даст сделать: GITEA_TOKEN) + +**Docker Hub Token:** +1. Войдите в Docker Hub +2. Перейдите в Account Settings → Security +3. Создайте новый Access Token +4. Скопируйте токен +5. В разделе Actions настроек инстанса gitea создайте секреты уровня инстанса (если много пользователей работает в gitea, то можно внести в уровень репозитория) я создал с именами DOCKERHUB_USERNAME, DOCKERHUB_TOKEN + +## Тестирование и запуск + +### Локальное тестирование + +1. **Проверим синтаксис workflow:** + +ну если мы в ide то наверное все автоматом отформатировано +но тем не менее ... + + ```bash + # Убедитесь, что YAML синтаксис корректен + yamllint .gitea/workflows/build.yaml + ``` + +2. **Протестируем сборку локально:** + ```bash + # Сборка для текущей платформы + go build -o hello-api main.go + + # Сборка для других платформ + GOOS=linux GOARCH=amd64 go build -o hello-api-linux-amd64 main.go + GOOS=linux GOARCH=arm64 go build -o hello-api-linux-arm64 main.go + ``` + +3. **Тестирование Docker образа:** + ```bash + # Сборка образа + docker build -t hello-api:test . + + # Запуск контейнера + docker run -p 8080:8080 hello-api:test + + # Тестирование API + curl http://localhost:8080/health + ``` + +### Запуск Actions + +1. **Создаем тег:** + ```bash + + git tag v1.1.20 + git push origin v1.1.20 + + # или скрипт + + + #!/bin/bash + + # Интерактивный скрипт для автоматизации релиза + # Использование: ./scripts/release-interactive.sh [version] + # Если версия не указана, скрипт запросит её интерактивно + + set -e # Остановить выполнение при ошибке + + # Функция для получения версии интерактивно + get_version_interactive() { + echo "🚀 Создание нового релиза" + echo "" + + # Показываем текущую версию + CURRENT_VERSION=$(grep 'const version = "' main.go | sed 's/const version = "\([^"]*\)"/\1/') + echo "📋 Текущая версия: $CURRENT_VERSION" + echo "" + + # Запрашиваем новую версию + read -p "Введите новую версию (формат X.Y.Z): " VERSION + + # Проверяем, что версия не пустая + if [ -z "$VERSION" ]; then + echo "❌ Версия не может быть пустой" + exit 1 + fi + + # Проверяем формат версии + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 + fi + + # Подтверждение + echo "" + echo "📝 Подтверждение:" + echo " Текущая версия: $CURRENT_VERSION" + echo " Новая версия: $VERSION" + echo "" + read -p "Продолжить? (y/N): " CONFIRM + + if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then + echo "❌ Релиз отменен" + exit 0 + fi + } + + # Проверяем, передана ли версия как аргумент + if [ $# -eq 0 ]; then + # Версия не указана, запрашиваем интерактивно + get_version_interactive + else + # Версия указана как аргумент + VERSION=$1 + + # Проверка формата версии + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Ошибка: Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 + fi + fi + + echo "🚀 Начинаем релиз версии v$VERSION..." + + # Проверяем, что мы в git репозитории + if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo "Ошибка: Не найден git репозиторий" + exit 1 + fi + + # Проверяем, что нет незакоммиченных изменений + # if ! git diff-index --quiet HEAD --; then + # echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их." + # exit 1 + # fi + + # Обновляем версию в main.go + echo "📝 Обновляем версию в main.go..." + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go + else + # Linux + sed -i "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go + fi + + # Проверяем, что изменение применилось + if ! grep -q "const version = \"$VERSION\"" main.go; then + echo "Ошибка: Не удалось обновить версию в main.go" + exit 1 + fi + + echo "✅ Версия обновлена в main.go" + + # Выполняем git команды + echo "📦 Добавляем изменения в git..." + git add . + + echo "💾 Создаем коммит..." + git commit -m "Release v$VERSION" + + echo "🏷️ Создаем тег..." + git tag -a "v$VERSION" -m "Release v$VERSION" + + echo "🚀 Отправляем изменения и теги..." + git push + git push --tags + + echo "🎉 Релиз v$VERSION успешно завершен!" + echo "📋 Выполненные действия:" + echo " - Обновлена версия в main.go" + echo " - Создан коммит с сообщением 'Release v$VERSION'" + echo " - Создан тег v$VERSION" + echo " - Изменения отправлены в удаленный репозиторий" + + ``` + +2. **Мониторинг выполнения:** + - Переходим в репозиторий → "Actions" + - Находим запущенный workflow + - Отслеживаем выполнение каждого job + +### Ожидаемый результат + +После успешного выполнения: + +1. **Docker образы** будут опубликованы в Docker Hub: + - `username/hello-api:v1.1.20` + - `username/hello-api:latest` + +2. **Релиз** будет создан в Gitea с бинарниками: + - `hello-api-linux-amd64.tar.gz` + - `hello-api-linux-arm64.tar.gz` + - `hello-api-windows-amd64.tar.gz` + - `hello-api-darwin-amd64.tar.gz` + - `hello-api-darwin-arm64.tar.gz` + +## Мониторинг и отладка + +### Просмотр логов + +1. **В Gitea:** + - Переходим в репозиторий → "Actions" + - Выберем workflow + - Нажмем на job для просмотра логов + +2. **Отладка ошибок:** + - Проверим правильность секретов + - Убедимся в корректности путей к репозиторию + - Проверим права доступа токенов + +### Частые проблемы + +1. **Ошибка авторизации в Docker Hub:** + - Проверим правильность `DOCKERHUB_TOKEN` + - Убедимся, что токен не истек + +2. **Ошибка создания релиза:** + - Проверим права токена `GITEATOKEN` + - Убедимся, что тег не существует + +3. **Ошибка сборки:** + - Проверим зависимости в `go.mod` + - Убедимся в корректности Dockerfile + + +## Заключение + +В этой статье мы рассмотрели полный процесс настройки Gitea Actions для Go проекта. Мы создали автоматизированный pipeline, который: + +- ✅ Собирает мультиплатформенные бинарники +- ✅ Создает Docker образы для разных архитектур +- ✅ Публикует образы в Docker Hub +- ✅ Создает релизы с бинарниками +- ✅ Запускается автоматически при создании тегов + +### Преимущества такого подхода + +1. **Автоматизация:** Минимизация ручной работы +2. **Консистентность:** Одинаковые условия сборки +3. **Мультиплатформенность:** Поддержка разных ОС и архитектур +4. **Безопасность:** Использование секретов для токенов +5. **Масштабируемость:** Легко добавить новые платформы или этапы + +### Следующие шаги + +1. тестирование в workflow +2. автоматическое развертывание +3. уведомления о результатах сборки +4. мониторинг и алерты + +Gitea Actions предоставляет мощные возможности для автоматизации процессов разработки, и с правильной настройкой вы можете значительно упростить процесс доставки вашего ПО. + +--- + +## Инфраструктура + +### Архитектура системы + +Этот и последующие разделы описывают инфраструктуру на которой я проверял все описанное выше. +Если у вас используются другие подходы, то можете пропустить их или ознакомиться для общего развития. + +Итак инфраструктура состоит из следующих компонентов: + +``` +Proxmox VE (ARM64) +├── K3s Cluster + ├── Master Node + └── Gitea Server (Helm Chart) + └── Worker Nodes +└── LXC Containers + └── Gitea Runner Container + ├── Docker Engine + ├── Go Toolchain + └── Build Tools +``` + +### Требования к системе + +**Proxmox VE:** +- ARM64 архитектура +- Минимум 8GB RAM +- 100GB свободного места +- Поддержка LXC контейнеров + +**K3s Cluster:** +- Kubernetes 1.24+ +- Helm 3.8+ +- Ingress Controller (Traefik) +- Persistent Storage (Longhorn) + +**LXC Container:** +- Ubuntu 22.04 LTS +- 4GB RAM +- 20GB дискового пространства +- Docker Engine +- Go 1.21+ + +## Установка Gitea в кластере K3s + +### Анализ существующего кластера + +Ваш кластер уже настроен и работает. Вот текущая конфигурация: + +**Узлы кластера:** +```bash +kubectl get nodes -o wide +NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME +k3s-control-01 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.2 10.xx.x.2 Ubuntu 20.04.6 LTS 5.10.160-rockchip-rk3588 containerd://2.0.5-k3s1 +k3s-control-02 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.3 10.xx.x.3 Ubuntu 20.04.6 LTS 5.10.160-rockchip-rk3588 containerd://2.0.5-k3s1 +k3s-control-03 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.4 10.xx.x.4 Ubuntu 20.04.6 LTS 6.1.31-sun50iw9 containerd://2.0.5-k3s1 +``` + +**Существующие компоненты:** +- Kubernetes v1.33.2+k3s1 +- 3 узла control-plane с etcd +- Traefik Ingress Controller +- NFS Storage Class для persistent storage +- Cert-Manager для SSL сертификатов +- Gitea уже установлен и работает + +### Проверка существующей установки Gitea + +```bash +# Проверка подов Gitea +kubectl get pods -n gitea +NAME READY STATUS RESTARTS AGE +gitea-798c56b58f-bsp2h 1/1 Running 0 30h +gitea-postgres-0 2/2 Running 10 (10d ago) 357d +gitea-valkey-cluster-0 1/1 Running 0 30h +gitea-valkey-cluster-1 1/1 Running 0 30h +gitea-valkey-cluster-2 1/1 Running 0 30h + +# Проверка сервисов +kubectl get svc -n gitea +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +gitea-http ClusterIP None 3000/TCP 543d +gitea-postgres ClusterIP 10.xx.xx.xx 5432/TCP 543d +gitea-ssh ClusterIP None 22/TCP 543d +gitea-valkey-cluster ClusterIP 10.xx.xx.xx 6379/TCP 30h +gitea-valkey-cluster-headless ClusterIP None 6379/TCP,16379/TCP 30h + +# Проверка persistent storage +kubectl get pvc -n gitea +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE +gitea-shared-storage Bound pvc-c869aea8-7b49-4b40-9b86-1a7fbe9b2ce8 10Gi RWO nfs 543d +pgdata-gitea-postgres-0 Bound pvc-c3405a46-594d-401f-91ae-e7398b3c5cc3 15Gi RWO nfs 543d +valkey-data-gitea-valkey-cluster-0 Bound pvc-a927baf5-b8f8-4c0b-b12f-68fbc63162d9 8Gi RWO nfs 30h +valkey-data-gitea-valkey-cluster-1 Bound pvc-b43a399b-fdf4-4c27-baf4-7056ecc4143b 8Gi RWO nfs 30h +valkey-data-gitea-valkey-cluster-2 Bound pvc-368f0ab9-7851-423f-afb9-443287c0c728 8Gi RWO nfs 30h +``` + +### Анализ конфигурации + +**Gitea версия:** 1.24.3 (rootless) +**База данных:** PostgreSQL с persistent storage (15Gi) +**Кэш:** Valkey cluster (Redis) с 3 репликами +**Storage:** NFS Storage Class +**Ingress:** Traefik + +### Настройка доступа к Gitea + +Доступ к gitea лучше сделать через ingress или на худой конец через port-forward: + +1. **Временный доступ через port-forward:** + +```bash +# Доступ к Gitea через port-forward +kubectl port-forward -n gitea svc/gitea-http 3000:3000 + +# В другом терминале проверьте доступ +curl http://localhost:3000 +``` + +2. **Создание IngressRoute для постоянного доступа:** + +```yaml +# gitea-ingressroute.yaml +# image: docker.io/traefik:v3.0.0 поэтому используется абстракция +# apiVersion: traefik.io/v1alpha1 +# kind: IngressRoute + +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + annotations: + cert-manager.io/cluster-issuer: letsencrypt-dns-cloudflare + creationTimestamp: "2024-05-24T03:50:33Z" + name: gitea-https-route + namespace: gitea +spec: + entryPoints: + - websecure + routes: + - kind: Rule + match: Host(`direct-dev.ru`) && PathPrefix(`/gitea`) + middlewares: + - name: strip-gitea-prefix + services: + - name: gitea-http + port: 3000 + tls: + secretName: le-root-direct-dev-ru +``` + +```bash +# Применение ingressroute +KUBECONFIG=~/.kube/config_hlab kubectl apply -f gitea-ingress.yaml +``` + +Таким образом мой инстанс gitea доступен с внешнего адреса https://direct-dev.ru/gitea + +в /data/gitea/conf/app.ini + +[server] +ROOT_URL = https://direct-dev.ru/gitea +DOMAIN = direct-dev.ru + +### Конфигурация Gitea + +Gitea установлен через Helm. + +**Версия:** Gitea 1.24.3 (rootless) +**Архитектура:** +- PostgreSQL для базы данных (развернут отдельно ) +- Valkey cluster (Redis) для кэширования +- NFS Storage Class для persistent storage + +```bash +helm repo add gitea-charts https://dl.gitea.io/charts/ +helm repo update + +kubectl create namespace gitea + +helm install gitea gitea-charts/gitea -n gitea +``` + +установка с кастомными значениями параметров + +``` bash +helm show values gitea-charts/gitea > gitea-values.yaml +#Отредактируйте файл (например, database или service). +helm install gitea gitea-charts/gitea -n gitea -f gitea-values.yaml +``` + +Обновление + +```bash +helm repo update +helm upgrade gitea gitea-charts/gitea -n gitea -f gitea-values.yaml +``` + + +**Проверка конфигурации:** + +```bash +# Проверка Helm релиза +helm list -n gitea + +# Просмотр конфигурации +helm get values gitea -n gitea +``` + +```yaml +USER-SUPPLIED VALUES: +gitea: + admin: + existingSecret: xxxxxx + config: + cache: + ADAPTER: memory + database: + DB_TYPE: postgres + HOST: gitea-postgres.gitea.svc.cluster.local:5432 + NAME: gitea + PASSWD: xxxxxxx + SCHEMA: gitea + USER: xxxx + indexer: + ISSUE_INDEXER_TYPE: bleve + REPO_INDEXER_ENABLED: true + queue: + TYPE: level + server: + APP_DATA_PATH: /data + DOMAIN: direct-dev.ru + ENABLE_PPROF: false + HTTP_PORT: 3000 + PROTOCOL: http + ROOT_URL: https://direct-dev.ru/gitea + SSH_DOMAIN: git.k3s-cluster-01.direct-dev.ru + SSH_LISTEN_PORT: 2222 + SSH_PORT: 22 + service: + DISABLE_REGISTRATION: true + SHOW_REGISTRATION_BUTTON: false + session: + PROVIDER: db +persistence: + enabled: true + storageClass: nfs +postgresql: + enabled: false +postgresql-ha: + enabled: false +redis-cluster: + enabled: false +``` + +``` bash +# Проверка секретов +kubectl get secrets -n gitea +``` + +### Настройка Actions в существующем Gitea + +1. **Проверка включения Actions:** + +```bash +# Проверка конфигурации Actions +kubectl exec -n gitea deployment/gitea -c gitea -- cat /data/gitea/conf/app.ini" +``` + +2. **Включение Actions через веб-интерфейс:** + - Откройте Gitea через port-forward или ingress + - Перейдите в Site Administration → Actions + - Включите "Enable Actions" + - Настройте "Default Actions URL" (например, https://gitea.com) + +3. **Проверка работы Actions:** + +```bash +# Проверка логов Gitea на предмет ошибок Actions +kubectl logs -n gitea deployment/gitea | grep -i action +``` + +### Настройка DNS и SSL + +Cert-Manager установлен. Инструкций вагон и маленькая тележка - не смысла повторяться. Проверим конфигурацию: + +```bash +# Проверка Cert-Manager +kubectl get pods -n cert-manager + +# Проверка ClusterIssuer +kubectl get clusterissuer +``` + +1. **Настройка DNS записи:** + +```bash +# Добавить A-запись в DNS (у меня сloudflare) +gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ... чтобы в итоге запрос поступил на traefik +``` + +2. **Создание ClusterIssuer (если не существует):** + +```bash +# Создание ClusterIssuer для Let's Encrypt - лучше через dns resolver +cat < /dev/null + +# Установка Docker +apt update +apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + +# Добавление пользователя в группу docker +usermod -aG docker $USER + +# Запуск Docker +systemctl enable docker +systemctl start docker + +# Проверка установки +docker --version +``` + +3. **Установка Go:** + +```bash +# Скачивание Go для ARM64 +wget https://go.dev/dl/go1.21.0.linux-arm64.tar.gz + +# Распаковка +tar -C /usr/local -xzf go1.21.0.linux-arm64.tar.gz + +# Настройка переменных окружения +echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc +echo 'export GOPATH=$HOME/go' >> ~/.bashrc +echo 'export GOROOT=/usr/local/go' >> ~/.bashrc +source ~/.bashrc + +# Проверка установки +go version +# Должно показать: go version go1.21.0 linux/arm64 + +# Создание рабочей директории +mkdir -p $HOME/go/{bin,src,pkg} +``` + +4. **Установка дополнительных инструментов:** + +```bash +# Установка build tools +apt install -y build-essential + +# Установка jq для JSON обработки +apt install -y jq + +# Установка yamllint для проверки YAML +apt install -y yamllint + +# Установка Docker Buildx +docker buildx version +``` + +### Установка Gitea Runner + +1. **Скачивание Gitea Runner:** + +```bash +# Определение архитектуры +ARCH=$(uname -m) +case $ARCH in + x86_64) ARCH=amd64 ;; + aarch64) ARCH=arm64 ;; + armv7l) ARCH=armv7 ;; +esac + +# Скачивание последней версии +# visit https://dl.gitea.com/act_runner/ copy link to version and arch you need + +wget https://dl.gitea.com/act_runner/0.2.12/act_runner-0.2.12-linux-arm64 + +# Переименование и установка +mv act_runner-0.2.12-linux-arm64 /usr/local/bin/act_runner +chmod +x /usr/local/bin/act_runner + +# Проверка установки +act_runner --version +``` + +2. **Создание пользователя для runner:** + +```bash +# Создание пользователя +useradd -m -s /bin/bash gitea-runner +usermod -aG docker gitea-runner + +# Создание директории для конфигурации +mkdir -p /opt/gitea-runner +chown gitea-runner:gitea-runner /opt/gitea-runner +``` + +3. **Настройка конфигурации runner:** + +```bash +# Переключение на пользователя runner +su - gitea-runner + +# Создание конфигурационного файла +cat > /opt/gitea-runner/config.yaml < config.yaml` to generate a config file. + +log: + # The level of logging, can be trace, debug, info, warn, error, fatal + level: info + +runner: + # Where to store the registration result. + file: .runner + # Execute how many tasks concurrently at the same time. + capacity: 1 + # Extra environment variables to run jobs. + envs: + A_TEST_ENV_NAME_1: a_test_env_value_1 + A_TEST_ENV_NAME_2: a_test_env_value_2 + + # Extra environment variables to run jobs from a file. + # It will be ignored if it's empty or the file doesn't exist. + env_file: .env + + # The timeout for a job to be finished. + # Please note that the Gitea instance also has a timeout (3h by default) for the job. + # So the job could be stopped by the Gitea instance if it's timeout is shorter than this. + timeout: 3h + + # The timeout for the runner to wait for running jobs to finish when shutting down. + # Any running jobs that haven't finished after this timeout will be cancelled. + shutdown_timeout: 0s + + # Whether skip verifying the TLS certificate of the Gitea instance. + insecure: true + + # The timeout for fetching the job from the Gitea instance. + fetch_timeout: 5s + + # The interval for fetching the job from the Gitea instance. + fetch_interval: 2s + + # The labels of a runner are used to determine which jobs the runner can run, and how to run them. + # Like: "macos-arm64:host" or "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" + # Find more images provided by Gitea at https://gitea.com/docker.gitea.com/runner-images . + # If it's empty when registering, it will ask for inputting labels. + # If it's empty when execute `daemon`, will use labels in `.runner` file. + labels: + - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" + - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" + - "ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04" + +cache: + # Enable cache server to use actions/cache. + enabled: true + # The directory to store the cache data. + # If it's empty, the cache data will be stored in $HOME/.cache/actcache. + dir: "" + # The host of the cache server. + # It's not for the address to listen, but the address to connect from job containers. + # So 0.0.0.0 is a bad choice, leave it empty to detect automatically. + host: "" + # The port of the cache server. + # 0 means to use a random available port. + port: 0 + # The external cache server URL. Valid only when enable is true. + # If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself. + # The URL should generally end with "/". + external_server: "" + +container: + # Specifies the network to which the container will connect. + # Could be host, bridge or the name of a custom network. + # If it's empty, act_runner will create a network automatically. + network: "" + # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker). + privileged: false + # And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway). + options: + # The parent directory of a job's working directory. + # NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically. + # If the path starts with '/', the '/' will be trimmed. + # For example, if the parent directory is /path/to/my/dir, workdir_parent should be path/to/my/dir + # If it's empty, /workspace will be used. + workdir_parent: + # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob + # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted. + # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to: + # valid_volumes: + # - data + # - /src/*.json + # If you want to allow any volume, please use the following configuration: + # valid_volumes: + # - '**' + valid_volumes: [] + # overrides the docker client host with the specified one. + # If it's empty, act_runner will find an available docker host automatically. + # If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers. + # If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work. + docker_host: "" + # Pull docker image(s) even if already present + force_pull: true + # Rebuild docker image(s) even if already present + force_rebuild: false + +host: + # The parent directory of a job's working directory. + # If it's empty, $HOME/.cache/act/ will be used. + workdir_parent: +EOF +``` + +### Регистрация Runner в Gitea + +1. **Получение токена регистрации:** + +- Войдите в веб-интерфейс Gitea +- Перейдите в Settings → Actions → Runners +- Нажмите "New Runner" +- Скопируйте токен регистрации + +2. **Регистрация runner:** + +```bash +# В контейнере LXC под пользователем gitea-runner +act_runner register \ + --instance https://gitea.your-domain.com \ + --token YOUR_REGISTRATION_TOKEN \ + --name "k3s-runner-arm64" \ + --labels "ubuntu-latest:docker://node:18,ubuntu-22.04:docker://node:18,self-hosted:docker://golang:1.21,arm64:docker://golang:1.21" \ + --no-interactive + +``` + +3. **Создание systemd сервиса:** + +```bash +# Создание файла сервиса +cat > /etc/systemd/system/gitea-runner.service < /opt/gitea-runner/monitor.sh <<'EOF' +#!/bin/bash + +LOG_FILE="/opt/gitea-runner/logs/monitor.log" +RUNNER_STATUS=$(systemctl is-active gitea-runner) + +echo "$(date): Runner status: $RUNNER_STATUS" >> $LOG_FILE + +if [ "$RUNNER_STATUS" != "active" ]; then + echo "$(date): Restarting gitea-runner service" >> $LOG_FILE + systemctl restart gitea-runner +fi + +# Проверка свободного места +DISK_USAGE=$(df /opt/gitea-runner | tail -1 | awk '{print $5}' | sed 's/%//') +if [ $DISK_USAGE -gt 80 ]; then + echo "$(date): High disk usage: ${DISK_USAGE}%" >> $LOG_FILE + # Очистка старых логов и кэша + find /opt/gitea-runner/logs -name "*.log" -mtime +7 -delete + docker system prune -f +fi +EOF + +chmod +x /opt/gitea-runner/monitor.sh +``` + +2. **Настройка cron для мониторинга:** + +```bash +# Добавление в crontab +echo "*/5 * * * * /opt/gitea-runner/monitor.sh" | crontab - +``` + +### Тестирование Runner + +1. **Проверка подключения:** + +```bash +# Проверка статуса runner в Gitea +curl -H "Authorization: token YOUR_GITEA_TOKEN" \ + https://gitea.your-domain.com/api/v1/actions/runners +``` + +2. **Создание тестового workflow:** + +```yaml +# .gitea/workflows/test-runner.yaml +name: Test Runner +# on: +# push: +# branches: [main] +on: + push: + tags: + - v* + +jobs: + test: + runs-on: self-hosted + steps: + - name: Checkout + run: | + git clone https://oauth2:${{ secrets.GITEATOKEN }}@gitea.your-domain.com/your-username/your-repo.git + cd your-repo + + - name: Test Go + run: | + go version + go mod download + go build -o test-app main.go + + - name: Test Docker + run: | + docker --version + docker run hello-world + + - name: Test ARM64 Build + run: | + # Тестирование сборки для ARM64 + GOOS=linux GOARCH=arm64 go build -o test-app-arm64 main.go + file test-app-arm64 + # Должно показать: ELF 64-bit LSB executable, ARM aarch64 +``` + +### Оптимизация производительности + +1. **Настройка Docker daemon:** + +```bash +# Создание daemon.json +cat > /etc/docker/daemon.json < /etc/systemd/system/gitea-runner.service < /opt/gitea-runner/backup.sh <<'EOF' +#!/bin/bash + +BACKUP_DIR="/opt/gitea-runner/backups" +DATE=$(date +%Y%m%d_%H%M%S) + +mkdir -p $BACKUP_DIR + +# Резервное копирование конфигурации +tar -czf $BACKUP_DIR/config_$DATE.tar.gz /opt/gitea-runner/config.yaml + +# Резервное копирование логов +tar -czf $BACKUP_DIR/logs_$DATE.tar.gz /opt/gitea-runner/logs/ + +# Удаление старых резервных копий (старше 30 дней) +find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete + +echo "Backup completed: $DATE" +EOF + +chmod +x /opt/gitea-runner/backup.sh + +# Добавление в crontab (ежедневно в 2:00) +echo "0 2 * * * /opt/gitea-runner/backup.sh" | crontab - +``` + + +## Работа с кластером + +### Основные команды для работы с кластером + +```bash +# Установка переменной окружения для работы с кластером +export KUBECONFIG=~/.kube/{configfile} + +# Проверка состояния кластера +kubectl get nodes -o wide +kubectl get pods -A + +# Работа с Gitea +kubectl get pods -n gitea +kubectl logs -n gitea deployment/gitea +kubectl port-forward -n gitea svc/gitea-http 3000:3000 + +# Проверка storage +kubectl get pvc -n gitea +kubectl get storageclass + +# Проверка ingress и сервисов +kubectl get svc -n default | grep traefik +kubectl get crds +kubectl get ingressroutes.traefik.io -A + +# Проверка сертификатов +kubectl get certificates -A -o wide +``` + +### Мониторинг и обслуживание + +```bash +# Проверка ресурсов кластера +kubectl top nodes +kubectl top pods -n gitea + +# Проверка событий +kubectl get events -n gitea --sort-by='.lastTimestamp' + +# Резервное копирование Gitea +kubectl exec -n gitea deployment/gitea -- gitea dump -c /data/gitea/conf/app.ini + +# Обновление Gitea +helm repo update +helm upgrade gitea gitea-charts/gitea -n gitea --values gitea-values.yaml +``` + +### Устранение неполадок + +```bash +# Проверка логов Gitea +kubectl logs -n gitea deployment/gitea -f + +# Проверка логов PostgreSQL +kubectl logs -n gitea statefulset/gitea-postgres -c postgres + +# Проверка логов Valkey +kubectl logs -n gitea statefulset/gitea-valkey-cluster -c valkey + +# Проверка сетевой связности +kubectl exec -n gitea deployment/gitea -- ping gitea-postgres +kubectl exec -n gitea deployment/gitea -- ping gitea-valkey-cluster +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..574fbcd --- /dev/null +++ b/go.mod @@ -0,0 +1,34 @@ +module direct-dev-ru/hello_gitea + +go 1.21 + +require github.com/gin-gonic/gin v1.10.1 + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0397106 --- /dev/null +++ b/go.sum @@ -0,0 +1,89 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..db52cca --- /dev/null +++ b/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "net/http" + "os" + + "github.com/gin-gonic/gin" +) + +const version = "1.0.0" + +func main() { + // Set Gin mode + gin.SetMode(gin.ReleaseMode) + + // Create router + r := gin.Default() + + // Add middleware for CORS + r.Use(func(c *gin.Context) { + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + return + } + + c.Next() + }) + + // Health check endpoint + r.GET("/health", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "status": "ok", + "version": version, + }) + }) + + // Main endpoint + r.GET("/", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "Hello, World!", + "version": version, + }) + }) + + // API endpoints + api := r.Group("/api/v1") + { + api.GET("/info", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "service": "hello-api", + "version": version, + "status": "running", + }) + }) + + api.POST("/echo", func(c *gin.Context) { + var request struct { + Message string `json:"message"` + } + + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Invalid JSON", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "echo": request.Message, + "version": version, + }) + }) + } + + // Get port from environment or use default + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + // Start server + r.Run(":" + port) +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..1e1d1b7 --- /dev/null +++ b/makefile @@ -0,0 +1,25 @@ +.PHONY: build clean test release + +BIN_DIR=bin +APP_NAME=hello-api +VERSION=1.0.0 + +build: + mkdir -p $(BIN_DIR) + go build -o $(BIN_DIR)/$(APP_NAME)-$(VERSION) main.go + +clean: + rm -rf $(BIN_DIR) + +test: + go test -v ./... + +# Задача для создания релиза +# Использование: make release VERSION=1.0.25 +release: + # @if [ -z "$(VERSION)" ]; then \ + # echo "Ошибка: Необходимо указать версию"; \ + # echo "Использование: make release VERSION=1.0.25"; \ + # exit 1; \ + # fi + @./scripts/release-interactive.sh $(VERSION) \ No newline at end of file diff --git a/scripts/release-interactive.sh b/scripts/release-interactive.sh new file mode 100755 index 0000000..ca82cf9 --- /dev/null +++ b/scripts/release-interactive.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Интерактивный скрипт для автоматизации релиза +# Использование: ./scripts/release-interactive.sh [version] +# Если версия не указана, скрипт запросит её интерактивно + +set -e # Остановить выполнение при ошибке + +# Функция для получения версии интерактивно +get_version_interactive() { + echo "🚀 Создание нового релиза" + echo "" + + # Показываем текущую версию + CURRENT_VERSION=$(grep 'const version = "' main.go | sed 's/const version = "\([^"]*\)"/\1/') + echo "📋 Текущая версия: $CURRENT_VERSION" + echo "" + + # Запрашиваем новую версию + read -p "Введите новую версию (формат X.Y.Z): " VERSION + + # Проверяем, что версия не пустая + if [ -z "$VERSION" ]; then + echo "❌ Версия не может быть пустой" + exit 1 + fi + + # Проверяем формат версии + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 + fi + + # Подтверждение + echo "" + echo "📝 Подтверждение:" + echo " Текущая версия: $CURRENT_VERSION" + echo " Новая версия: $VERSION" + echo "" + read -p "Продолжить? (y/N): " CONFIRM + + if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then + echo "❌ Релиз отменен" + exit 0 + fi +} + +# Проверяем, передана ли версия как аргумент +if [ $# -eq 0 ]; then + # Версия не указана, запрашиваем интерактивно + get_version_interactive +else + # Версия указана как аргумент + VERSION=$1 + + # Проверка формата версии + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Ошибка: Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 + fi +fi + +echo "🚀 Начинаем релиз версии v$VERSION..." + +# Проверяем, что мы в git репозитории +if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo "Ошибка: Не найден git репозиторий" + exit 1 +fi + +# Проверяем, что нет незакоммиченных изменений +# if ! git diff-index --quiet HEAD --; then +# echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их." +# exit 1 +# fi + +# Обновляем версию в main.go +echo "📝 Обновляем версию в main.go..." +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go +else + # Linux + sed -i "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go +fi + +# Проверяем, что изменение применилось +if ! grep -q "const version = \"$VERSION\"" main.go; then + echo "Ошибка: Не удалось обновить версию в main.go" + exit 1 +fi + +echo "✅ Версия обновлена в main.go" + +# Выполняем git команды +echo "📦 Добавляем изменения в git..." +git add . + +echo "💾 Создаем коммит..." +git commit -m "Release v$VERSION" + +echo "🏷️ Создаем тег..." +git tag -a "v$VERSION" -m "Release v$VERSION" + +echo "🚀 Отправляем изменения и теги..." +git push +git push --tags + +echo "🎉 Релиз v$VERSION успешно завершен!" +echo "📋 Выполненные действия:" +echo " - Обновлена версия в main.go" +echo " - Создан коммит с сообщением 'Release v$VERSION'" +echo " - Создан тег v$VERSION" +echo " - Изменения отправлены в удаленный репозиторий" \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..acdd560 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Скрипт для автоматизации релиза +# Использование: ./scripts/release.sh +# Пример: ./scripts/release.sh 1.0.25 + +set -e # Остановить выполнение при ошибке + +# Проверка аргументов +if [ $# -eq 0 ]; then + echo "Ошибка: Необходимо указать версию" + echo "Использование: $0 " + echo "Пример: $0 1.0.25" + exit 1 +fi + +VERSION=$1 + +# Проверка формата версии (простая проверка) +if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Ошибка: Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 +fi + +echo "🚀 Начинаем релиз версии v$VERSION..." + +# Проверяем, что мы в git репозитории +if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo "Ошибка: Не найден git репозиторий" + exit 1 +fi + +# Проверяем, что нет незакоммиченных изменений +if ! git diff-index --quiet HEAD --; then + echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их." + exit 1 +fi + +# Обновляем версию в main.go +echo "📝 Обновляем версию в main.go..." +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go +else + # Linux + sed -i "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go +fi + +# Проверяем, что изменение применилось +if ! grep -q "const version = \"$VERSION\"" main.go; then + echo "Ошибка: Не удалось обновить версию в main.go" + exit 1 +fi + +echo "✅ Версия обновлена в main.go" + +# Выполняем git команды +echo "📦 Добавляем изменения в git..." +git add . + +echo "💾 Создаем коммит..." +git commit -m "Release v$VERSION" + +echo "🏷️ Создаем тег..." +git tag -a "v$VERSION" -m "Release v$VERSION" + +echo "🚀 Отправляем изменения и теги..." +git push +git push --tags + +echo "🎉 Релиз v$VERSION успешно завершен!" +echo "📋 Выполненные действия:" +echo " - Обновлена версия в main.go" +echo " - Создан коммит с сообщением 'Release v$VERSION'" +echo " - Создан тег v$VERSION" +echo " - Изменения отправлены в удаленный репозиторий" \ No newline at end of file