Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM mcr.microsoft.com/devcontainers/go:dev-1.25-trixie

SHELL ["/bin/bash", "-c"]

RUN apt-get update && apt-get -y install --no-install-recommends \
bash \
make \
git \
curl \
ca-certificates \
findutils \
coreutils

RUN curl -sSfL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin
7 changes: 7 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "test-backend",
"build": {
"dockerfile": "Dockerfile"
},
"postCreateCommand": "go version && task --version && task install-golangci-lint && task install-formatters"
}
104 changes: 104 additions & 0 deletions .github/scripts/extract-versions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/bin/bash

# Скрипт для извлечения версий инструментов из Taskfile.yml
# Использование: .github/scripts/extract-versions.sh

# Путь к Taskfile.yml
TASKFILE="Taskfile.yml"

# Проверка наличия файла
if [ ! -f "$TASKFILE" ]; then
echo "Ошибка: Файл $TASKFILE не найден" >&2
exit 1
fi

# Извлечение всех переменных из секции vars
echo "Извлекаем переменные из Taskfile.yml:"

# Определяем начало и конец секции vars
VARS_START=$(grep -n "^vars:" "$TASKFILE" | cut -d: -f1)
if [ -z "$VARS_START" ]; then
echo "Ошибка: секция vars не найдена в $TASKFILE" >&2
exit 1
fi

VARS_START=$((VARS_START + 1))

# Ищем следующую секцию после vars или конец файла
NEXT_SECTION=$(tail -n +$VARS_START "$TASKFILE" | grep -n "^[a-z]" | head -1 | cut -d: -f1)
if [ -n "$NEXT_SECTION" ]; then
VARS_END=$((VARS_START + NEXT_SECTION - 2))
else
VARS_END=$(wc -l < "$TASKFILE")
fi

# Извлекаем все строки из секции vars
VARS_SECTION=$(sed -n "${VARS_START},${VARS_END}p" "$TASKFILE")

# Инициализируем ассоциативный массив для хранения переменных
declare -A VARS

# Извлекаем имя и значение каждой переменной
while IFS= read -r line; do
# Пропускаем пустые строки и строки с комментариями
if [[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]]; then
continue
fi

# Извлекаем имя и значение
if [[ "$line" =~ ^[[:space:]]*([A-Z_0-9]+):\ *\'([^\']*)\' ]]; then
var_name=${BASH_REMATCH[1]}
var_value=${BASH_REMATCH[2]}
VARS["$var_name"]="$var_value"
echo "- $var_name: ${VARS[$var_name]}"
elif [[ "$line" =~ ^[[:space:]]*([A-Z_0-9]+):\ *\"([^\"]*)\" ]]; then
var_name=${BASH_REMATCH[1]}
var_value=${BASH_REMATCH[2]}
VARS["$var_name"]="$var_value"
echo "- $var_name: ${VARS[$var_name]}"
elif [[ "$line" =~ ^[[:space:]]*([A-Z_0-9]+):\ *(.*) ]]; then
var_name=${BASH_REMATCH[1]}
var_value=${BASH_REMATCH[2]}
VARS["$var_name"]="$var_value"
echo "- $var_name: ${VARS[$var_name]}"
fi
done <<< "$VARS_SECTION"

# Находим список модулей
if [ -n "${VARS[MODULES]}" ]; then
MODULES="${VARS[MODULES]}"
echo "- найдены модули: $MODULES"
else
# Если не найдено в vars, пытаемся найти в другом месте (для обратной совместимости)
MODULES=$(sed -n 's/.*MODULES: \(.*\)/\1/p' "$TASKFILE" | head -1)
echo "- модули (из старого формата): $MODULES"
fi

# Установка переменных GitHub Actions
if [ -n "$GITHUB_ENV" ]; then
echo "Устанавливаем переменные в GITHUB_ENV:"
# Экспортируем все переменные
for var_name in "${!VARS[@]}"; do
echo "$var_name=${VARS[$var_name]}" >> $GITHUB_ENV
echo " $var_name -> GITHUB_ENV"
done
# Для совместимости добавляем MODULES отдельно, если оно не в vars
if [ -z "${VARS[MODULES]}" ] && [ -n "$MODULES" ]; then
echo "MODULES=$MODULES" >> $GITHUB_ENV
echo " MODULES -> GITHUB_ENV"
fi
fi

if [ -n "$GITHUB_OUTPUT" ]; then
echo "Устанавливаем переменные в GITHUB_OUTPUT:"
# Экспортируем все переменные
for var_name in "${!VARS[@]}"; do
echo "$var_name=${VARS[$var_name]}" >> $GITHUB_OUTPUT
echo " $var_name -> GITHUB_OUTPUT"
done
# Для совместимости добавляем MODULES отдельно, если оно не в vars
if [ -z "${VARS[MODULES]}" ] && [ -n "$MODULES" ]; then
echo "MODULES=$MODULES" >> $GITHUB_OUTPUT
echo " MODULES -> GITHUB_OUTPUT"
fi
fi
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
extract-vars:
name: Extract Variables from Taskfile
runs-on: ubuntu-latest
outputs:
go-version: ${{ steps.extract.outputs.GO_VERSION }}
golangci-lint-version: ${{ steps.extract.outputs.GOLANGCI_LINT_VERSION }}
modules: ${{ steps.extract.outputs.MODULES }}

steps:
- name: Checkout code
uses: actions/checkout@v4.2.2

- name: Extract variables from Taskfile
id: extract
run: |
chmod +x .github/scripts/extract-versions.sh
.github/scripts/extract-versions.sh

lint:
needs: extract-vars
uses: ./.github/workflows/lint-reusable.yml
with:
modules: ${{ needs.extract-vars.outputs.modules }}
go-version: ${{ needs.extract-vars.outputs.go-version }}
golangci-lint-version: ${{ needs.extract-vars.outputs.golangci-lint-version }}
40 changes: 40 additions & 0 deletions .github/workflows/lint-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Lint Reusable

on:
workflow_call:
inputs:
modules:
required: true
type: string
go-version:
required: true
type: string
golangci-lint-version:
required: true
type: string

jobs:
golangci-lint:
name: Lint all Go modules
runs-on: ubuntu-latest

steps:
- name: 📦 Checkout code
uses: actions/checkout@v4.2.2

- name: 🛠 Set up Go
uses: actions/setup-go@v5.4.0
with:
go-version: ${{ inputs.go-version }}

- name: 🐾 Show go.work (debug)
run: cat go.work || echo "❗ go.work not found"

- name: 📌 Install Task
uses: arduino/setup-task@v2.0.0

- name: ✅ Run golangci-lint via Taskfile
env:
MODULES: ${{ inputs.modules }}
GOLANGCI_LINT_VERSION: ${{ inputs.golangci-lint-version }}
run: task lint
30 changes: 30 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# env file
.env
/.idea
/.vscode
/.cursor
.DS_Store

/bin
node_modules
/shared/api/bundles/
/coverage/
104 changes: 104 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
version: "2" # Версия формата конфигурационного файла (v2 — для golangci-lint 2.x)

run: # Параметры запуска линтера
timeout: 5m # Максимальное время выполнения анализа
relative-path-mode: gomod # Пути к файлам интерпретируются относительно корня go-модуля
issues-exit-code: 1 # Код выхода = 1, если найдены ошибки (полезно для CI)
tests: true # Анализировать также _test.go файлы
modules-download-mode: readonly # Не разрешать загрузку зависимостей во время анализа (безопасно и быстрее на CI)

output: # Настройки вывода результатов линтинга
formats:
text: # Формат вывода в терминал
print-linter-name: true # Показывать имя линтера рядом с каждым сообщением
print-issued-lines: true # Показывать строку кода, где найдена проблема
colors: true # Использовать цветной вывод (для лучшей читаемости)

issues: # Общие настройки для обработки проблем
max-issues-per-linter: 0 # Без лимита на количество ошибок от одного линтера
max-same-issues: 0 # Без лимита на количество одинаковых ошибок
uniq-by-line: true # Отображать максимум одну ошибку на строку (избегает спама от разных линтеров по одной строке)

linters: # Список активных и отключённых линтеров
default: standard # Использовать стандартный набор линтеров golangci-lint
enable: # Явно включенные дополнительные линтеры
- errcheck # Проверяет, что ошибки не игнорируются
- staticcheck # Глубокий статический анализ (includes gosimple, stylecheck)
- govet # Встроенный в Go инструмент анализа (ловит потенциальные баги)
- gocritic # Набор продвинутых правил для улучшения качества кода
- revive # Проверяет стиль кода, оформление, названия
- unused # Находит неиспользуемые переменные, типы, функции
- gosec # Проверка на уязвимости и небезопасные практики
- depguard # Запрещает импорт указанных пакетов (контроль зависимостей)
- bodyclose # Проверяет, что закрывается `resp.Body` у HTTP-запросов
- asciicheck # Предупреждает о не-ASCII символах в коде
- cyclop # Контролирует цикломатическую сложность функций
- dupl # Находит дублированные фрагменты кода
- ineffassign # Обнаруживает неиспользуемые присваивания
- unparam # Находит неиспользуемые параметры функций
- errorlint # Рекомендует использовать `errors.Is` / `errors.As` вместо прямых сравнений
- errname # Проверяет имена переменных/типов ошибок (должны содержать "Err")
- forbidigo # Запрещает определённые вызовы или конструкции по regex-паттернам
- contextcheck # Проверяет, что `context.Context` передаётся в методы/функции
- containedctx # Предупреждает, если `context.Context` сохраняется в структуре (плохо)
disable: # Линтеры, которые намеренно отключены
- gocyclo # Старый линтер сложности (заменён на cyclop)
- lll # Проверка длины строк (часто шумная и мешает)

exclusions: # Исключения из анализа
generated: strict # Игнорировать сгенерированные файлы (по "// Code generated ... DO NOT EDIT.")
rules: # Локальные исключения по пути/файлу/линерам
- path: _test\.go # Для всех тестов:
linters:
- cyclop # Не проверять сложность тестов
- dupl # Не проверять дублирование в тестах
- gosec # Не искать уязвимости в тестах

settings: # Индивидуальные настройки для конкретных линтеров

gosec:
# Включаем все правила, включая экспериментальные
config:
global:
audit: true # Активировать все правила безопасности
show-ignored: true # Показывать проигнорированные проблемы в комментариях
severity: "medium" # Минимальная важность проблем, high/medium/low
confidence: "medium" # Минимальный уровень уверенности, high/medium/low

cyclop:
max-complexity: 20 # Допустимая сложность функции (больше 10, но не слишком жёстко)

depguard:
rules:
main:
deny:
- pkg: io/ioutil # Запрет использовать устаревший пакет
desc: "Использование устаревшего пакета io/ioutil запрещено (замените вызовы на аналоги из пакетов os/io)"

revive:
severity: warning # Нарушения revive будут как предупреждения, а не ошибки

forbidigo:
exclude-godoc-examples: true # Не применять запреты к Godoc-примерам
analyze-types: true # Искать запреты и в типах/константах, а не только в функциях
forbid: # Список запрещённых паттернов
- pattern: '^fmt\.Print.*$' # Запрет использования fmt.Print*, fmt.Println и т.д.
msg: "Запрещено напрямую использовать fmt.Print* для логирования (вместо этого используйте структурированный логгер)"
- pattern: '^time\.Sleep$' # Запрет использования time.Sleep
msg: "Запрещено использовать time.Sleep в продакшен-коде (используй таймеры/контекст)"
- pattern: '^http\.DefaultClient$' # Запрет использования небезопасного http.DefaultClient
msg: "Не используй http.DefaultClient (нет таймаутов); создай *http.Client с таймаутами"

formatters: # Форматтеры кода (проверка стиля)
enable:
- gofumpt # Строгая версия gofmt с дополнительными правилами
- gci # Форматтер импортов (гибкая альтернатива goimports)
settings:
gofumpt:
extra-rules: true # Включить дополнительные строгие правила (например, удаление лишних пустых строк)
gci:
sections: # Порядок группировки импортов
- Standard # Стандартная библиотека
- Default # Сторонние зависимости
- Prefix(github.com/qyrlabs/test-backend) # Локальные импорты проекта
no-inline-comments: false # Разрешить комментарии после импортов (true — запретит)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# test-backend
Loading
Loading