Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9687f440ff |
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: Epic
|
||||||
|
about: Большая задача/эпик — используйте для крупных фич и релизов
|
||||||
|
labels: epic
|
||||||
|
---
|
||||||
|
|
||||||
|
# Заголовок epica
|
||||||
|
|
||||||
|
Опишите цель эпика, scope и критерии приёма.
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
|
||||||
|
(Кратко опишите проблему/цель)
|
||||||
|
|
||||||
|
## Критерии приёма (GIVEN / WHEN / THEN)
|
||||||
|
|
||||||
|
- GIVEN ...
|
||||||
|
- WHEN ...
|
||||||
|
- THEN ...
|
||||||
|
|
||||||
|
## Артефакты
|
||||||
|
|
||||||
|
- OpenAPI spec (если есть)
|
||||||
|
- Proposed image tag
|
||||||
|
- Доп. доки
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
Предполагаемые owners: @backend, @devops, @qa
|
||||||
|
Оценка: (story points)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Release Request
|
||||||
|
about: Запрос на релиз
|
||||||
|
labels: release
|
||||||
|
---
|
||||||
|
|
||||||
|
# Release Request
|
||||||
|
|
||||||
|
Версия:
|
||||||
|
Image tag:
|
||||||
|
Список PRs/links:
|
||||||
|
CI status link:
|
||||||
|
Test report link:
|
||||||
|
|
||||||
|
Релиз в staging: [ ]
|
||||||
|
Релиз в prod: [ ] (требует TechLead approval)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: Task
|
||||||
|
about: Конкретная задача
|
||||||
|
labels: task
|
||||||
|
---
|
||||||
|
|
||||||
|
# Заголовок задачи
|
||||||
|
|
||||||
|
Описание:
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
- GIVEN
|
||||||
|
- WHEN
|
||||||
|
- THEN
|
||||||
|
|
||||||
|
Owner: @
|
||||||
|
Estimate (SP):
|
||||||
|
Priority: low/medium/high
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Labels for project
|
||||||
|
- name: epic
|
||||||
|
color: ffbf00
|
||||||
|
description: large feature or project
|
||||||
|
- name: backend
|
||||||
|
color: 0e8a16
|
||||||
|
description: backend related work
|
||||||
|
- name: frontend
|
||||||
|
color: 1d76db
|
||||||
|
description: frontend related work
|
||||||
|
- name: devops
|
||||||
|
color: e99695
|
||||||
|
description: infra / deployment
|
||||||
|
- name: qa
|
||||||
|
color: cfd3d7
|
||||||
|
description: testing
|
||||||
|
- name: task
|
||||||
|
color: ededed
|
||||||
|
description: generic task
|
||||||
|
- name: blocker
|
||||||
|
color: b60205
|
||||||
|
description: blocking issue
|
||||||
|
- name: release
|
||||||
|
color: 5319e7
|
||||||
|
description: release request
|
||||||
|
- name: hotfix
|
||||||
|
color: fbca04
|
||||||
|
description: urgent hotfix
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# PR Title
|
||||||
|
|
||||||
|
Описание изменений:
|
||||||
|
|
||||||
|
Checklist:
|
||||||
|
- [ ] CI green
|
||||||
|
- [ ] Unit tests passed
|
||||||
|
- [ ] Integration tests (mock) passed
|
||||||
|
- [ ] Coverage >= 70%
|
||||||
|
- [ ] Security scan: no high/critical findings
|
||||||
|
- [ ] Assigned reviewer(s)
|
||||||
|
|
||||||
|
Ссылки на связанные issue:
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# CODEOWNERS file
|
||||||
|
# Format: path @owner
|
||||||
|
|
||||||
|
/backend/ @backend
|
||||||
|
/frontend/ @frontend
|
||||||
|
/devops/ @devops
|
||||||
|
/tests/ @qa
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
|
||||||
#
|
|
||||||
# awg-dash — единый образ: AmneziaWG gateway + policy routing + REST API + Web UI.
|
|
||||||
# Задача: #18 (эпик #15). ADR: docs/adr/002-single-image-modular-monolith.md
|
|
||||||
#
|
|
||||||
# ВАЖНО: AMNEZIAWG_GO_REF / AMNEZIAWG_TOOLS_REF по умолчанию указывают на master
|
|
||||||
# и должны быть запинены на проверенный тег/коммит перед релизом (см. #45).
|
|
||||||
|
|
||||||
ARG GO_IMAGE=golang:1.25-alpine
|
|
||||||
|
|
||||||
# --- stage 1: сборка awg-утилит (Go) -----------------------------------------
|
|
||||||
FROM ${GO_IMAGE} AS go-build
|
|
||||||
|
|
||||||
ARG AMNEZIAWG_GO_REPO=https://github.com/amnezia-vpn/amneziawg-go.git
|
|
||||||
ARG AMNEZIAWG_GO_REF=master
|
|
||||||
ARG AMNEZIAWG_TOOLS_REPO=https://github.com/amnezia-vpn/amneziawg-tools.git
|
|
||||||
ARG AMNEZIAWG_TOOLS_REF=master
|
|
||||||
|
|
||||||
RUN apk add --no-cache git make gcc musl-dev linux-headers
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
RUN git clone --depth 1 --branch "${AMNEZIAWG_GO_REF}" "${AMNEZIAWG_GO_REPO}" amneziawg-go \
|
|
||||||
&& cd amneziawg-go && make && cp amneziawg-go /usr/local/bin/amneziawg-go
|
|
||||||
|
|
||||||
RUN git clone --depth 1 --branch "${AMNEZIAWG_TOOLS_REF}" "${AMNEZIAWG_TOOLS_REPO}" amneziawg-tools \
|
|
||||||
&& cd amneziawg-tools/src && make \
|
|
||||||
&& cp wg /usr/local/bin/awg \
|
|
||||||
&& if [ -f wg-quick ]; then cp wg-quick /usr/local/bin/awg-quick; fi
|
|
||||||
|
|
||||||
# --- stage 2: runtime -------------------------------------------------------
|
|
||||||
FROM alpine:3.21 AS runtime
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="awg-dash" \
|
|
||||||
org.opencontainers.image.description="AmneziaWG gateway with intelligent routing: REST API + Web UI + split-tunnel" \
|
|
||||||
org.opencontainers.image.source="http://192.168.1.111:3000/embeddevio/awg-dash"
|
|
||||||
|
|
||||||
# Сетевые утилиты: iptables/ip6tables, ipset, iproute2 (policy routing), dnsmasq (ADR-005),
|
|
||||||
# curl (HEALTHCHECK), tini (PID 1), su-exec (запуск API под непривилегированным пользователем).
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
bash curl iproute2 ipset iptables ip6tables dnsmasq \
|
|
||||||
python3 py3-pip tini su-exec \
|
|
||||||
&& addgroup -S awg \
|
|
||||||
&& adduser -S -G awg -h /app awg
|
|
||||||
|
|
||||||
ENV PATH=/opt/venv/bin:$PATH \
|
|
||||||
PYTHONUNBUFFERED=1 \
|
|
||||||
PYTHONDONTWRITEBYTECODE=1 \
|
|
||||||
PYTHONPATH=/app/backend \
|
|
||||||
AWG_CONFIG_DIR=/config \
|
|
||||||
AWG_DATA_DIR=/data \
|
|
||||||
AWG_RUN_DIR=/run/awg \
|
|
||||||
AWG_API_HOST=0.0.0.0 \
|
|
||||||
AWG_API_PORT=8080
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY requirements.txt /app/requirements.txt
|
|
||||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev python3-dev libffi-dev \
|
|
||||||
&& python3 -m venv /opt/venv \
|
|
||||||
&& /opt/venv/bin/pip install --no-cache-dir --upgrade pip \
|
|
||||||
&& /opt/venv/bin/pip install --no-cache-dir -r /app/requirements.txt \
|
|
||||||
&& apk del .build-deps
|
|
||||||
|
|
||||||
COPY backend/ /app/backend/
|
|
||||||
COPY --from=go-build /usr/local/bin/awg /usr/local/bin/awg
|
|
||||||
COPY --from=go-build /usr/local/bin/amneziawg-go /usr/local/bin/amneziawg-go
|
|
||||||
COPY deploy/entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod +x /entrypoint.sh \
|
|
||||||
&& mkdir -p /config /data /run/awg \
|
|
||||||
&& chown -R awg:awg /app /config /data /run/awg
|
|
||||||
|
|
||||||
VOLUME ["/config", "/data"]
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
# healthcheck ходит в /api/v1/health (задача #31). До реализации API контейнер
|
|
||||||
# будет помечаться как unhealthy — это ожидаемое состояние до #31.
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
||||||
CMD curl -fsS "http://127.0.0.1:${AWG_API_PORT}/api/v1/health" >/dev/null || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh"]
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Пример клиентского конфига AmneziaWG для awg-dash.
|
|
||||||
#
|
|
||||||
# Скопируйте в том /config как awg0.conf (имя файла = имя интерфейса) или
|
|
||||||
# загрузите через Web UI (задача #38). Значения ниже — заглушки, замените своими.
|
|
||||||
# Файл в репозитории не является рабочим конфигом и не содержит реальных ключей.
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
# Адрес внутри туннеля (из панели провайдера AmneziaWG)
|
|
||||||
Address = 10.8.1.2/32
|
|
||||||
|
|
||||||
# DNS: адрес должен указывать на этот шлюз, иначе не сработает DNS-split (ADR-005)
|
|
||||||
DNS = 10.8.1.1
|
|
||||||
|
|
||||||
PrivateKey = <PRIVATE_KEY>
|
|
||||||
|
|
||||||
# Обфускация AmneziaWG (Amnezia VPN: экспорт -> AmneziaWG)
|
|
||||||
Jc = 4
|
|
||||||
Jmin = 40
|
|
||||||
Jmax = 70
|
|
||||||
S1 = 0
|
|
||||||
S2 = 0
|
|
||||||
H1 = 1
|
|
||||||
H2 = 2
|
|
||||||
H3 = 3
|
|
||||||
H4 = 4
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = <SERVER_PUBLIC_KEY>
|
|
||||||
PresharedKey = <PRESHARED_KEY>
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = <SERVER_HOST>:<SERVER_PORT>
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Супервизор контейнера awg-dash (задача #18, ADR-002).
|
|
||||||
# Последовательность: preflight -> подготовка -> запуск awg-api под awg.
|
|
||||||
# Привилегированный агент awg-agent стартует отсюда же после реализации (#22).
|
|
||||||
#
|
|
||||||
# Роль процесса: все сетевые операции выполняет агент под root; здесь только
|
|
||||||
# проверяем, что нужные возможности и устройства проброшены, и падаем явной
|
|
||||||
# ошибкой, если контейнер запущен без них (а не тихо деградируем).
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
CONFIG_DIR="${AWG_CONFIG_DIR:-/config}"
|
|
||||||
DATA_DIR="${AWG_DATA_DIR:-/data}"
|
|
||||||
RUN_DIR="${AWG_RUN_DIR:-/run/awg}"
|
|
||||||
API_HOST="${AWG_API_HOST:-0.0.0.0}"
|
|
||||||
API_PORT="${AWG_API_PORT:-8080}"
|
|
||||||
ROUTING_TABLE_ID=100
|
|
||||||
ROUTING_TABLE_NAME=awg-vpn
|
|
||||||
|
|
||||||
log() { printf '[entrypoint] %s\n' "$*"; }
|
|
||||||
fail() { printf '[entrypoint] FATAL: %s\n' "$*" >&2; exit 1; }
|
|
||||||
|
|
||||||
log 'preflight'
|
|
||||||
[ -e /dev/net/tun ] || fail '/dev/net/tun не проброшен: добавьте --device=/dev/net/tun:/dev/net/tun'
|
|
||||||
iptables -t mangle -L -n >/dev/null 2>&1 || fail 'iptables недоступен: нужен --cap-add=NET_ADMIN (и/или --privileged)'
|
|
||||||
command -v awg >/dev/null 2>&1 || fail 'бинарь awg не найден в образе'
|
|
||||||
command -v amneziawg-go >/dev/null 2>&1 || log 'WARN: amneziawg-go не найден — остаётся только kernel-модуль'
|
|
||||||
command -v ipset >/dev/null 2>&1 || fail 'ipset не найден в образе'
|
|
||||||
|
|
||||||
log 'подготовка каталогов состояния'
|
|
||||||
mkdir -p "$CONFIG_DIR" "$DATA_DIR" "$RUN_DIR"
|
|
||||||
chown -R awg:awg "$CONFIG_DIR" "$DATA_DIR" "$RUN_DIR" 2>/dev/null || true
|
|
||||||
chmod 700 "$CONFIG_DIR" "$DATA_DIR" 2>/dev/null || true
|
|
||||||
|
|
||||||
mkdir -p /etc/iproute2
|
|
||||||
touch /etc/iproute2/rt_tables 2>/dev/null || true
|
|
||||||
if ! grep -q "^${ROUTING_TABLE_ID} ${ROUTING_TABLE_NAME}" /etc/iproute2/rt_tables 2>/dev/null; then
|
|
||||||
printf '%s %s\n' "$ROUTING_TABLE_ID" "$ROUTING_TABLE_NAME" >> /etc/iproute2/rt_tables \
|
|
||||||
|| log 'WARN: не удалось записать таблицу маршрутизации в /etc/iproute2/rt_tables'
|
|
||||||
fi
|
|
||||||
|
|
||||||
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 \
|
|
||||||
|| log 'WARN: net.ipv4.ip_forward не включён — задайте --sysctl net.ipv4.ip_forward=1 или --privileged (см. runbook #45)'
|
|
||||||
|
|
||||||
log "awg: $(awg --version 2>/dev/null | head -n 1 || echo 'version unknown')"
|
|
||||||
|
|
||||||
api_pid=''
|
|
||||||
if [ -f /app/backend/awg_api/app.py ]; then
|
|
||||||
log "запуск awg-api на ${API_HOST}:${API_PORT}"
|
|
||||||
su-exec awg /opt/venv/bin/uvicorn awg_api.app:app --host "$API_HOST" --port "$API_PORT" &
|
|
||||||
api_pid=$!
|
|
||||||
else
|
|
||||||
log 'WARN: awg_api/app.py ещё не реализован (задача #31) — API не запущен, контейнер работает только как агент'
|
|
||||||
fi
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
if [ -n "$api_pid" ] && kill -0 "$api_pid" 2>/dev/null; then
|
|
||||||
log 'остановка awg-api'
|
|
||||||
kill -TERM "$api_pid" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap stop INT TERM
|
|
||||||
|
|
||||||
if [ -n "$api_pid" ]; then
|
|
||||||
# ждём завершения API и прокидываем его код возврата в Docker
|
|
||||||
set +e
|
|
||||||
wait "$api_pid"
|
|
||||||
rc=$?
|
|
||||||
set -e
|
|
||||||
log "awg-api завершился с кодом $rc"
|
|
||||||
exit "$rc"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log 'активных процессов нет (agent #22 и api #31 ещё не реализованы) — ожидание сигнала'
|
|
||||||
while :; do
|
|
||||||
sleep 3600 &
|
|
||||||
wait $!
|
|
||||||
done
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# TechLead agent payload for Hermes import
|
||||||
|
id: techlead
|
||||||
|
persona: "TechLead‑bot проекта VPN Gateway"
|
||||||
|
responsibilities:
|
||||||
|
- Руководство архитектурой и принятие релизов
|
||||||
|
- Декомпозиция эпиков, утверждение DoD
|
||||||
|
- Назначение владельцев и приоритизация задач
|
||||||
|
- Эскалация blocker'ов и контроль качества
|
||||||
|
triggers:
|
||||||
|
- epic_created
|
||||||
|
- release_request
|
||||||
|
- blocker_over_48h
|
||||||
|
inputs:
|
||||||
|
- epic_description
|
||||||
|
- backlog_status
|
||||||
|
- ci_status
|
||||||
|
- test_reports
|
||||||
|
outputs:
|
||||||
|
- decomposed_issues
|
||||||
|
- release_checklist
|
||||||
|
- comments_on_issues_prs
|
||||||
|
allowed_actions:
|
||||||
|
- create_issue
|
||||||
|
- update_issue
|
||||||
|
- add_comment
|
||||||
|
- set_labels
|
||||||
|
- assign_users
|
||||||
|
decision_rules:
|
||||||
|
merge_requirements: "CI == green AND coverage >= 70% AND security_scan == low/none AND >=1 code review"
|
||||||
|
hotfix: "manual TechLead approval required (tag: hotfix-bypass)"
|
||||||
|
deploy_prod: "only after TechLead approval + smoke tests green"
|
||||||
|
escalation:
|
||||||
|
delay_hours: 48
|
||||||
|
action: "create issue 'escalation', mention owners, propose meeting within 24h"
|
||||||
|
required_permissions:
|
||||||
|
- repo: issues write
|
||||||
|
- repo: pull_request comment
|
||||||
|
- ci: read_artifacts
|
||||||
|
observability:
|
||||||
|
- action_log: /var/log/hermes/techlead_actions.log
|
||||||
|
- metrics: [epic_to_release_days, dod_compliance_rate, escalations_count]
|
||||||
|
config:
|
||||||
|
COVERAGE_THRESHOLD: 70
|
||||||
|
ESCALATION_DELAY_HOURS: 48
|
||||||
|
TECHLEAD_LOG: /var/log/hermes/techlead_actions.log
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Start TechLead Hermes agent in tmux session
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TECHLEAD_LOG=${TECHLEAD_LOG:-/var/log/hermes/techlead_actions.log}
|
||||||
|
COVERAGE_THRESHOLD=${COVERAGE_THRESHOLD:-70}
|
||||||
|
ESCALATION_DELAY_HOURS=${ESCALATION_DELAY_HOURS:-48}
|
||||||
|
TMUX_SESSION=${TMUX_SESSION:-techlead}
|
||||||
|
PROFILE=${HERMES_PROFILE:-default}
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$TECHLEAD_LOG")"
|
||||||
|
|
||||||
|
# Start tmux session with hermes interactive chat
|
||||||
|
if ! tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
|
||||||
|
tmux new-session -d -s "$TMUX_SESSION" "hermes --profile $PROFILE chat"
|
||||||
|
sleep 2
|
||||||
|
# Send initial system instruction
|
||||||
|
tmux send-keys -t "$TMUX_SESSION" "You are TechLead-bot. Load persona from /workspace/exports/techlead_payload.yaml and start monitoring issues with label 'epic'." C-m
|
||||||
|
tmux send-keys -t "$TMUX_SESSION" "COVERAGE_THRESHOLD=$COVERAGE_THRESHOLD ESCALATION_DELAY_HOURS=$ESCALATION_DELAY_HOURS TECHLEAD_LOG=$TECHLEAD_LOG" C-m
|
||||||
|
echo "Started tmux session '$TMUX_SESSION' with TechLead agent."
|
||||||
|
else
|
||||||
|
echo "Tmux session '$TMUX_SESSION' already exists. Attach with: tmux attach -t $TMUX_SESSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# One-shot alternative (no tmux):
|
||||||
|
# hermes --profile $PROFILE chat -q "You are TechLead-bot. Load persona from /workspace/exports/techlead_payload.yaml and decompose epic: ..."
|
||||||
Reference in New Issue
Block a user