ci: workflow деплоя dedinit.ru через SFTP при пуше в main (Gitea Actions)
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
name: deploy-dedinit
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: Build & SFTP Deploy
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq make python3 python3-venv python3-pip hugo
|
||||||
|
|
||||||
|
- name: Deploy via SFTP
|
||||||
|
env:
|
||||||
|
SSHPASS: ${{ secrets.SSHPASS }}
|
||||||
|
run: |
|
||||||
|
make deploy
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Design: Автодеплой dedinit.ru через Gitea Actions
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
[пользователь/агент]
|
||||||
|
│ git push (main) в gitverse.ru (источник истины)
|
||||||
|
▼
|
||||||
|
[gitverse.ru] kpa39l/dedinit.ru
|
||||||
|
│ крон-скрипт: git fetch gitverse && git push gitea (каждые ~10 мин)
|
||||||
|
▼
|
||||||
|
[gitea.nixg.ru] estorozhenko/dedinit.ru ← НЕ mirror, Actions включены
|
||||||
|
│ push в main → Gitea Actions
|
||||||
|
▼
|
||||||
|
[bigbox-runner] (gitea-act-runner v3.3.1, label docker27-bash)
|
||||||
|
│ job-контейнер docker27-bash: checkout → make build → tools/deploy_sftp.py
|
||||||
|
▼
|
||||||
|
[kpa39l.myjino.ru:2222:/dedinit.ru/] (SFTP-only, пароль из secrets.SSHPASS)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 1. Снять mirror-статус у Gitea-репозитория
|
||||||
|
|
||||||
|
Раньше репо было pull-mirror (интервал 8ч) с gitverse.ru. На mirror-репо Gitea
|
||||||
|
отключает Actions. Снимаем:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X PATCH "https://gitea.nixg.ru/api/v1/repos/estorozhenko/dedinit.ru" \
|
||||||
|
-H "Authorization: token $GITEA_NIXG_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"mirror": false}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка: `GET /repos/estorozhenko/dedinit.ru` → `"mirror":false`, `"has_actions":true`.
|
||||||
|
|
||||||
|
### 2. Синхронизация gitverse → gitea крон-скриптом
|
||||||
|
|
||||||
|
Создать `/opt/gitea.nixg.ru/sync-dedinit.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd /opt/dedinit.ru
|
||||||
|
git fetch gitverse main || git fetch origin main # gitverse = источник истины
|
||||||
|
# аккуратно: приносим refs/heads/main gitverse → gitea
|
||||||
|
git push gitea main:main
|
||||||
|
```
|
||||||
|
|
||||||
|
(детали remote gitea — см. ниже) + cron на bigbox каждые 10 минут.
|
||||||
|
|
||||||
|
### 3. Workflow `.gitea/workflows/deploy.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: [ubuntu-24.04] # label раннера bigbox-runner
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: install hugo
|
||||||
|
run: |
|
||||||
|
curl -sL https://api.github.com/repos/gohugoio/hugo/releases/latest \
|
||||||
|
| grep -oE 'browser_download_url.*_extended.*linux-amd64.tar.gz' | head -1
|
||||||
|
# распаковать hugo в /usr/local/bin
|
||||||
|
- name: build
|
||||||
|
run: make build
|
||||||
|
- name: deploy
|
||||||
|
env:
|
||||||
|
SSHPASS: ${{ secrets.SSHPASS }}
|
||||||
|
run: make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Секрет в Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X PUT "https://gitea.nixg.ru/api/v1/repos/estorozhenko/dedinit.ru/actions/secrets/SSHPASS" \
|
||||||
|
-H "Authorization: token $GITEA_NIXG_TOKEN" \
|
||||||
|
-d '{"value": "-Zp4ep747t25"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Файлы
|
||||||
|
|
||||||
|
- `.gitea/workflows/deploy.yml` — новый
|
||||||
|
- `tools/deploy_sftp.py`, `Makefile` — переиспользуются без изменений
|
||||||
|
- `/opt/gitea.nixg.ru/sync-dedinit.sh` (+ cron) — новый
|
||||||
|
- Для SSH-доступа к gitea: remote `gitea` = `git@gitverse` нет; проверю, есть ли SSH/HTTPS-доступ к gitea.nixg.ru, или поднимем родной remote через https+token.
|
||||||
|
|
||||||
|
## Команды верификации
|
||||||
|
|
||||||
|
- `curl -s https://gitea.nixg.ru/api/v1/repos/estorozhenko/dedinit.ru | grep '"has_actions"'` → true
|
||||||
|
- `openspec validate auto-deploy-dedinit-actions` → valid
|
||||||
|
- После пуша: `curl -s https://gitea.nixg.ru/api/v1/repos/estorozhenko/dedinit.ru/actions/runs | ...` → run существует
|
||||||
|
- `curl -s -o /dev/null -w '%{http_code}' https://dedinit.ru/` → 200
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
1. Вернуть mirror: `PATCH /repos/estorozhenko/dedinit.ru` → `{"mirror": true}`
|
||||||
|
2. Убрать workflow: `git rm .gitea/workflows/deploy.yml` + push
|
||||||
|
3. Удалить секрет: `DELETE /repos/estorozhenko/dedinit.ru/actions/secrets/SSHPASS`
|
||||||
|
4. Остановить крон-скрипт синхронизации
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Proposal: Автодеплой dedinit.ru через Gitea Actions при пуше в master
|
||||||
|
|
||||||
|
## Проблема
|
||||||
|
|
||||||
|
Сейчас деплой dedinit.ru на Jino (SFTP) выполняется вручную: `make build && export SSHPASS=... && make deploy`.
|
||||||
|
Это требует присутствия человека, не даёт истории деплоев и не срабатывает при пуше со стороны
|
||||||
|
пользователя (он коммитит сам, но деплой забывает).
|
||||||
|
|
||||||
|
## Что предлагается
|
||||||
|
|
||||||
|
1. Подключить Gitea Actions: на каждый push в ветку `master` репозитория
|
||||||
|
`estorozhenko/dedinit.ru` (bigbox, gitea.nixg.ru) запускать workflow:
|
||||||
|
- checkout репозитория
|
||||||
|
- сборка Hugo: `make build` (hugo --minify)
|
||||||
|
- загрузка `public/` на Jino SFTP через существующий `tools/deploy_sftp.py`
|
||||||
|
2. Для этого:
|
||||||
|
- **снять mirror-статус** у `estorozhenko/dedinit.ru` в Gitea (на зеркале Actions не работают — штатное ограничение Gitea)
|
||||||
|
- синхронизацию gitverse → gitea перенести в крон-скрипт `git fetch + git push` (вместо mirror-интервала 8ч)
|
||||||
|
- деплой-секрет SFTP (пароль Jino) завести в Gitea Actions secrets
|
||||||
|
- раннер уже стоит и работает (bigbox-runner, v3.3.1, label `docker27-bash`)
|
||||||
|
|
||||||
|
## Затронутые сервисы и файлы
|
||||||
|
|
||||||
|
- `estorozhenko/dedinit.ru` (gitea.nixg.ru) — снятие mirror, включение Actions
|
||||||
|
- `/opt/dedinit.ru/.gitea/workflows/deploy.yml` — новый workflow
|
||||||
|
- `/opt/dedinit.ru/Makefile`, `tools/deploy_sftp.py` — без изменений (переиспользуются)
|
||||||
|
- `~/.ssh/config` / ssh-ключи — не нужны (деплой через пароль, как сейчас)
|
||||||
|
- Крон на bigbox: синхронизация gitverse→gitea (замена mirror-интервала)
|
||||||
|
|
||||||
|
## План отката
|
||||||
|
|
||||||
|
- Вернуть mirror-статус репозитория в Gitea (API `PATCH /repos/{owner}/{repo}` → `mirror:true`)
|
||||||
|
- Удалить `.gitea/workflows/deploy.yml` из репозитория (или отключить в настройках Actions)
|
||||||
|
- Удалить secrets SFTP из Gitea (пароль не скомпрометирован — он и так лежит в docs/)
|
||||||
|
|
||||||
|
## Прочие решения
|
||||||
|
|
||||||
|
- Ветка по умолчанию в репо — `main`, но задание «при пуше в master» — оформим триггер на `main`
|
||||||
|
(репо уже на main; в Gitea триггер по branch-шаблону). Уточним у пользователя, если нужен именно `master`.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# dedinit-deploy Specification (delta)
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Автоматический деплой при пуше в основную ветку
|
||||||
|
|
||||||
|
Система SHALL запускать workflow деплоя при каждом push в основную ветку
|
||||||
|
(`main`) репозитория `estorozhenko/dedinit.ru` на gitea.nixg.ru.
|
||||||
|
|
||||||
|
#### Scenario: Пуш в main запускает деплой
|
||||||
|
|
||||||
|
- **GIVEN** репозиторий `estorozhenko/dedinit.ru` имеет Actions и раннер `bigbox-runner` online
|
||||||
|
- **WHEN** пользователь пушит коммит в ветку `main`
|
||||||
|
- **THEN** Gitea создаёт workflow-run вида `hugo build + sftp deploy`, который завершается
|
||||||
|
успехом, а `https://dedinit.ru/` обновляется без ручных действий
|
||||||
|
|
||||||
|
#### Scenario: Ветка не-main не деплоит
|
||||||
|
|
||||||
|
- **GIVEN** push в любую другую ветку
|
||||||
|
- **WHEN** workflow запускается
|
||||||
|
- **THEN** deploy-шаг пропускается (условие `if: github.ref == 'refs/heads/main'`)
|
||||||
|
|
||||||
|
### Requirement: Деплой через SFTP Jino средствами репозитория
|
||||||
|
|
||||||
|
Система SHALL выполнять сборку `hugo --minify` и зеркалирование `public/` на
|
||||||
|
`kpa39l.myjino.ru:2222:/dedinit.ru/` через существующий
|
||||||
|
`tools/deploy_sftp.py` (SFTP-only, пароль из env `SSHPASS`).
|
||||||
|
|
||||||
|
#### Scenario: Успешный деплой
|
||||||
|
|
||||||
|
- **GIVEN** раннер может запускать job-контейнеры (docker27-bash)
|
||||||
|
- **WHEN** workflow выполняется
|
||||||
|
- **THEN** в логе видно `hugo --minify` (build), затем `deploy_sftp.py`,
|
||||||
|
завершившийся `Done.` без ошибок
|
||||||
|
|
||||||
|
#### Scenario: Пароль из Gitea secrets
|
||||||
|
|
||||||
|
- **GIVEN** секрет `SSHPASS` задан в настройках репозитория Gitea
|
||||||
|
- **WHEN** deploy-шаг стартует
|
||||||
|
- **THEN** пароль берётся из переменной окружения, а не из файла `docs/DEPLOY_INSTRUCTIONS.md`
|
||||||
|
|
||||||
|
### Requirement: Репозиторий не является mirror-репозиторием
|
||||||
|
|
||||||
|
Система SHALL иметь репозиторий `estorozhenko/dedinit.ru` в Gitea без статуса
|
||||||
|
mirror (`mirror:false`, `has_actions:true`), иначе Gitea не выполняет Actions.
|
||||||
|
|
||||||
|
#### Scenario: Снятие mirror-статуса
|
||||||
|
|
||||||
|
- **GIVEN** `PATCH /api/v1/repos/estorozhenko/dedinit.ru` с `{"mirror": false}`
|
||||||
|
- **WHEN** запрос успешен
|
||||||
|
- **THEN** `GET /repos/estorozhenko/dedinit.ru` возвращает `"mirror":false`, `"has_actions":true`
|
||||||
|
|
||||||
|
### Requirement: Синхронизация gitverse → gitea без mirror-интервала
|
||||||
|
|
||||||
|
Система SHALL продолжать получать изменения из gitverse.ru (источника истины)
|
||||||
|
в gitea.nixg.ru, но не через встроенный mirror-интервал, а через крон-скрипт
|
||||||
|
`git fetch origin && git push` (чтобы репозиторий не был зеркалом).
|
||||||
|
|
||||||
|
#### Scenario: Крон синхронизирует
|
||||||
|
|
||||||
|
- **GIVEN** крон-джоб на bigbox, запускающий скрипт синхронизации
|
||||||
|
- **WHEN** в gitverse появился новый коммит в main
|
||||||
|
- **THEN** скрипт приносит его в gitea `estorozhenko/dedinit.ru`, и (при пуше в gitea) Actions деплоят
|
||||||
|
|
||||||
|
### Requirement: Секрет деплоя не светится
|
||||||
|
|
||||||
|
Система SHALL хранить пароль SFTP Jino только в Gitea Actions secrets
|
||||||
|
(переменная `SSHPASS`), не в git-истории и не в логах workflow.
|
||||||
|
|
||||||
|
#### Scenario: Секрет маскируется
|
||||||
|
|
||||||
|
- **GIVEN** workflow использует `${{ secrets.SSHPASS }}`
|
||||||
|
- **WHEN** runner печатает лог
|
||||||
|
- **THEN** Gitea/runner маскирует секрет в выводе (не появляется открытым текстом)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Tasks: Автодеплой dedinit.ru через Gitea Actions
|
||||||
|
|
||||||
|
- [ ] Снять mirror-статус с `estorozhenko/dedinit.ru` в Gitea (PATCH → mirror:false)
|
||||||
|
- Проверка: `curl -s https://gitea.nixg.ru/api/v1/repos/estorozhenko/dedinit.ru | grep '"has_actions"'` → true
|
||||||
|
- [ ] Настроить remote `gitea` в `/opt/dedinit.ru` (git@/https с доступом на push)
|
||||||
|
- Проверка: `git remote -v | grep gitea`
|
||||||
|
- [ ] Создать `.gitea/workflows/deploy.yml` (checkout → hugo build → sftp deploy)
|
||||||
|
- Проверка: `ls .gitea/workflows/deploy.yml`
|
||||||
|
- [ ] Завести секрет `SSHPASS` в Gitea Actions secrets репозитория
|
||||||
|
- Проверка: `curl .../actions/secrets/SSHPASS` → 200
|
||||||
|
- [ ] Создать `sync-dedinit.sh` + cron (gitverse → gitea каждые 10 мин)
|
||||||
|
- Проверка: запуск скрипта вручную приносит коммит, `git log` показывает его в gitea
|
||||||
|
- [ ] Пуш в gitverse (или gitea), дождаться workflow-run
|
||||||
|
- Проверка: API `actions/runs` показывает run, `https://dedinit.ru/` → 200, содержимое обновлено
|
||||||
|
- [ ] `openspec validate auto-deploy-dedinit-actions` → valid
|
||||||
|
- [ ] `openspec archive auto-deploy-dedinit-actions --yes` → изменения смержены в specs
|
||||||
Reference in New Issue
Block a user