Compare commits
1 Commits
v1.0.35
...
builder-1.
Author | SHA1 | Date | |
---|---|---|---|
a3867ba3de |
@@ -5,103 +5,10 @@ on:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
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
|
||||
# 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
|
||||
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 - 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
|
||||
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
|
||||
|
||||
echo "Listing bin directory..."
|
||||
ls -la bin
|
||||
|
||||
- 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"
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
echo "Release created successfully"
|
||||
|
||||
create-docker-image:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: docker:28.3.2-dind
|
||||
needs: create-release
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
run: |
|
||||
@@ -122,9 +29,9 @@ jobs:
|
||||
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 create --use
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
- name: Login to Docker Hub
|
||||
@@ -134,7 +41,6 @@ jobs:
|
||||
- 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 \
|
||||
@@ -142,58 +48,77 @@ jobs:
|
||||
--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:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: docker:28.3.2-dind
|
||||
# image: golang:1.21
|
||||
image: ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest
|
||||
needs: create-docker-image
|
||||
steps:
|
||||
- name: Create Release Branch
|
||||
- name: Checkout repository
|
||||
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
|
||||
cd hello_gitea
|
||||
git checkout ${{ github.ref }}
|
||||
|
||||
# Configure git
|
||||
echo "Configuring git..."
|
||||
git config user.email "info@direct-dev.ru"
|
||||
git config user.name "Direct-Dev-Robot"
|
||||
- name: Setup Go
|
||||
run: |
|
||||
# Install jq for JSON parsing
|
||||
apt-get update && apt-get install -y jq
|
||||
git --version
|
||||
go version
|
||||
jq --version
|
||||
|
||||
# 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
|
||||
- name: Build all binaries
|
||||
run: |
|
||||
cd hello_gitea
|
||||
mkdir -p bin
|
||||
|
||||
# Reset to the tag commit
|
||||
echo "Resetting to the tag commit ${{ github.ref_name }} ..."
|
||||
git reset --hard ${{ github.ref_name }}
|
||||
# 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
|
||||
|
||||
# Push changes to release branch
|
||||
echo "Pushing changes to release branch..."
|
||||
git push origin release --force
|
||||
echo "Changes pushed to release branch successfully"
|
||||
# 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
|
||||
|
@@ -1,4 +1,4 @@
|
||||
name: Build Builder Docker Image
|
||||
name: Release Build
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
@@ -21,31 +21,26 @@ 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
|
||||
echo "setup buildx"
|
||||
docker buildx create --name builder-builx --use
|
||||
docker buildx create --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 ..."
|
||||
# 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 }} \
|
||||
@@ -54,6 +49,3 @@ jobs:
|
||||
-f Dockerfile.builder \
|
||||
.
|
||||
|
||||
- name: Remove buildx
|
||||
run: |
|
||||
docker buildx rm builder-builx
|
@@ -1,5 +1,5 @@
|
||||
# Build stage
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
|
||||
|
||||
# Install git and ca-certificates
|
||||
RUN apk --no-cache add git ca-certificates
|
||||
|
@@ -1,32 +0,0 @@
|
||||
# Используем образ 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/*
|
||||
|
||||
# Создаем рабочую директорию
|
||||
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"]
|
13
Dockerfile_builder
Normal file
13
Dockerfile_builder
Normal file
@@ -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"]
|
250
README.md
250
README.md
@@ -1,15 +1,12 @@
|
||||
# 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
|
||||
@@ -40,8 +37,6 @@ go run main.go
|
||||
|
||||
## 🔧 Конфигурация
|
||||
|
||||
### Основные настройки
|
||||
|
||||
Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения:
|
||||
|
||||
```bash
|
||||
@@ -49,98 +44,46 @@ 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.33"
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /healthz
|
||||
|
||||
### GET /health
|
||||
Health check endpoint
|
||||
|
||||
**Ответ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "1.0.33"
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/v1/info
|
||||
|
||||
Информация о сервисе
|
||||
|
||||
**Ответ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"service": "hello-api",
|
||||
"status": "running",
|
||||
"version": "1.0.33"
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/v1/echo
|
||||
|
||||
Echo endpoint - возвращает отправленное сообщение
|
||||
|
||||
**Запрос:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello from client!"
|
||||
@@ -148,149 +91,18 @@ Echo endpoint - возвращает отправленное сообщение
|
||||
```
|
||||
|
||||
**Ответ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"echo": "Hello from client!",
|
||||
"version": "1.0.33"
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 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.24+
|
||||
- Go 1.21+
|
||||
- Gin framework
|
||||
|
||||
### Сборка
|
||||
@@ -316,65 +128,27 @@ 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/healthz
|
||||
curl http://localhost:8080/health
|
||||
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
|
||||
|
||||
При создании тега (например, `v1.1.20`) автоматически:
|
||||
При создании тега (например, `v1.1.0`) автоматически:
|
||||
|
||||
1. Собираются бинарники для всех платформ
|
||||
2. Создается Docker образ для Linux AMD64/ARM64
|
||||
3. Образ публикуется в Docker Hub
|
||||
4. Создается релиз в Gitea с бинарниками
|
||||
|
||||
Дополнительно `.gitea/workflows/build_build.yaml` предназначен для автоматизации процесса сборки и публикации Docker-образов билдера - то есть образа который будет использоваться в основном процессе сборки и релиза. Этот workflow запускается (триггерится) автоматически при пуше тега, начинающегося с `builder-` (например, `builder-v1.2.3`), в репозиторий на сервере Gitea.
|
||||
|
||||
Когда такой тег появляется, workflow выполняет следующие задачи:
|
||||
|
||||
- Клонирует репозиторий и переключается на соответствующую версию кода.
|
||||
- Настраивает окружение для сборки Docker-образов с поддержкой мультиплатформенности (amd64 и arm64).
|
||||
- Выполняет аутентификацию в Docker Hub.
|
||||
- Собирает и публикует Docker-образы для разных архитектур (tag DOCKERHUB_USERNAME/my-build-golang-runnerr:builder-v1.2.3 и tag DOCKERHUB_USERNAME/my-build-golang-runner:latest.
|
||||
- Для сборки используется специальный Dockerfile (`Dockerfile.builder`) для создания образа билдера.
|
||||
|
||||
Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей.
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
MIT Licens
|
||||
MIT License
|
BIN
bin/hello-api-1.0.0
Executable file
BIN
bin/hello-api-1.0.0
Executable file
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag "${DOCKERHUB_USERNAME:-kuznetcovay}"/my-build-golang-runner:latest \
|
||||
--tag ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest \
|
||||
--push \
|
||||
-f Dockerfile_for_runner_image \
|
||||
.
|
@@ -1,13 +1,13 @@
|
||||
# Настройка Gitea Actions для Go проекта: Полное руководство
|
||||
|
||||
```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
|
||||
```
|
||||
---
|
||||
**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 @@ version: 1.0.0
|
||||
6. [Тестирование и запуск](#тестирование-и-запуск)
|
||||
7. [Мониторинг и отладка](#мониторинг-и-отладка)
|
||||
8. [Заключение](#заключение)
|
||||
A. [Инфраструктура](#инфраструктура)
|
||||
B. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s)
|
||||
C. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере)
|
||||
9. [Инфраструктура](#инфраструктура)
|
||||
10. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s)
|
||||
11. [Настройка 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("/healthz", func(c *gin.Context) {
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"version": version,
|
||||
@@ -197,6 +197,7 @@ CMD ["./hello-api"]
|
||||
базововго образа для каждого из этапов использовать предварительно собранный образ со всем необходимым.
|
||||
При каждом запуске workflow будет запускаться новый контейнер docker in docker и там запускаться построение образа естественно кэшей не будет или надо как то придумывать как их хранить на ранере и пробрасывать в dind. Ну или руками сделать нужные образы и использоват их в Dockerfile. Правда пулиться они опять же будут походу каждый раз. Короче говоря тут есть чем поразбираться и пооптимизировать ...
|
||||
|
||||
|
||||
## Настройка Gitea Actions
|
||||
|
||||
### Включение Actions в Gitea
|
||||
@@ -241,11 +242,55 @@ 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
|
||||
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
|
||||
image: golang:1.21
|
||||
needs: create-docker-image
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
run: |
|
||||
@@ -256,7 +301,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
|
||||
@@ -313,121 +358,37 @@ 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 запускается при пуше в Gitea тега, начинающегося с `v*` (например, `v1.1.29`)
|
||||
- Workflow запускается при создании тега, начинающегося с `v*` (например, `v1.0.0`)
|
||||
|
||||
**Jobs:**
|
||||
|
||||
1 **create-release:**
|
||||
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)
|
||||
|
||||
- Эта таска запускается первой и не требует никаких зависимых тасков выполненными
|
||||
- Использует контейнер собранный на базе golang:1.24 для сборки бинарников (image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest)
|
||||
чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всей gitea в настройках или в настройках конкретного репозитория
|
||||
я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ...
|
||||
|
||||
2. **create-release:**
|
||||
- Запускается после успешной сборки Docker образов
|
||||
- Использует Go контейнер для сборки бинарников (image: golang:1.21)
|
||||
- Собирает бинарники для всех платформ (Linux, Windows, macOS)
|
||||
- Создает архивы с бинарниками
|
||||
- Создает релиз с именем текущей версии через Gitea API
|
||||
- Создает релиз через Gitea API
|
||||
- Загружает бинарники как assets релиза
|
||||
|
||||
для работы данного job используется кастомный образ - если бы мы использовали просто golang:1.24, то при каждом запуске данного job необходимо было бы скачивать jq и устанавливаться в контейнере (возможно со временем, что-то еще потребовалось бы ...)
|
||||
при каждом запуске, как я понимаю, данного job будет скачиваться и устанавливаться в контейнере, созданном на базе golang:1.21
|
||||
|
||||
```yaml
|
||||
- name: Setup Go container
|
||||
- name: Setup Go
|
||||
run: |
|
||||
# Install jq for JSON parsing
|
||||
apt-get update && apt-get install -y jq
|
||||
@@ -436,133 +397,28 @@ jobs:
|
||||
jq --version
|
||||
```
|
||||
|
||||
**решение в лоб:** применено, чтобы убрать эту повторяющуюся работу при каждом выполнении workflow - сделан свой образ - "${DOCKERHUB_USERNAME}"/my-build-golang-runner:latest
|
||||
решение как говорится в лоб: чтобы убрать эту работу при каждом выполнении workflow надо сделать свой образ
|
||||
|
||||
``` Dockerfile
|
||||
# базовый образ
|
||||
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"]
|
||||
# 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:-defaultdockeruser}/my-build-golang-runner:latest \
|
||||
--tag ${DOCKERHUB_USERNAME}/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`
|
||||
|
||||
|
||||
## Настройка секретов
|
||||
|
||||
@@ -585,14 +441,12 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
### Создание токенов
|
||||
|
||||
**Gitea Token:**
|
||||
|
||||
1. Перейдите в настройки профиля → "Applications"
|
||||
2. Создайте новый токен с правами на репозиторий
|
||||
3. Скопируйте токен
|
||||
4. В разделе Actions репозитория создайте секрет уровня репозитория - я создал с именем GITEATOKEN (такой не даст сделать: GITEA_TOKEN)
|
||||
|
||||
**Docker Hub Token:**
|
||||
|
||||
1. Войдите в Docker Hub
|
||||
2. Перейдите в Account Settings → Security
|
||||
3. Создайте новый Access Token
|
||||
@@ -603,9 +457,9 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
### Локальное тестирование
|
||||
|
||||
1 **Проверим синтаксис workflow:**
|
||||
1. **Проверим синтаксис workflow:**
|
||||
|
||||
ну если мы в ide, то наверное все хорошо и уже автоматом отформатировано ...
|
||||
ну если мы в ide то наверное все автоматом отформатировано
|
||||
но тем не менее ...
|
||||
|
||||
```bash
|
||||
@@ -613,10 +467,7 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
yamllint .gitea/workflows/build.yaml
|
||||
```
|
||||
|
||||
2 **Протестируем сборку локально:**
|
||||
|
||||
пока ручное тестирование
|
||||
|
||||
2. **Протестируем сборку локально:**
|
||||
```bash
|
||||
# Сборка для текущей платформы
|
||||
go build -o hello-api main.go
|
||||
@@ -626,8 +477,7 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
GOOS=linux GOARCH=arm64 go build -o hello-api-linux-arm64 main.go
|
||||
```
|
||||
|
||||
3 **Тестирование Docker образа:**
|
||||
|
||||
3. **Тестирование Docker образа:**
|
||||
```bash
|
||||
# Сборка образа
|
||||
docker build -t hello-api:test .
|
||||
@@ -636,23 +486,19 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
docker run -p 8080:8080 hello-api:test
|
||||
|
||||
# Тестирование API
|
||||
curl http://localhost:8080/healthz
|
||||
curl http://localhost:8080/health
|
||||
```
|
||||
|
||||
тут надо погонять curl по эндпойнтам, убедиться что приложение работает (пока тоже вручную)
|
||||
|
||||
### Запуск Actions
|
||||
|
||||
1 **Создаем тег вручную или скриптом (`scripts/release-interactive.sh`):**
|
||||
|
||||
1. **Создаем тег:**
|
||||
```bash
|
||||
|
||||
git tag v1.1.20
|
||||
git push origin v1.1.20
|
||||
|
||||
# текст скрипта - запуск можно сделать через make
|
||||
#release-interactive:
|
||||
# @./scripts/release-interactive.sh
|
||||
# или скрипт
|
||||
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
@@ -673,7 +519,7 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
echo ""
|
||||
|
||||
# Запрашиваем новую версию
|
||||
read -r -p "Введите новую версию (формат X.Y.Z): " VERSION
|
||||
read -p "Введите новую версию (формат X.Y.Z): " VERSION
|
||||
|
||||
# Проверяем, что версия не пустая
|
||||
if [ -z "$VERSION" ]; then
|
||||
@@ -693,7 +539,7 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
echo " Текущая версия: $CURRENT_VERSION"
|
||||
echo " Новая версия: $VERSION"
|
||||
echo ""
|
||||
read -r -p "Продолжить? (y/N): " CONFIRM
|
||||
read -p "Продолжить? (y/N): " CONFIRM
|
||||
|
||||
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
|
||||
echo "❌ Релиз отменен"
|
||||
@@ -748,24 +594,6 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
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 .
|
||||
@@ -783,15 +611,13 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
echo "🎉 Релиз v$VERSION успешно завершен!"
|
||||
echo "📋 Выполненные действия:"
|
||||
echo " - Обновлена версия в main.go"
|
||||
echo " - Обновлена версия в makefile"
|
||||
echo " - Создан коммит с сообщением 'Release v$VERSION'"
|
||||
echo " - Создан тег v$VERSION"
|
||||
echo " - Изменения отправлены в удаленный репозиторий"
|
||||
|
||||
```
|
||||
|
||||
2 **Мониторинг выполнения:**
|
||||
|
||||
2. **Мониторинг выполнения:**
|
||||
- Переходим в репозиторий → "Actions"
|
||||
- Находим запущенный workflow
|
||||
- Отслеживаем выполнение каждого job
|
||||
@@ -800,54 +626,46 @@ docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
После успешного выполнения:
|
||||
|
||||
1 **Релиз** будет создан в Gitea с бинарниками:
|
||||
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`
|
||||
|
||||
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, который:
|
||||
@@ -881,13 +699,12 @@ Gitea Actions предоставляет мощные возможности д
|
||||
|
||||
### Архитектура системы
|
||||
|
||||
Этот и последующие разделы описывают инфраструктуру, на которой я проверял все описанное выше.
|
||||
Если у вас используются другие подходы, то можете пропустить чтение этих разделов
|
||||
или ознакомиться для общего развития.
|
||||
Этот и последующие разделы описывают инфраструктуру на которой я проверял все описанное выше.
|
||||
Если у вас используются другие подходы, то можете пропустить их или ознакомиться для общего развития.
|
||||
|
||||
Итак инфраструктура состоит из следующих компонентов:
|
||||
|
||||
``` text
|
||||
```
|
||||
Proxmox VE (ARM64)
|
||||
├── K3s Cluster
|
||||
├── Master Node
|
||||
@@ -903,34 +720,31 @@ Proxmox VE (ARM64)
|
||||
### Требования к системе
|
||||
|
||||
**Proxmox VE:**
|
||||
|
||||
- ARM64 архитектура (orangepi 5 Plus)
|
||||
- Минимум 8GB RAM (16Gb)
|
||||
- 100GB свободного места (1 Gb)
|
||||
- Поддержка LXC контейнеров (+)
|
||||
- ARM64 архитектура
|
||||
- Минимум 8GB RAM
|
||||
- 100GB свободного места
|
||||
- Поддержка LXC контейнеров
|
||||
|
||||
**K3s Cluster:**
|
||||
|
||||
- Kubernetes 1.24+
|
||||
- Helm 3.8+
|
||||
- Ingress Controller (Traefik)
|
||||
- Persistent Storage (NFS)
|
||||
- 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
|
||||
@@ -940,7 +754,6 @@ 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
|
||||
@@ -991,7 +804,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
|
||||
@@ -1001,7 +814,7 @@ kubectl port-forward -n gitea svc/gitea-http 3000:3000
|
||||
curl http://localhost:3000
|
||||
```
|
||||
|
||||
2 **Создание IngressRoute для постоянного доступа:**
|
||||
2. **Создание IngressRoute для постоянного доступа:**
|
||||
|
||||
```yaml
|
||||
# gitea-ingressroute.yaml
|
||||
@@ -1037,12 +850,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
|
||||
@@ -1051,7 +864,6 @@ Gitea установлен через Helm.
|
||||
|
||||
**Версия:** Gitea 1.24.3 (rootless)
|
||||
**Архитектура:**
|
||||
|
||||
- PostgreSQL для базы данных (развернут отдельно )
|
||||
- Valkey cluster (Redis) для кэширования
|
||||
- NFS Storage Class для persistent storage
|
||||
@@ -1080,6 +892,7 @@ helm repo update
|
||||
helm upgrade gitea gitea-charts/gitea -n gitea -f gitea-values.yaml
|
||||
```
|
||||
|
||||
|
||||
**Проверка конфигурации:**
|
||||
|
||||
```bash
|
||||
@@ -1136,31 +949,27 @@ 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
|
||||
@@ -1169,8 +978,7 @@ kubectl logs -n gitea deployment/gitea | grep -i action
|
||||
|
||||
### Настройка DNS и SSL
|
||||
|
||||
Инструкция по установке Cert-Manager не приводится - инструкций вагон и маленькая тележка -
|
||||
нет смысла повторяться:
|
||||
Cert-Manager установлен. Инструкций вагон и маленькая тележка - не смысла повторяться. Проверим конфигурацию:
|
||||
|
||||
```bash
|
||||
# Проверка Cert-Manager
|
||||
@@ -1182,13 +990,12 @@ kubectl get clusterissuer
|
||||
|
||||
1. **Настройка DNS записи:**
|
||||
|
||||
```text
|
||||
Добавить A-запись в DNS (у меня сloudflare)
|
||||
gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ...
|
||||
чтобы в итоге запрос поступил на traefik
|
||||
```bash
|
||||
# Добавить A-запись в DNS (у меня сloudflare)
|
||||
gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ... чтобы в итоге запрос поступил на traefik
|
||||
```
|
||||
|
||||
2 **Создание ClusterIssuer (если не существует):**
|
||||
2. **Создание ClusterIssuer (если не существует):**
|
||||
|
||||
```bash
|
||||
# Создание ClusterIssuer для Let's Encrypt - лучше через dns resolver
|
||||
@@ -1220,7 +1027,7 @@ EOF
|
||||
|
||||
### Доступ к Gitea
|
||||
|
||||
1 **Временный доступ через port-forward:**
|
||||
1. **Временный доступ через port-forward:**
|
||||
|
||||
```bash
|
||||
# Доступ к Gitea через port-forward
|
||||
@@ -1229,14 +1036,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
|
||||
@@ -1287,7 +1094,7 @@ spec:
|
||||
# - Network: DHCP
|
||||
```
|
||||
|
||||
2 **Настройка сети:**
|
||||
2. **Настройка сети:**
|
||||
|
||||
```bash
|
||||
# В контейнере
|
||||
@@ -1307,7 +1114,7 @@ apt update && apt upgrade -y
|
||||
apt install -y curl wget git vim htop
|
||||
```
|
||||
|
||||
2 **Установка Docker:**
|
||||
2. **Установка Docker:**
|
||||
|
||||
```bash
|
||||
# Удаление старых версий
|
||||
@@ -1344,7 +1151,7 @@ systemctl start docker
|
||||
docker --version
|
||||
```
|
||||
|
||||
3 **Установка Go:**
|
||||
3. **Установка Go:**
|
||||
|
||||
```bash
|
||||
# Скачивание Go для ARM64
|
||||
@@ -1367,7 +1174,7 @@ go version
|
||||
mkdir -p $HOME/go/{bin,src,pkg}
|
||||
```
|
||||
|
||||
4 **Установка дополнительных инструментов:**
|
||||
4. **Установка дополнительных инструментов:**
|
||||
|
||||
```bash
|
||||
# Установка build tools
|
||||
@@ -1409,7 +1216,7 @@ chmod +x /usr/local/bin/act_runner
|
||||
act_runner --version
|
||||
```
|
||||
|
||||
2 **Создание пользователя для runner:**
|
||||
2. **Создание пользователя для runner:**
|
||||
|
||||
```bash
|
||||
# Создание пользователя
|
||||
@@ -1421,7 +1228,7 @@ mkdir -p /opt/gitea-runner
|
||||
chown gitea-runner:gitea-runner /opt/gitea-runner
|
||||
```
|
||||
|
||||
3 **Настройка конфигурации runner:**
|
||||
3. **Настройка конфигурации runner:**
|
||||
|
||||
```bash
|
||||
# Переключение на пользователя runner
|
||||
@@ -1546,7 +1353,7 @@ EOF
|
||||
- Нажмите "New Runner"
|
||||
- Скопируйте токен регистрации
|
||||
|
||||
2 **Регистрация runner:**
|
||||
2. **Регистрация runner:**
|
||||
|
||||
```bash
|
||||
# В контейнере LXC под пользователем gitea-runner
|
||||
@@ -1559,7 +1366,7 @@ act_runner register \
|
||||
|
||||
```
|
||||
|
||||
3 **Создание systemd сервиса:**
|
||||
3. **Создание systemd сервиса:**
|
||||
|
||||
```bash
|
||||
# Создание файла сервиса
|
||||
@@ -1625,7 +1432,7 @@ EOF
|
||||
chmod +x /opt/gitea-runner/monitor.sh
|
||||
```
|
||||
|
||||
2 **Настройка cron для мониторинга:**
|
||||
2. **Настройка cron для мониторинга:**
|
||||
|
||||
```bash
|
||||
# Добавление в crontab
|
||||
@@ -1642,7 +1449,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
|
||||
@@ -1710,7 +1517,7 @@ EOF
|
||||
systemctl restart docker
|
||||
```
|
||||
|
||||
2 **Настройка ограничений ресурсов:**
|
||||
2. **Настройка ограничений ресурсов:**
|
||||
|
||||
```bash
|
||||
# Обновление systemd сервиса с ограничениями
|
||||
@@ -1773,6 +1580,7 @@ chmod +x /opt/gitea-runner/backup.sh
|
||||
echo "0 2 * * * /opt/gitea-runner/backup.sh" | crontab -
|
||||
```
|
||||
|
||||
|
||||
## Работа с кластером
|
||||
|
||||
### Основные команды для работы с кластером
|
||||
|
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module direct-dev-ru/hello_gitea
|
||||
|
||||
go 1.24
|
||||
go 1.21
|
||||
|
||||
require github.com/gin-gonic/gin v1.10.1
|
||||
|
||||
|
243
main.go
243
main.go
@@ -1,127 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
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...)...,
|
||||
)
|
||||
}
|
||||
const version = "1.0.0"
|
||||
|
||||
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 with custom logger
|
||||
r := gin.New()
|
||||
r.Use(logMiddleware())
|
||||
r.Use(gin.Recovery())
|
||||
// Create router
|
||||
r := gin.Default()
|
||||
|
||||
// Add middleware for CORS
|
||||
r.Use(func(c *gin.Context) {
|
||||
@@ -138,8 +31,7 @@ func main() {
|
||||
})
|
||||
|
||||
// Health check endpoint
|
||||
r.GET("/healthz", func(c *gin.Context) {
|
||||
logger.Debug("Health check requested", "client_ip", c.ClientIP())
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"version": version,
|
||||
@@ -148,10 +40,6 @@ 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,
|
||||
@@ -162,7 +50,6 @@ 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,
|
||||
@@ -176,104 +63,17 @@ 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
|
||||
@@ -282,37 +82,6 @@ func main() {
|
||||
port = "8080"
|
||||
}
|
||||
|
||||
// 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
|
||||
// Start server
|
||||
r.Run(":" + port)
|
||||
}
|
||||
|
15
makefile
15
makefile
@@ -2,7 +2,7 @@
|
||||
|
||||
BIN_DIR=bin
|
||||
APP_NAME=hello-api
|
||||
VERSION=1.0.35
|
||||
VERSION=1.0.0
|
||||
|
||||
build:
|
||||
mkdir -p $(BIN_DIR)
|
||||
@@ -17,12 +17,9 @@ test:
|
||||
# Задача для создания релиза
|
||||
# Использование: make release VERSION=1.0.25
|
||||
release:
|
||||
@if [ -z "$(VERSION)" ]; then \
|
||||
echo "Ошибка: Необходимо указать версию в формате v1.0.25"; \
|
||||
echo "Использование: make release VERSION=1.0.25"; \
|
||||
exit 1; \
|
||||
fi
|
||||
# @if [ -z "$(VERSION)" ]; then \
|
||||
# echo "Ошибка: Необходимо указать версию"; \
|
||||
# echo "Использование: make release VERSION=1.0.25"; \
|
||||
# exit 1; \
|
||||
# fi
|
||||
@./scripts/release-interactive.sh $(VERSION)
|
||||
|
||||
release-interactive:
|
||||
@./scripts/release-interactive.sh
|
@@ -1,21 +0,0 @@
|
||||
#!/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
|
@@ -17,7 +17,7 @@ get_version_interactive() {
|
||||
echo ""
|
||||
|
||||
# Запрашиваем новую версию
|
||||
read -r -p "Введите новую версию (формат X.Y.Z): " VERSION
|
||||
read -p "Введите новую версию (формат X.Y.Z): " VERSION
|
||||
|
||||
# Проверяем, что версия не пустая
|
||||
if [ -z "$VERSION" ]; then
|
||||
@@ -36,11 +36,8 @@ get_version_interactive() {
|
||||
echo "📝 Подтверждение:"
|
||||
echo " Текущая версия: $CURRENT_VERSION"
|
||||
echo " Новая версия: $VERSION"
|
||||
if [ "$CURRENT_VERSION" == "$VERSION" ]; then
|
||||
echo "⚠️ Новая версия совпадает с текущей. Возможно будет обновлен тег ..."
|
||||
fi
|
||||
echo ""
|
||||
read -r -p "Продолжить? (y/N): " CONFIRM
|
||||
read -p "Продолжить? (y/N): " CONFIRM
|
||||
|
||||
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
|
||||
echo "❌ Релиз отменен"
|
||||
@@ -71,27 +68,11 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
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
|
||||
# Проверяем, что нет незакоммиченных изменений
|
||||
# if ! git diff-index --quiet HEAD --; then
|
||||
# echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
# Обновляем версию в main.go
|
||||
echo "📝 Обновляем версию в main.go..."
|
||||
@@ -111,24 +92,6 @@ fi
|
||||
|
||||
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 .
|
||||
@@ -146,7 +109,6 @@ git push --tags
|
||||
echo "🎉 Релиз v$VERSION успешно завершен!"
|
||||
echo "📋 Выполненные действия:"
|
||||
echo " - Обновлена версия в main.go"
|
||||
echo " - Обновлена версия в makefile"
|
||||
echo " - Создан коммит с сообщением 'Release v$VERSION'"
|
||||
echo " - Создан тег v$VERSION"
|
||||
echo " - Изменения отправлены в удаленный репозиторий"
|
76
scripts/release.sh
Executable file
76
scripts/release.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Скрипт для автоматизации релиза
|
||||
# Использование: ./scripts/release.sh <version>
|
||||
# Пример: ./scripts/release.sh 1.0.25
|
||||
|
||||
set -e # Остановить выполнение при ошибке
|
||||
|
||||
# Проверка аргументов
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Ошибка: Необходимо указать версию"
|
||||
echo "Использование: $0 <version>"
|
||||
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 " - Изменения отправлены в удаленный репозиторий"
|
Reference in New Issue
Block a user