12 Commits

Author SHA1 Message Date
75822f7fb8 Release v1.0.35
Some checks failed
Release Build / create-release (push) Failing after 6m17s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:33:06 +06:00
707cd82717 Release v1.0.34
Some checks failed
Release Build / create-release (push) Failing after 6m29s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:11:14 +06:00
6f10caa69d Release v1.0.33
Some checks failed
Release Build / create-release (push) Successful in 6m37s
Release Build / create-docker-image (push) Failing after 37s
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:00:47 +06:00
da3538051e Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 3s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:59:35 +06:00
032bdaa619 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 2s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:56:50 +06:00
b786ed1bf8 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 3s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:55:34 +06:00
231867a310 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 2s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:50:29 +06:00
9d82d42680 Release v1.0.32
Some checks failed
Release Build / create-release (push) Failing after 6s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:26:11 +06:00
9da418648c changed builder image
All checks were successful
Build Builder Docker Image / create-builder-docker-image (push) Successful in 5m42s
2025-07-28 16:02:16 +06:00
7a33e815af Release v1.0.31
All checks were successful
Release Build / create-release (push) Successful in 6m27s
Release Build / create-docker-image (push) Successful in 3m32s
Release Build / update-to-release-branch (push) Successful in 8s
2025-07-28 15:47:12 +06:00
bf643393ad Release v1.0.30
All checks were successful
Release Build / create-release (push) Successful in 6m30s
Release Build / create-docker-image (push) Successful in 3m41s
Release Build / update-to-release-branch (push) Successful in 8s
2025-07-28 14:05:01 +06:00
c4a3ea4d98 Release v1.0.28
All checks were successful
Release Build / create-release (push) Successful in 6m40s
Release Build / create-docker-image (push) Successful in 3m28s
Release Build / update-to-release-branch (push) Successful in 7s
2025-07-28 12:42:44 +06:00
12 changed files with 1056 additions and 448 deletions

View File

@@ -1,4 +1,4 @@
name: Release Build
name: Build Builder Docker Image
on:
push:
tags:
@@ -21,26 +21,31 @@ jobs:
echo "github.sha = ${{ github.sha }}"
echo "github.repository = ${{ github.repository }}"
echo "========================"
echo "Cloning..."
git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea
cd hello_gitea
echo "Checkout to ${{ github.ref }} ..."
git checkout ${{ github.ref }}
- name: Setup Docker Buildx
run: |
# Docker is already installed in docker:dind image
echo "look at docker version"
docker --version
# Setup Docker Buildx for multi-platform builds
docker buildx create --use
echo "setup buildx"
docker buildx create --name builder-builx --use
docker buildx inspect --bootstrap
- name: Login to Docker Hub
run: |
echo "login to docker hub ..."
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
echo "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 }} \
@@ -49,3 +54,6 @@ jobs:
-f Dockerfile.builder \
.
- name: Remove buildx
run: |
docker buildx rm builder-builx

View File

@@ -5,139 +5,168 @@ on:
- v*
jobs:
# create-release:
# runs-on: ubuntu-latest
# container:
# # image: golang:1.21
# image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
# needs: create-release-branch
# 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 }}
create-release:
runs-on: ubuntu-latest
container:
# image: golang:1.21
# image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:builder-1.0.32
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
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
- name: Setup Go
run: |
# Install jq for JSON parsing
# apt-get update && apt-get install -y jq
# git --version
# go version
# jq --version
git --version
go version
jq --version
# - name: Build all binaries
# run: |
# cd hello_gitea
# mkdir -p bin
- name: Build all binaries
run: |
cd hello_gitea
mkdir -p bin
echo "Building for all platforms..."
# Build for all platforms
echo "Building for linux amd64..."
quick-build linux amd64
echo "Building for linux arm64..."
quick-build linux arm64
echo "Building for windows amd64..."
quick-build windows amd64
echo "Building for darwin amd64..."
quick-build darwin amd64
echo "Building for darwin arm64..."
quick-build darwin arm64
# # Build for all platforms
# Build for all platforms - old variant
# 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
# Create archives
echo "Creating 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
echo "Listing bin directory..."
ls -la bin
# - 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"
- name: Create Release
run: |
cd hello_gitea
# Create release using Gitea API
echo "Creating release..."
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')
echo "Getting release id..."
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
# Upload all binaries
echo "Uploading assets..."
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
# create-docker-image:
# runs-on: ubuntu-latest
# container:
# image: docker:28.3.2-dind
# needs: create-release
# steps:
# - name: Checkout repository
# run: |
# # Install git
# apk add --no-cache git
echo "Release created successfully"
# 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-branch:
create-docker-image:
runs-on: ubuntu-latest
container:
image: docker:28.3.2-dind
# needs: create-docker-image
needs: create-release
steps:
- name: Create Release Branch
- 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
echo "Setting up Docker Buildx for multi-platform builds..."
# Setup Docker Buildx for multi-platform builds
docker buildx create --name go-buildx --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
echo "Building multi-platform images using buildx..."
# 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 \
.
echo "Multi-platform images built successfully"
- name: Remove buildx
run: |
echo "Removing buildx..."
docker buildx rm go-buildx
update-to-release-branch:
runs-on: ubuntu-latest
container:
image: docker:28.3.2-dind
needs: create-docker-image
steps:
- name: Create Release Branch
run: |
echo "Creating release branch..."
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 "DOCKERHUB_USERNAME = ${{ secrets.DOCKERHUB_USERNAME }}"
echo "DOCKERHUB_TOKEN = ${{ secrets.DOCKERHUB_TOKEN }}"
echo "GITEATOKEN = ${{ secrets.GITEATOKEN }}"
echo "========================"
# Clone repository
echo "Cloning repository..."
git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea
@@ -167,3 +196,4 @@ jobs:
# Push changes to release branch
echo "Pushing changes to release branch..."
git push origin release --force
echo "Changes pushed to release branch successfully"

View File

@@ -1,5 +1,5 @@
# Build stage
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
# Install git and ca-certificates
RUN apk --no-cache add git ca-certificates

View File

@@ -9,5 +9,24 @@ RUN apt-get update && \
jq && \
rm -rf /var/lib/apt/lists/*
# Создаем рабочую директорию
WORKDIR /app
# Копируем файлы зависимостей
COPY go.mod go.sum ./
# Предварительно загружаем все зависимости
RUN go mod download && go mod verify
# Создаем скрипт для быстрой сборки
COPY scripts/quick-build.sh /usr/local/bin/quick-build
# делаем скрипт исполняемым
RUN chmod +x /usr/local/bin/quick-build
# Устанавливаем переменные окружения
ENV GOPATH=/go
ENV PATH=$PATH:/go/bin:/usr/local/bin
# (Опционально) Можно добавить команду по умолчанию
CMD ["bash"]

238
README.md
View File

@@ -1,12 +1,15 @@
# Hello Gitea API
Простой REST API сервер, построенный на Go с использованием Gin framework.
Современный REST API сервер, построенный на Go с использованием Gin framework и структурированного логирования.
## 🚀 Возможности
- ✅ REST API с JSON ответами
- ✅ Health check endpoint
- ✅ CORS поддержка
-**Современное структурированное логирование** (Go 1.24+)
-**Настраиваемые уровни и форматы логирования**
-**Системная информация и мониторинг**
- ✅ Мультиплатформенная сборка
- ✅ Docker образы для Linux AMD64/ARM64
- ✅ Автоматические релизы через Gitea Actions
@@ -37,6 +40,8 @@ go run main.go
## 🔧 Конфигурация
### Основные настройки
Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения:
```bash
@@ -44,46 +49,98 @@ export PORT=3000
./hello-api
```
### Настройка логирования
Приложение поддерживает гибкую настройку логирования через переменные окружения:
#### Уровни логирования (`LOG_LEVEL`)
```bash
# Доступные уровни (по возрастанию важности):
export LOG_LEVEL=DEBUG # Все сообщения
export LOG_LEVEL=INFO # Информационные и выше
export LOG_LEVEL=WARN # Предупреждения и выше
export LOG_LEVEL=ERROR # Только ошибки
```
#### Форматы логирования (`LOG_FORMAT`)
```bash
# JSON формат (по умолчанию) - для production
export LOG_FORMAT=json
# Текстовый формат - для разработки
export LOG_FORMAT=text
```
#### Примеры конфигурации
**Для разработки:**
```bash
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
export PORT=8080
go run main.go
```
**Для production:**
```bash
export LOG_LEVEL=INFO
export LOG_FORMAT=json
export PORT=8080
./hello-api
```
## 📡 API Endpoints
### GET /
Основной endpoint
**Ответ:**
```json
{
"message": "Hello, World!",
"version": "1.0.0"
"version": "1.0.33"
}
```
### GET /health
### GET /healthz
Health check endpoint
**Ответ:**
```json
{
"status": "ok",
"version": "1.0.0"
"version": "1.0.33"
}
```
### GET /api/v1/info
Информация о сервисе
**Ответ:**
```json
{
"service": "hello-api",
"status": "running",
"version": "1.0.0"
"version": "1.0.33"
}
```
### POST /api/v1/echo
Echo endpoint - возвращает отправленное сообщение
**Запрос:**
```json
{
"message": "Hello from client!"
@@ -91,18 +148,149 @@ Echo endpoint - возвращает отправленное сообщение
```
**Ответ:**
```json
{
"echo": "Hello from client!",
"version": "1.0.0"
"version": "1.0.33"
}
```
## 🛠 Разработка
### GET /api/v1/config
**Новый endpoint** - системная информация и конфигурация
**Ответ:**
```json
{
"version": "1.0.33",
"system_info": {
"go_version": "go1.24.0",
"os": "linux",
"architecture": "amd64",
"num_cpu": 8,
"start_time": "2024-01-15T10:30:45Z"
},
"environment": {
"PATH": "/usr/local/bin:/usr/bin:/bin",
"PORT": "8080",
"LOG_LEVEL": "INFO"
},
"uptime": "2h30m15s"
}
```
### POST /api/v1/log-test
**Новый endpoint** - тестирование уровней логирования
**Запрос:**
```json
{
"level": "debug",
"message": "Test log message"
}
```
**Ответ:**
```json
{
"status": "logged",
"level": "debug",
"message": "Test log message"
}
```
## 📊 Логирование
### Структурированное логирование
Приложение использует современную систему логирования Go 1.24+ (`log/slog`) с поддержкой:
- **Структурированных логов** в JSON и текстовом формате
- **Уровней логирования** (DEBUG, INFO, WARN, ERROR)
- **Контекстной информации** (IP клиента, User-Agent, время запроса)
- **Автоматического форматирования** времени
- **Фильтрации чувствительных данных**
### Примеры логов
#### JSON формат (production)
```json
{
"time": "2024-01-15T10:30:45.123Z",
"level": "INFO",
"msg": "HTTP Request",
"method": "GET",
"path": "/api/v1/config",
"status": 200,
"latency": "1.234ms",
"client_ip": "192.168.1.100",
"user_agent": "curl/7.68.0",
"timestamp": "2024-01-15T10:30:45.123Z"
}
```
#### Текстовый формат (development)
``` text
2024-01-15T10:30:45.123Z INFO HTTP Request method=GET path=/api/v1/config status=200 latency=1.234ms client_ip=192.168.1.100 user_agent="curl/7.68.0"
```
## Основные изменения в README
### 🆕 **Новые разделы:**
1. **Конфигурация логирования** - подробное описание переменных окружения
2. **Форматы логирования** - примеры JSON и текстового формата
3. **Новые API endpoints** - `/api/v1/config` и `/api/v1/log-test`
4. **Безопасность логирования** - фильтрация чувствительных данных
### 📊 **Детальное описание логирования:**
- **Уровни логирования** (DEBUG, INFO, WARN, ERROR)
- **Форматы** (JSON для production, text для разработки)
- **Примеры логов** с реальными данными
- **Контекстная информация** в логах
### 🔧 **Практические примеры:**
- Настройка для разработки и production
- Команды для тестирования
- Отладочные приемы
### 📦 **Улучшенная структура:**
- Четкое разделение возможностей
- Пошаговые инструкции
- Примеры конфигурации
- Команды для тестирования
Теперь README полностью отражает современные возможности приложения с детальным описанием системы логирования! 🎉
### Логируемые события
- **HTTP запросы** с детальной информацией
- **Ошибки приложения** с контекстом
- **Системные события** (запуск, остановка)
- **API вызовы** с параметрами
- **Конфигурационные изменения**
### Безопасность
- **Автоматическая фильтрация** чувствительных переменных окружения
- **Безопасное логирование** без утечки секретов
- **Контролируемые уровни** для разных сред
## Разработка
### Зависимости
- Go 1.21+
- Go 1.24+
- Gin framework
### Сборка
@@ -128,16 +316,42 @@ GOOS=darwin GOARCH=arm64 go build -o hello-api-darwin-arm64 main.go
### Тестирование
```bash
# Запуск сервера
# Запуск сервера с отладочным логированием
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
go run main.go
# Тестирование API
curl http://localhost:8080/
curl http://localhost:8080/health
curl http://localhost:8080/healthz
curl http://localhost:8080/api/v1/info
curl http://localhost:8080/api/v1/config
# Тестирование echo
curl -X POST http://localhost:8080/api/v1/echo \
-H "Content-Type: application/json" \
-d '{"message":"Hello!"}'
# Тестирование логирования
curl -X POST http://localhost:8080/api/v1/log-test \
-H "Content-Type: application/json" \
-d '{"level": "debug", "message": "Test log message"}'
```
### Отладка
Для детального анализа работы приложения:
```bash
# Включить отладочное логирование
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
# Запустить приложение
go run main.go
# В другом терминале - мониторинг логов
tail -f /var/log/application.log # если логи в файл
```
## 🚀 CI/CD
@@ -161,8 +375,6 @@ curl -X POST http://localhost:8080/api/v1/echo \
Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей.
## 📄 Лицензия
MIT License
MIT Licens

View File

@@ -1,124 +0,0 @@
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: ${{ secrets.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

View File

@@ -1,13 +1,13 @@
# Настройка 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
---
```metadata
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
```
## Содержание
@@ -19,9 +19,9 @@
6. [Тестирование и запуск](#тестирование-и-запуск)
7. [Мониторинг и отладка](#мониторинг-и-отладка)
8. [Заключение](#заключение)
9. [Инфраструктура](#инфраструктура)
10. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s)
11. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере)
A. [Инфраструктура](#инфраструктура)
B. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s)
C. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере)
## Введение
@@ -43,7 +43,7 @@ Gitea Actions — это встроенная система непрерывн
Собственно функциональная часть проекта Go не блещет оригинальностью и имеет следующую простую структуру:
```
``` text
hello_gitea/
├── main.go # Основной код приложения
├── go.mod # Зависимости Go
@@ -93,7 +93,7 @@ func main() {
})
// Endpoints
r.GET("/health", func(c *gin.Context) {
r.GET("/healthz", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"version": version,
@@ -197,7 +197,6 @@ CMD ["./hello-api"]
базововго образа для каждого из этапов использовать предварительно собранный образ со всем необходимым.
При каждом запуске workflow будет запускаться новый контейнер docker in docker и там запускаться построение образа естественно кэшей не будет или надо как то придумывать как их хранить на ранере и пробрасывать в dind. Ну или руками сделать нужные образы и использоват их в Dockerfile. Правда пулиться они опять же будут походу каждый раз. Короче говоря тут есть чем поразбираться и пооптимизировать ...
## Настройка Gitea Actions
### Включение Actions в Gitea
@@ -242,55 +241,11 @@ on:
- 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
# image: golang:1.21
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
steps:
- name: Checkout repository
run: |
@@ -301,7 +256,7 @@ jobs:
- name: Setup Go
run: |
# Install jq for JSON parsing
apt-get update && apt-get install -y jq
# apt-get update && apt-get install -y jq
git --version
go version
jq --version
@@ -358,37 +313,121 @@ jobs:
--data-binary @$file \
"https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)"
done
create-docker-image:
runs-on: ubuntu-latest
container:
image: docker:28.3.2-dind
needs: create-release
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 \
.
update-to-release-branch:
runs-on: ubuntu-latest
container:
image: docker:28.3.2-dind
needs: create-docker-image
steps:
- name: Create Release Branch
run: |
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 "DOCKERHUB_USERNAME = ${{ secrets.DOCKERHUB_USERNAME }}"
echo "DOCKERHUB_TOKEN = ${{ secrets.DOCKERHUB_TOKEN }}"
echo "GITEATOKEN = ${{ secrets.GITEATOKEN }}"
echo "========================"
# Clone repository
echo "Cloning repository..."
git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea
cd hello_gitea
# Configure git
echo "Configuring git..."
git config user.email "info@direct-dev.ru"
git config user.name "Direct-Dev-Robot"
# Check if release branch exists
echo "Checking if release branch exists..."
if git ls-remote --heads origin release | grep -q release; then
echo "Release branch exists, checking out..."
git checkout release
echo "release branch exists - pulling release branch..."
git pull origin release
else
echo "release branch does not exist - creating new release branch..."
git checkout -b release
fi
# Reset to the tag commit
echo "Resetting to the tag commit ${{ github.ref_name }} ..."
git reset --hard ${{ github.ref_name }}
# Push changes to release branch
echo "Pushing changes to release branch..."
git push origin release --force
```
### Разберемся как работает workflow
**Триггеры:**
- Workflow запускается при создании тега, начинающегося с `v*` (например, `v1.0.0`)
- Workflow запускается при пуше в Gitea тега, начинающегося с `v*` (например, `v1.1.29`)
**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)
1 **create-release:**
чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всей gitea в настройках или в настройках конкретного репозитория
я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ...
2. **create-release:**
- Запускается после успешной сборки Docker образов
- Использует Go контейнер для сборки бинарников (image: golang:1.21)
- Эта таска запускается первой и не требует никаких зависимых тасков выполненными
- Использует контейнер собранный на базе golang:1.24 для сборки бинарников (image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest)
- Собирает бинарники для всех платформ (Linux, Windows, macOS)
- Создает архивы с бинарниками
- Создает релиз через Gitea API
- Создает релиз с именем текущей версии через Gitea API
- Загружает бинарники как assets релиза
при каждом запуске, как я понимаю, данного job будет скачиваться и устанавливаться в контейнере, созданном на базе golang:1.21
для работы данного job используется кастомный образ - если бы мы использовали просто golang:1.24, то при каждом запуске данного job необходимо было бы скачивать jq и устанавливаться в контейнере (возможно со временем, что-то еще потребовалось бы ...)
```yaml
- name: Setup Go
- name: Setup Go container
run: |
# Install jq for JSON parsing
apt-get update && apt-get install -y jq
@@ -397,28 +436,133 @@ jobs:
jq --version
```
решение как говорится в лоб: чтобы убрать эту работу при каждом выполнении workflow надо сделать свой образ
**решение в лоб:** применено, чтобы убрать эту повторяющуюся работу при каждом выполнении workflow - сделан свой образ - "${DOCKERHUB_USERNAME}"/my-build-golang-runner:latest
``` Dockerfile
# базовый образ
FROM golang:1.24
# Install some packages
RUN apt-get update && apt-get install -y git ca-certificates jq
# Устанавливаем пакеты (одинаково работают на 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"]
```
собрать его и запушить (желательно с мультиплатформенностью). конечно если вам не надо билдить образы для разных платформ тогда все попроще
Сначала собрал его и запушил (желательно с мультиплатформенностью) на каком-то локальном АРМ (не раннере)
```bash
#!/bin/bash
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest \
--tag ${DOCKERHUB_USERNAME:-defaultdockeruser}/my-build-golang-runner:latest \
--push \
-f Dockerfile_for_runner_image \
.
```
решение рабочее но надо всегда иметь под рукой АРм с buildx или лучше автоматизировать все и сделать задачу для сборки на раннере `.gitea/workflows/build-builder.yaml`
```yaml
name: Build Builder Docker Image
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 "========================"
echo "Cloning..."
git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea
cd hello_gitea
echo "Checkout to ${{ github.ref }} ..."
git checkout ${{ github.ref }}
- name: Setup Docker Buildx
run: |
# Docker is already installed in docker:dind image
echo "look at docker version"
docker --version
# Setup Docker Buildx for multi-platform builds
echo "setup buildx"
docker buildx create --name builder-builx --use
docker buildx inspect --bootstrap
- name: Login to Docker Hub
run: |
echo "login to docker hub ..."
echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- name: Build multi-platform Docker images
run: |
cd hello_gitea
echo "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 \
.
- name: Remove buildx
run: |
docker buildx rm builder-builx
```
эта задача будет запущена на ранере при пуше тега с префиксом `builder-`
2 **create-docker-image:** - создание образов docker с нашим проектом
- Запускается только после успешного завершения `create-release`
- Используем 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 команды docker buildx build ...)
чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всего инстанса gitea в настройках инстанса или в настройках конкретного репозитория
я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ...
Уже после ряда тестовых релизо выяснил, что запуски workflow `build.yaml` да собственно как и `build-builder.yaml` порождают на хосте раннера зависшие докер контейнеры buildx
```text
СONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a9ad63c5a31 moby/buildkit:buildx-stable-1 "buildkitd --allow-i…" 11 seconds ago Up 10 seconds buildx_buildkit_test-buldx0
843eee192570 moby/buildkit:buildx-stable-1 "buildkitd --allow-i…" 22 minutes ago Up 22 minutes buildx_buildkit_modest_haibt0
```
Это происходит потому, что раннер запускает докер в докере контейнер также
docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock docker:28.3.2-dind sh
то есть пробрасывает хостовый сокет докер демона в контейнер и когда там выполняется `docker buildx create --use` контейнер запускается на хосте а не внутри docker:28.3.2-dind
Со временем это может превратиться в утечку ресурсов
Решения как минимум два - добавить параметр --name - `docker buildx create --name go-buildx --use` - тогда подхватится существующий контейнер или создастся новый. Второй способ удалять buildx `docker buildx rm --name go-buildx`
## Настройка секретов
@@ -441,12 +585,14 @@ docker buildx build \
### Создание токенов
**Gitea Token:**
1. Перейдите в настройки профиля → "Applications"
2. Создайте новый токен с правами на репозиторий
3. Скопируйте токен
4. В разделе Actions репозитория создайте секрет уровня репозитория - я создал с именем GITEATOKEN (такой не даст сделать: GITEA_TOKEN)
**Docker Hub Token:**
1. Войдите в Docker Hub
2. Перейдите в Account Settings → Security
3. Создайте новый Access Token
@@ -457,9 +603,9 @@ docker buildx build \
### Локальное тестирование
1. **Проверим синтаксис workflow:**
1 **Проверим синтаксис workflow:**
ну если мы в ide то наверное все автоматом отформатировано
ну если мы в ide, то наверное все хорошо и уже автоматом отформатировано ...
но тем не менее ...
```bash
@@ -467,7 +613,10 @@ docker buildx build \
yamllint .gitea/workflows/build.yaml
```
2. **Протестируем сборку локально:**
2 **Протестируем сборку локально:**
пока ручное тестирование
```bash
# Сборка для текущей платформы
go build -o hello-api main.go
@@ -477,7 +626,8 @@ docker buildx build \
GOOS=linux GOARCH=arm64 go build -o hello-api-linux-arm64 main.go
```
3. **Тестирование Docker образа:**
3 **Тестирование Docker образа:**
```bash
# Сборка образа
docker build -t hello-api:test .
@@ -486,19 +636,23 @@ docker buildx build \
docker run -p 8080:8080 hello-api:test
# Тестирование API
curl http://localhost:8080/health
curl http://localhost:8080/healthz
```
тут надо погонять curl по эндпойнтам, убедиться что приложение работает (пока тоже вручную)
### Запуск Actions
1. **Создаем тег:**
1 **Создаем тег вручную или скриптом (`scripts/release-interactive.sh`):**
```bash
git tag v1.1.20
git push origin v1.1.20
# или скрипт
# текст скрипта - запуск можно сделать через make
#release-interactive:
# @./scripts/release-interactive.sh
#!/bin/bash
@@ -519,7 +673,7 @@ docker buildx build \
echo ""
# Запрашиваем новую версию
read -p "Введите новую версию (формат X.Y.Z): " VERSION
read -r -p "Введите новую версию (формат X.Y.Z): " VERSION
# Проверяем, что версия не пустая
if [ -z "$VERSION" ]; then
@@ -539,7 +693,7 @@ docker buildx build \
echo " Текущая версия: $CURRENT_VERSION"
echo " Новая версия: $VERSION"
echo ""
read -p "Продолжить? (y/N): " CONFIRM
read -r -p "Продолжить? (y/N): " CONFIRM
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
echo "❌ Релиз отменен"
@@ -594,6 +748,24 @@ docker buildx build \
echo "✅ Версия обновлена в main.go"
# Обновляем версию в makefile
echo "📝 Обновляем версию в makefile..."
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
sed -i '' "s/^VERSION=.*/VERSION=$VERSION/" makefile
else
# Linux
sed -i "s/^VERSION=.*/VERSION=$VERSION/" makefile
fi
# Проверяем, что изменение применилось
if ! grep -q "^VERSION=$VERSION" makefile; then
echo "Ошибка: Не удалось обновить версию в makefile"
exit 1
fi
echo "✅ Версия обновлена в makefile"
# Выполняем git команды
echo "📦 Добавляем изменения в git..."
git add .
@@ -611,13 +783,15 @@ docker buildx build \
echo "🎉 Релиз v$VERSION успешно завершен!"
echo "📋 Выполненные действия:"
echo " - Обновлена версия в main.go"
echo " - Обновлена версия в makefile"
echo " - Создан коммит с сообщением 'Release v$VERSION'"
echo " - Создан тег v$VERSION"
echo " - Изменения отправлены в удаленный репозиторий"
```
2. **Мониторинг выполнения:**
2 **Мониторинг выполнения:**
- Переходим в репозиторий → "Actions"
- Находим запущенный workflow
- Отслеживаем выполнение каждого job
@@ -626,46 +800,54 @@ docker buildx build \
После успешного выполнения:
1. **Docker образы** будут опубликованы в Docker Hub:
- `username/hello-api:v1.1.20`
- `username/hello-api:latest`
1 **Релиз** будет создан в Gitea с бинарниками:
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`
2 **Docker образы** будут опубликованы в Docker Hub:
- `username/hello-api:v1.1.20`
- `username/hello-api:latest`
3 **В ветке relese** появится новый коммит - на него можно, например, настроить деплой ArgoCD/flux в кластере k3s
## Мониторинг и отладка
### Просмотр логов
1. **В Gitea:**
1 **В Gitea:**
- Переходим в репозиторий → "Actions"
- Выберем workflow
- Нажмем на job для просмотра логов
2. **Отладка ошибок:**
2 **Отладка ошибок:**
- Проверим правильность секретов
- Убедимся в корректности путей к репозиторию
- Проверим права доступа токенов
### Частые проблемы
1. **Ошибка авторизации в Docker Hub:**
1 **Ошибка авторизации в Docker Hub:**
- Проверим правильность `DOCKERHUB_TOKEN`
- Убедимся, что токен не истек
2. **Ошибка создания релиза:**
2 **Ошибка создания релиза:**
- Проверим права токена `GITEATOKEN`
- Убедимся, что тег не существует
3. **Ошибка сборки:**
3 **Ошибка сборки:**
- Проверим зависимости в `go.mod`
- Убедимся в корректности Dockerfile
## Заключение
В этой статье мы рассмотрели полный процесс настройки Gitea Actions для Go проекта. Мы создали автоматизированный pipeline, который:
@@ -699,12 +881,13 @@ Gitea Actions предоставляет мощные возможности д
### Архитектура системы
Этот и последующие разделы описывают инфраструктуру на которой я проверял все описанное выше.
Если у вас используются другие подходы, то можете пропустить их или ознакомиться для общего развития.
Этот и последующие разделы описывают инфраструктуру, на которой я проверял все описанное выше.
Если у вас используются другие подходы, то можете пропустить чтение этих разделов
или ознакомиться для общего развития.
Итак инфраструктура состоит из следующих компонентов:
```
``` text
Proxmox VE (ARM64)
├── K3s Cluster
├── Master Node
@@ -720,31 +903,34 @@ Proxmox VE (ARM64)
### Требования к системе
**Proxmox VE:**
- ARM64 архитектура
- Минимум 8GB RAM
- 100GB свободного места
- Поддержка LXC контейнеров
- ARM64 архитектура (orangepi 5 Plus)
- Минимум 8GB RAM (16Gb)
- 100GB свободного места (1 Gb)
- Поддержка LXC контейнеров (+)
**K3s Cluster:**
- Kubernetes 1.24+
- Helm 3.8+
- Ingress Controller (Traefik)
- Persistent Storage (Longhorn)
- Persistent Storage (NFS)
**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
@@ -754,6 +940,7 @@ k3s-control-03 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.x
```
**Существующие компоненты:**
- Kubernetes v1.33.2+k3s1
- 3 узла control-plane с etcd
- Traefik Ingress Controller
@@ -804,7 +991,7 @@ valkey-data-gitea-valkey-cluster-2 Bound pvc-368f0ab9-7851-423f-afb9-443287
Доступ к gitea лучше сделать через ingress или на худой конец через port-forward:
1. **Временный доступ через port-forward:**
1 **Временный доступ через port-forward:**
```bash
# Доступ к Gitea через port-forward
@@ -814,7 +1001,7 @@ kubectl port-forward -n gitea svc/gitea-http 3000:3000
curl http://localhost:3000
```
2. **Создание IngressRoute для постоянного доступа:**
2 **Создание IngressRoute для постоянного доступа:**
```yaml
# gitea-ingressroute.yaml
@@ -850,12 +1037,12 @@ spec:
KUBECONFIG=~/.kube/config_hlab kubectl apply -f gitea-ingress.yaml
```
Таким образом мой инстанс gitea доступен с внешнего адреса https://direct-dev.ru/gitea
Таким образом мой инстанс gitea доступен с внешнего адреса <https://direct-dev.ru/gitea>
в /data/gitea/conf/app.ini
[server]
ROOT_URL = https://direct-dev.ru/gitea
ROOT_URL = <https://direct-dev.ru/gitea>
DOMAIN = direct-dev.ru
### Конфигурация Gitea
@@ -864,6 +1051,7 @@ Gitea установлен через Helm.
**Версия:** Gitea 1.24.3 (rootless)
**Архитектура:**
- PostgreSQL для базы данных (развернут отдельно )
- Valkey cluster (Redis) для кэширования
- NFS Storage Class для persistent storage
@@ -892,7 +1080,6 @@ helm repo update
helm upgrade gitea gitea-charts/gitea -n gitea -f gitea-values.yaml
```
**Проверка конфигурации:**
```bash
@@ -949,27 +1136,31 @@ redis-cluster:
enabled: false
```
Как видите я организовал доступ через внешний IP к URL `https://direct-dev.ru/gitea`, то есть через префикс роута, опыт работы с actions показал, что лучше бы организовать было через поддомен третьего уровня: что то типа `https://gitea.direct-dev.ru` - в этом случае всякие разные предопределенные jobs типа checkout@v3 должны клонирование отрабатывать нормально.
``` bash
# Проверка секретов
kubectl get secrets -n gitea
```
### Настройка Actions в существующем Gitea
### Настройка Actions в существующем инстансе Gitea
1. **Проверка включения Actions:**
1 **Проверка включения Actions:**
```bash
# Проверка конфигурации Actions
kubectl exec -n gitea deployment/gitea -c gitea -- cat /data/gitea/conf/app.ini"
```
2. **Включение Actions через веб-интерфейс:**
2 **Включение Actions через веб-интерфейс:**
- Откройте Gitea через port-forward или ingress
- Перейдите в Site Administration → Actions
- Включите "Enable Actions"
- Настройте "Default Actions URL" (например, https://gitea.com)
- Настройте "Default Actions URL" (например, <https://gitea.com>)
3. **Проверка работы Actions:**
3 **Проверка работы Actions:**
```bash
# Проверка логов Gitea на предмет ошибок Actions
@@ -978,7 +1169,8 @@ kubectl logs -n gitea deployment/gitea | grep -i action
### Настройка DNS и SSL
Cert-Manager установлен. Инструкций вагон и маленькая тележка - не смысла повторяться. Проверим конфигурацию:
Инструкция по установке Cert-Manager не приводится - инструкций вагон и маленькая тележка -
нет смысла повторяться:
```bash
# Проверка Cert-Manager
@@ -990,12 +1182,13 @@ kubectl get clusterissuer
1. **Настройка DNS записи:**
```bash
# Добавить A-запись в DNS (у меня сloudflare)
gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ... чтобы в итоге запрос поступил на traefik
```text
Добавить A-запись в DNS (у меня сloudflare)
gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ...
чтобы в итоге запрос поступил на traefik
```
2. **Создание ClusterIssuer (если не существует):**
2 **Создание ClusterIssuer (если не существует):**
```bash
# Создание ClusterIssuer для Let's Encrypt - лучше через dns resolver
@@ -1027,7 +1220,7 @@ EOF
### Доступ к Gitea
1. **Временный доступ через port-forward:**
1 **Временный доступ через port-forward:**
```bash
# Доступ к Gitea через port-forward
@@ -1036,14 +1229,14 @@ kubectl port-forward -n gitea svc/gitea-http 3000:3000
# В браузере http://localhost:3000/gitea
```
2. **Постоянный доступ через ingressroute:**
2 **Постоянный доступ через ingressroute:**
``` bash
# Проверка сертификата
kubectl get certificate -n gitea
```
3. **Проверка доступа:**
3 **Проверка доступа:**
```bash
# Проверка через curl
@@ -1094,7 +1287,7 @@ spec:
# - Network: DHCP
```
2. **Настройка сети:**
2 **Настройка сети:**
```bash
# В контейнере
@@ -1114,7 +1307,7 @@ apt update && apt upgrade -y
apt install -y curl wget git vim htop
```
2. **Установка Docker:**
2 **Установка Docker:**
```bash
# Удаление старых версий
@@ -1151,7 +1344,7 @@ systemctl start docker
docker --version
```
3. **Установка Go:**
3 **Установка Go:**
```bash
# Скачивание Go для ARM64
@@ -1174,7 +1367,7 @@ go version
mkdir -p $HOME/go/{bin,src,pkg}
```
4. **Установка дополнительных инструментов:**
4 **Установка дополнительных инструментов:**
```bash
# Установка build tools
@@ -1216,7 +1409,7 @@ chmod +x /usr/local/bin/act_runner
act_runner --version
```
2. **Создание пользователя для runner:**
2 **Создание пользователя для runner:**
```bash
# Создание пользователя
@@ -1228,7 +1421,7 @@ mkdir -p /opt/gitea-runner
chown gitea-runner:gitea-runner /opt/gitea-runner
```
3. **Настройка конфигурации runner:**
3 **Настройка конфигурации runner:**
```bash
# Переключение на пользователя runner
@@ -1353,7 +1546,7 @@ EOF
- Нажмите "New Runner"
- Скопируйте токен регистрации
2. **Регистрация runner:**
2 **Регистрация runner:**
```bash
# В контейнере LXC под пользователем gitea-runner
@@ -1366,7 +1559,7 @@ act_runner register \
```
3. **Создание systemd сервиса:**
3 **Создание systemd сервиса:**
```bash
# Создание файла сервиса
@@ -1432,7 +1625,7 @@ EOF
chmod +x /opt/gitea-runner/monitor.sh
```
2. **Настройка cron для мониторинга:**
2 **Настройка cron для мониторинга:**
```bash
# Добавление в crontab
@@ -1449,7 +1642,7 @@ curl -H "Authorization: token YOUR_GITEA_TOKEN" \
https://gitea.your-domain.com/api/v1/actions/runners
```
2. **Создание тестового workflow:**
2 **Создание тестового workflow:**
```yaml
# .gitea/workflows/test-runner.yaml
@@ -1517,7 +1710,7 @@ EOF
systemctl restart docker
```
2. **Настройка ограничений ресурсов:**
2 **Настройка ограничений ресурсов:**
```bash
# Обновление systemd сервиса с ограничениями
@@ -1580,7 +1773,6 @@ chmod +x /opt/gitea-runner/backup.sh
echo "0 2 * * * /opt/gitea-runner/backup.sh" | crontab -
```
## Работа с кластером
### Основные команды для работы с кластером

2
go.mod
View File

@@ -1,6 +1,6 @@
module direct-dev-ru/hello_gitea
go 1.21
go 1.24
require github.com/gin-gonic/gin v1.10.1

243
main.go
View File

@@ -1,20 +1,127 @@
package main
import (
"context"
"log/slog"
"net/http"
"os"
"runtime"
"time"
"github.com/gin-gonic/gin"
)
const version = "1.0.27"
const version = "1.0.35"
// SystemInfo holds system information
type SystemInfo struct {
GoVersion string `json:"go_version"`
OS string `json:"os"`
Architecture string `json:"architecture"`
NumCPU int `json:"num_cpu"`
StartTime string `json:"start_time"`
}
var (
startTime = time.Now()
logger *slog.Logger
)
// setupLogger configures structured logging with different levels for dev/prod
func setupLogger() {
var logLevel slog.Level
var logFormat string
// Determine log level from environment
switch os.Getenv("LOG_LEVEL") {
case "DEBUG":
logLevel = slog.LevelDebug
case "INFO":
logLevel = slog.LevelInfo
case "WARN":
logLevel = slog.LevelWarn
case "ERROR":
logLevel = slog.LevelError
default:
logLevel = slog.LevelInfo
}
// Determine log format from environment
logFormat = os.Getenv("LOG_FORMAT")
if logFormat == "" {
logFormat = "json" // Default to JSON for production
}
// Configure logger based on format
var handler slog.Handler
opts := &slog.HandlerOptions{
Level: logLevel,
AddSource: true,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// Customize timestamp format
if a.Key == slog.TimeKey {
return slog.Attr{
Key: slog.TimeKey,
Value: slog.StringValue(a.Value.Time().Format("2006-01-02T15:04:05.000Z07:00")),
}
}
return a
},
}
if logFormat == "text" {
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
handler = slog.NewJSONHandler(os.Stdout, opts)
}
logger = slog.New(handler)
slog.SetDefault(logger)
}
// logMiddleware creates a structured logging middleware for Gin
func logMiddleware() gin.HandlerFunc {
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// Create structured log entry
logger.Info("HTTP Request",
"method", param.Method,
"path", param.Path,
"status", param.StatusCode,
"latency", param.Latency,
"client_ip", param.ClientIP,
"user_agent", param.Request.UserAgent(),
"timestamp", param.TimeStamp.Format("2006-01-02T15:04:05.000Z07:00"),
)
return "" // Return empty string as we handle logging ourselves
})
}
// errorLogger logs errors with structured context
func ErrorLogger(err error, context ...any) {
logger.Error("Application Error",
append([]any{"error", err.Error()}, context...)...,
)
}
func main() {
// Setup structured logging
setupLogger()
logger.Info("Starting application",
"version", version,
"go_version", runtime.Version(),
"os", runtime.GOOS,
"architecture", runtime.GOARCH,
"num_cpu", runtime.NumCPU(),
)
// Set Gin mode
gin.SetMode(gin.ReleaseMode)
// Create router
r := gin.Default()
// Create router with custom logger
r := gin.New()
r.Use(logMiddleware())
r.Use(gin.Recovery())
// Add middleware for CORS
r.Use(func(c *gin.Context) {
@@ -31,7 +138,8 @@ func main() {
})
// Health check endpoint
r.GET("/health", func(c *gin.Context) {
r.GET("/healthz", func(c *gin.Context) {
logger.Debug("Health check requested", "client_ip", c.ClientIP())
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"version": version,
@@ -40,6 +148,10 @@ func main() {
// Main endpoint
r.GET("/", func(c *gin.Context) {
logger.Info("Root endpoint accessed",
"client_ip", c.ClientIP(),
"user_agent", c.Request.UserAgent(),
)
c.JSON(http.StatusOK, gin.H{
"message": "Hello, World!",
"version": version,
@@ -50,6 +162,7 @@ func main() {
api := r.Group("/api/v1")
{
api.GET("/info", func(c *gin.Context) {
logger.Debug("API info requested", "client_ip", c.ClientIP())
c.JSON(http.StatusOK, gin.H{
"service": "hello-api",
"version": version,
@@ -63,17 +176,104 @@ func main() {
}
if err := c.ShouldBindJSON(&request); err != nil {
logger.Warn("Invalid JSON in echo request",
"client_ip", c.ClientIP(),
"error", err.Error(),
)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid JSON",
})
return
}
logger.Info("Echo request processed",
"client_ip", c.ClientIP(),
"message_length", len(request.Message),
)
c.JSON(http.StatusOK, gin.H{
"echo": request.Message,
"version": version,
})
})
// Configuration endpoint with detailed logging
api.GET("/config", func(c *gin.Context) {
logger.Debug("Configuration requested", "client_ip", c.ClientIP())
systemInfo := SystemInfo{
GoVersion: runtime.Version(),
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
NumCPU: runtime.NumCPU(),
StartTime: startTime.Format(time.RFC3339),
}
// Get environment variables (excluding sensitive ones)
envVars := make(map[string]string)
sensitiveCount := 0
for _, env := range os.Environ() {
// Skip sensitive environment variables
if !isSensitiveEnvVar(env) {
envVars[env] = os.Getenv(env)
} else {
sensitiveCount++
}
}
logger.Info("Configuration accessed",
"client_ip", c.ClientIP(),
"env_vars_count", len(envVars),
"sensitive_vars_filtered", sensitiveCount,
"uptime", time.Since(startTime).String(),
)
c.JSON(http.StatusOK, gin.H{
"version": version,
"system_info": systemInfo,
"environment": envVars,
"uptime": time.Since(startTime).String(),
})
})
// New logging endpoint to test log levels
api.POST("/log-test", func(c *gin.Context) {
var request struct {
Level string `json:"level"`
Message string `json:"message"`
}
if err := c.ShouldBindJSON(&request); err != nil {
logger.Warn("Invalid JSON in log-test request",
"client_ip", c.ClientIP(),
"error", err.Error(),
)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid JSON",
})
return
}
// Log based on requested level
switch request.Level {
case "debug":
logger.Debug(request.Message, "client_ip", c.ClientIP())
case "info":
logger.Info(request.Message, "client_ip", c.ClientIP())
case "warn":
logger.Warn(request.Message, "client_ip", c.ClientIP())
case "error":
logger.Error(request.Message, "client_ip", c.ClientIP())
default:
logger.Info(request.Message, "client_ip", c.ClientIP(), "level", request.Level)
}
c.JSON(http.StatusOK, gin.H{
"status": "logged",
"level": request.Level,
"message": request.Message,
})
})
}
// Get port from environment or use default
@@ -82,6 +282,37 @@ func main() {
port = "8080"
}
// Start server
r.Run(":" + port)
// Start server with structured logging
logger.Info("Server starting",
"port", port,
"mode", gin.Mode(),
)
ctx := context.Background()
go func() {
// Graceful shutdown handling
<-ctx.Done()
logger.Info("Shutdown signal received, stopping server...")
}()
err := r.Run(":" + port)
if err != nil {
logger.Error("Server failed to start", "error", err.Error())
os.Exit(1)
}
}
// isSensitiveEnvVar checks if an environment variable contains sensitive information
func isSensitiveEnvVar(env string) bool {
sensitivePrefixes := []string{
"PASSWORD", "SECRET", "KEY", "TOKEN", "CREDENTIAL",
"AWS_", "GITHUB_", "DOCKER_", "KUBERNETES_",
}
for _, prefix := range sensitivePrefixes {
if len(env) >= len(prefix) && env[:len(prefix)] == prefix {
return true
}
}
return false
}

View File

@@ -2,7 +2,7 @@
BIN_DIR=bin
APP_NAME=hello-api
VERSION=1.0.27
VERSION=1.0.35
build:
mkdir -p $(BIN_DIR)

21
scripts/quick-build.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
if [ ! -f go.mod ]; then
echo "go.mod not found in current directory"
exit 1
fi
# Проверяем, изменились ли зависимости
if [ ! -f go.sum ] || ! go mod verify >/dev/null 2>&1; then
echo "Dependencies changed, downloading..."
go mod download
fi
# Собираем для указанной платформы
if [ -n "$1" ] && [ -n "$2" ]; then
echo "Building for $1 $2..."
GOOS=$1 GOARCH=$2 go build -o "bin/hello-api-$1-$2" main.go
else
echo "Building for current platform..."
go build -o bin/hello-api main.go
fi

View File

@@ -17,7 +17,7 @@ get_version_interactive() {
echo ""
# Запрашиваем новую версию
read -p "Введите новую версию (формат X.Y.Z): " VERSION
read -r -p "Введите новую версию (формат X.Y.Z): " VERSION
# Проверяем, что версия не пустая
if [ -z "$VERSION" ]; then
@@ -36,6 +36,9 @@ get_version_interactive() {
echo "📝 Подтверждение:"
echo " Текущая версия: $CURRENT_VERSION"
echo " Новая версия: $VERSION"
if [ "$CURRENT_VERSION" == "$VERSION" ]; then
echo "⚠️ Новая версия совпадает с текущей. Возможно будет обновлен тег ..."
fi
echo ""
read -r -p "Продолжить? (y/N): " CONFIRM
@@ -68,11 +71,27 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
exit 1
fi
# Проверяем, что нет незакоммиченных изменений
# if ! git diff-index --quiet HEAD --; then
# echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их."
# exit 1
# fi
# Проверяем, существует ли уже тег с такой версией
if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
echo "⚠️ Тег v$VERSION уже существует!"
echo ""
read -r -p "Обновить существующий тег? (y/N): " UPDATE_TAG
if [[ ! $UPDATE_TAG =~ ^[Yy]$ ]]; then
echo "❌ Обновление тега отменено"
exit 0
fi
echo "🔄 Обновляем существующий тег..."
# Удаляем локальный тег
git tag -d "v$VERSION" 2>/dev/null || true
# Удаляем удаленный тег
git push origin ":refs/tags/v$VERSION" 2>/dev/null || true
echo "✅ Старый тег удален"
fi
# Обновляем версию в main.go
echo "📝 Обновляем версию в main.go..."