CI/CD для Go проектов с GitHub Actions: полное руководство
Автоматизация процессов сборки, тестирования и развертывания — неотъемлемая часть современной разработки на Go (Golang). GitHub Actions предоставляет мощный и гибкий инструментарий для построения пайплайнов CI/CD прямо в вашем репозитории. В этой статье мы разберем, как настроить полный цикл непрерывной интеграции и доставки для Go-проекта, от простого тестирования до автоматического деплоя.
Что такое CI/CD и почему это важно для Go?
CI (Continuous Integration) — это практика частого слияния кода в основную ветку с автоматической сборкой и тестированием. CD (Continuous Delivery/Deployment) — автоматическое развертывание готового приложения.
Для Go-проектов CI/CD особенно полезен, так как:
- Go компилируется в статический бинарник, что упрощает деплой
- Быстрое тестирование благодаря нативной скорости Go
- Автоматическая проверка линтерами и форматирования (
gofmt,golangci-lint) - Возможность мультиплатформенной сборки (Linux, macOS, Windows)
Основы GitHub Actions для Go
GitHub Actions использует YAML-файлы в директории .github/workflows. Каждый файл описывает один или несколько workflows (пайплайнов).
Рассмотрим базовый workflow для Go-проекта:
name: Go CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Run tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.out
Этот workflow запускается при пуше в main/develop и на pull request. Он устанавливает Go, кэширует зависимости, запускает тесты с флагами -race (детектор гонок) и -coverprofile (покрытие кода).
Продвинутые настройки: линтеры и мультиплатформенная сборка
Для поддержания качества кода добавим проверку линтером. golangci-lint — стандарт де-факто для Go.
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --timeout=5m ./...
Мультиплатформенная сборка полезна, если ваше приложение должно работать на разных ОС:
build:
needs: [test, lint]
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
goarch: [amd64, arm64]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build
run: |
GOARCH=${{ matrix.goarch }} go build -v -o myapp-${{ matrix.os }}-${{ matrix.goarch }} ./cmd/app
Обратите внимание: для Windows нужно будет скорректировать имя выходного файла (добавить .exe). Используйте if runner.os == 'Windows' или задайте имя через переменные.
Автоматический деплой Go-приложения
Для деплоя рассмотрим два популярных сценария: публикация Docker-образа и загрузка бинарника на сервер.
Сценарий 1: Docker Hub
deploy-docker:
needs: [build]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/myapp:latest
${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }}
Для работы потребуется Dockerfile в корне проекта. Пример минимального Dockerfile для Go:
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o myapp ./cmd/app
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
Сценарий 2: Прямая загрузка на сервер через SSH
deploy-ssh:
needs: [build]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: myapp-linux-amd64
- name: Deploy via SSH
uses: easingthemes/ssh-deploy@v5
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
SOURCE: "myapp-linux-amd64"
TARGET: "/opt/myapp/"
ARGS: "-rltgoDzvO"
- name: Restart service
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
sudo systemctl restart myapp
echo "Service restarted successfully"