# Gitea Complete Guide - Hunab.app > **Для репозитория legal на сервере 206.189.35.205** см. **[GITEA_LEGAL_206_SERVER_GUIDE.md](./GITEA_LEGAL_206_SERVER_GUIDE.md)** (единый стандарт: push, пути, мониторинг). > Ниже — гайд по Gitea для Hunab.app (основной сервер 209.38.32.21). --- **Дата:** 2026-01-02 **Версия Gitea:** 1.25.3 **Статус:** ✅ Полностью развернут и работает **Последнее обновление:** Резервный способ коммитов через временный скрипт (2026-02-16) --- ## 📋 Обзор Gitea успешно развернут на production сервере как self-hosted альтернатива GitHub. ### Преимущества - ✅ Полный контроль данных (код на вашем сервере) - ✅ Бесплатно для неограниченного числа репозиториев - ✅ Нет лимитов на CI/CD минуты - ✅ Низкая задержка (локальная сеть) - ✅ Интеграция с существующей инфраструктурой ### Текущая конфигурация - **Домен:** https://gitea.hunab.app - **Сервер:** 209.38.32.21 - **HTTP порт:** 3000 (внутренний) - **SSH порт:** 2223 (внешний) - **База данных:** PostgreSQL (hunabgit) - **СТАНДАРТ** - **SSL:** Let's Encrypt, TLSv1.2/TLSv1.3 - **Контейнер:** `gitea` - **Сеть:** `hunab-network` **⚠️ ВАЖНО:** PostgreSQL является стандартом для всех развертываний Gitea. MySQL и SQLite не используются. --- ## 🚀 Развертывание ### Быстрый старт ```bash # Развертывание Gitea bash scripts/deployment/gitea/deploy-gitea.sh # Настройка bash scripts/deployment/gitea/setup-gitea.sh # Тестирование bash scripts/deployment/gitea/test-gitea.sh ``` ### Docker Compose (для reference) ```yaml version: "3" services: gitea: image: gitea/gitea:1.25.3 container_name: gitea restart: always networks: - hunab-network ports: - "3000:3000" - "2223:22" volumes: - /opt/gitea:/data environment: - USER_UID=1000 - USER_GID=1000 ``` --- ## 🗄️ База данных PostgreSQL **⚠️ СТАНДАРТ (ОБЯЗАТЕЛЬНО К СОБЛЮДЕНИЮ):** PostgreSQL является ЕДИНСТВЕННЫМ стандартом для всех развертываний Gitea. MySQL и SQLite ЗАПРЕЩЕНЫ. ### ✅ PostgreSQL - ОБЯЗАТЕЛЬНЫЙ СТАНДАРТ - ✅ **Используется на основном сервере** (209.38.32.21) - ✅ **Используется на зеркальном сервере** (206.189.35.205) - ✅ **ОБЯЗАТЕЛЕН для всех новых развертываний** - ✅ **ОБЯЗАТЕЛЕН для всех существующих развертываний** (миграция с MySQL/SQLite) - ✅ Лучшая производительность для больших репозиториев - ✅ Поддержка транзакций и ACID - ✅ Масштабируемость и надежность ### ❌ MySQL и SQLite - ЗАПРЕЩЕНЫ - ❌ **ЗАПРЕЩЕНО использовать MySQL** в проекте - ❌ **ЗАПРЕЩЕНО использовать SQLite** в production - ❌ Ошибка "dial tcp [::1]:3306" означает попытку подключения к MySQL - **НЕДОПУСТИМО** ### Стандартная конфигурация **Основной сервер (209.38.32.21):** ```ini [database] DB_TYPE = postgres HOST = hunab-prod-postgres:5432 NAME = hunabgit USER = hunabgit PASSWD = YOUR_PASSWORD SSL_MODE = disable CHARSET = utf8mb4 ``` **Зеркальный сервер (206.189.35.205):** ```ini [database] DB_TYPE = postgres HOST = gitea-mirror-postgres:5432 NAME = hunabgit USER = utils # ⚠️ На зеркальном сервере используется пользователь utils PASSWD = YOUR_PASSWORD SSL_MODE = disable CHARSET = utf8mb4 ``` **⚠️ КРИТИЧЕСКИ ВАЖНО:** - ❌ Параметр `PATH` ЗАПРЕЩЕН (используется только для SQLite) - ❌ `HOST = localhost:3306` ЗАПРЕЩЕН (это MySQL порт) - ✅ Обязательно использовать имя контейнера PostgreSQL в Docker сети - ⚠️ **На зеркальном сервере используется пользователь `utils` вместо `hunabgit`** ### Создание пользователя utils для зеркального сервера **Проблема:** Если пользователь `utils` не существует или имеет неправильный пароль, Gitea не сможет подключиться к PostgreSQL. **Решение:** #### Автоматический скрипт (рекомендуется) ```bash # На сервере 206.189.35.205 bash scripts/deployment/fix-postgres-utils-user.sh ``` #### Ручное создание пользователя ```bash # Определение суперпользователя PostgreSQL POSTGRES_SUPERUSER=$(docker inspect gitea-mirror-postgres | grep -oP '(?<="POSTGRES_USER=")[^"]*' | head -1 || echo "postgres") # Если не найден, пробуем стандартные варианты if [ -z "$POSTGRES_SUPERUSER" ] || [ "$POSTGRES_SUPERUSER" = "null" ]; then if docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "SELECT 1;" > /dev/null 2>&1; then POSTGRES_SUPERUSER="postgres" elif docker exec gitea-mirror-postgres psql -U hunabgit -d postgres -c "SELECT 1;" > /dev/null 2>&1; then POSTGRES_SUPERUSER="hunabgit" else POSTGRES_SUPERUSER="postgres" fi fi # Создание или изменение пароля для utils docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d postgres << 'SQL' DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'utils') THEN CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD'; ELSE ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD'; END IF; END $$; SQL # Предоставление прав docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;" docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO utils;" docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO utils;" docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT CREATE ON SCHEMA public TO utils;" # Проверка docker exec gitea-mirror-postgres psql -U utils -d hunabgit -c "SELECT version();" ``` **Диагностика проблем:** 1. **"role \"utils\" does not exist"** - Пользователь не создан: ```bash docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD';" ``` 2. **"password authentication failed"** - Пароль неверный: ```bash docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD';" ``` 3. **"permission denied for database"** - Нет прав: ```bash docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;" ``` **Проверка после создания:** ```bash docker exec gitea-mirror-postgres psql -U utils -d hunabgit -c "SELECT 1;" # Должно вернуть: 1 без ошибок ``` ### Настройка через веб-интерфейс При первом запуске http://209.38.32.21:3000: **Database Settings (СТАНДАРТ - PostgreSQL):** - **Database Type:** `PostgreSQL` ⚠️ **ОБЯЗАТЕЛЬНО PostgreSQL, НЕ MySQL, НЕ SQLite** - **Host:** `hunab-prod-postgres:5432` (для основного сервера) или `gitea-mirror-postgres:5432` (для зеркального) - **Database Name:** `hunabgit` ⚠️ **НЕ gitea!** - **Username:** `hunabgit` (основной) или `utils` (зеркальный) - **Password:** `YOUR_PASSWORD` - **SSL Mode:** `disable` **General Settings:** - Domain: `gitea.hunab.app` - SSH Port: `2223` - HTTP Port: `3000` - Gitea Base URL: `https://gitea.hunab.app` **Administrator Account:** - Username: `hunabgit` - Email: `mikhevel@gmail.com` - Password: (надежный пароль) --- ## 🔐 SSL Configuration ### Сертификат Let's Encrypt ```bash # Получение сертификата sudo certbot certonly --standalone -d gitea.hunab.app # Копирование в рабочую директорию sudo cp /etc/letsencrypt/live/gitea.hunab.app/fullchain.pem /opt/app/ssl/gitea-fullchain.pem sudo cp /etc/letsencrypt/live/gitea.hunab.app/privkey.pem /opt/app/ssl/gitea-privkey.pem ``` ### Nginx Configuration Конфигурация находится в `docker/nginx/environments/gitea.conf`: ```nginx # HTTP → HTTPS redirect server { listen 80; server_name gitea.hunab.app; return 301 https://$host$request_uri; } # HTTPS server # CRITICAL: HTTP/2 disabled - git operations require HTTP/1.1 server { listen 443 ssl; # http2 on; # DISABLED: git operations don't work well with HTTP/2 server_name gitea.hunab.app; # SSL configuration ssl_certificate /opt/app/ssl/gitea-fullchain.pem; ssl_certificate_key /opt/app/ssl/gitea-privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Gitea reverse proxy location / { proxy_pass http://gitea:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; # Buffer settings proxy_buffering off; proxy_request_buffering off; } # Git operations (git-receive-pack, git-upload-pack) - увеличенные таймауты # CRITICAL: HTTP/2 disabled for git operations (git uses HTTP/1.1) location ~ ^/.*\.git/(git-receive-pack|git-upload-pack) { proxy_http_version 1.1; proxy_pass http://gitea:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # CRITICAL: Use keep-alive for stable connection during data transfer proxy_set_header Connection "keep-alive"; # Extended timeouts for git operations (для больших репозиториев) proxy_connect_timeout 300s; proxy_send_timeout 600s; proxy_read_timeout 600s; # Buffer settings (отключены для больших файлов) proxy_buffering off; proxy_request_buffering off; client_max_body_size 0; # Дополнительные настройки для стабильности proxy_redirect off; proxy_set_header Accept-Encoding ""; } # Git info/refs operations - также с увеличенными таймаутами location ~ ^/.*\.git/info/refs { proxy_http_version 1.1; proxy_pass http://gitea:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ""; # Extended timeouts proxy_connect_timeout 300s; proxy_send_timeout 600s; proxy_read_timeout 600s; # Buffer settings proxy_buffering off; proxy_request_buffering off; client_max_body_size 0; } } ``` **Ключевые особенности:** - ✅ HTTP/2 отключен для всего server блока (git требует HTTP/1.1) - ✅ Правильные SSL сертификаты (gitea-fullchain.pem с SAN для gitea.hunab.app) - ✅ Увеличенные таймауты для git операций (300-600s) - ✅ Отключена буферизация для больших файлов - ✅ Специальные location блоки для git операций ### SSL Optimization - ✅ TLSv1.2 и TLSv1.3 - ✅ Современные cipher suites - ✅ HSTS enabled (1 год) - ✅ Security headers - ✅ Session tickets disabled **Рекомендация:** Добавьте DNS CAA запись: ``` Type: CAA Name: gitea.hunab.app Value: 0 issue "letsencrypt.org" ``` --- ## 🔄 Push репозиториев (КРИТИЧЕСКИ ВАЖНО) ### ❌ Проблема: Push через HTTPS зависает При попытке `git push` через HTTPS возникают таймауты: ``` fatal: unable to access 'https://gitea.hunab.app/...': Recv failure: Operation timed out ``` **Корневые причины:** 1. HTTP/2 несовместим с git операциями (git использует HTTP/1.1) 2. Недостаточные таймауты для больших репозиториев 3. Проблемы с keepalive соединениями через reverse proxy 4. Ограничения curl/git на стороне клиента (особенно на macOS) **Диагностика в логах Gitea:** ``` Fail to serve RPC(receive-pack): exit status 128 fatal: the remote end hung up unexpectedly ``` POST запрос доходит до Gitea, но соединение обрывается во время передачи данных. ### ✅ Систематическое решение: Универсальный скрипт **РЕКОМЕНДУЕТСЯ:** Используйте универсальный скрипт, который автоматически выбирает лучший метод: ```bash # Обычный скрипт (для работы не из РФ) bash scripts/deployment/gitea/git-push-gitea.sh [branch] # 🇷🇺 ДЛЯ РФ: Скрипт через прокси (ускоряет в 5-10 раз) bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch] ``` **⚠️ КРИТИЧЕСКИ ВАЖНО:** При работе из Российской Федерации **ПРЕДПОЧТИТЕЛЬНО** использовать скрипт с суффиксом `-via-proxy`! **🇷🇺 ПРАВИЛО ДЛЯ РФ:** - ✅ **ВСЕГДА используйте прокси-версии** для push и создания тегов из РФ - ✅ **Ускорение в 5-10 раз** по сравнению с прямым подключением - ✅ **Стабильное соединение** через российский прокси-сервер (149.154.64.19) - ✅ **Избежание таймаутов** - прямое подключение из РФ часто нестабильно **Скрипт автоматически:** 1. Пытается SSH push (если доступен, через прокси для `-via-proxy`) 2. Пытается HTTPS push с оптимизированными настройками (только на Linux с `timeout`) 3. Использует bundle метод (всегда работает, особенно на macOS) 4. ✅ **Мгновенная верификация** - обновляет локальный tracking ref напрямую через `git update-ref` (без медленного `git fetch`) **Пример:** ```bash # Push текущей ветки (по умолчанию dev) bash scripts/deployment/gitea/git-push-gitea.sh # 🇷🇺 ДЛЯ РФ: Push через прокси (рекомендуется из РФ) bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh # Push конкретной ветки bash scripts/deployment/gitea/git-push-gitea.sh main bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh main # 🇷🇺 ДЛЯ РФ # Режим проверки без пуша (ничего не меняет, только показывает сколько коммитов уйдёт) bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run dev ``` **Безопасность скрипта пуша:** - Не трогает текущую ветку, HEAD, рабочую копию и локальные коммиты. - Не выполняет: `git reset`, `git checkout --hard`, `git clean`, `git branch -D`, `push --force`. - Единственная запись в репо: обновление `refs/remotes/origin/` только после успешного пуша и только на кончик текущей ветки (все «ждущие пуша» коммиты при этом уже на сервере). - Перед пушем можно проверить: `--dry-run` — показывает число коммитов к пушу, ничего не отправляет и не меняет. **Git Alias (для удобства):** Добавьте в `~/.gitconfig`: ```ini [alias] gpush = !bash -c 'cd "$(git rev-parse --show-toplevel)" && bash scripts/deployment/gitea/git-push-gitea.sh "$@"' - ``` Использование: `git gpush dev` ### ✅ Резервный способ: Временный скрипт для коммита и пуша **Когда использовать:** - Терминал недоступен из-за проблем с zsh/shell - Агент не может выполнить команды напрямую - Нужно подготовить все команды заранее **Процесс:** 1. **Агент создает временный скрипт** в `temp/commit-and-push.sh` с полным коммитом и push командой 2. **Пользователь выполняет скрипт** вручную в терминале 3. **Скрипт автоматически** делает `git add`, `git commit` и `git push` через универсальный скрипт **Формат временного скрипта:** ```bash #!/bin/bash # Скрипт для коммита и пуша изменений set -e echo "📦 Committing changes..." git add -A echo "💾 Creating commit..." git commit -m "🎨 feat(frontend): центрирование нод при tidyUp с учетом реальных ширин v3.10.4 - ✅ Улучшена функция calculateLevelPositions для центрирования нод - ✅ Добавлен учет реальных ширин нод (node.width или NODE_WIDTH) - ✅ Вся группа нод на уровне центрируется относительно START_X - ✅ Более широкие ноды правильно центрированы относительно узких - ✅ Обновлена документация (TROUBLESHOOTING_COMPLETE.md, CHANGELOG.md) Проблема: Более широкие ноды выравнивались по левому краю Решение: Центрирование с учетом реальных ширин нод Результат: Ноды центрируются на уровне, визуально выглядит красиво" echo "🚀 Pushing to Gitea..." bash scripts/deployment/gitea/git-push-gitea.sh dev echo "✅ Done!" ``` **Требования к скрипту:** - ✅ Должен быть исполняемым (`chmod +x`) - ✅ Должен содержать полное сообщение коммита с описанием изменений - ✅ Должен использовать универсальный скрипт `git-push-gitea.sh` для push - ✅ Должен быть в папке `temp/` для временных скриптов - ✅ Должен иметь понятные echo сообщения для пользователя **Использование:** ```bash # Выполнить скрипт bash temp/commit-and-push.sh ``` **Очистка:** После успешного выполнения скрипт можно удалить: ```bash rm temp/commit-and-push.sh ``` **⚠️ ВАЖНО:** - Скрипт должен быть создан агентом **ПЕРЕД** запросом пользователя на коммит - Агент должен **ВСЕГДА** готовить скрипт, если терминал недоступен - Скрипт должен содержать **ПОЛНОЕ** описание изменений в коммите ### Стандарт: без трейлеров в коммитах В проекте **не используются** git-трейлеры в сообщениях коммитов (строки вида `Key: Value` в конце сообщения). Не добавлять: - `Made-with: Cursor` и аналогичные метки инструмента - `Co-authored-by:`, `Signed-off-by:` и т.п., если это не требуется явно политикой репозитория Коммиты должны соответствовать [COMMIT_STANDARDS.md](../../standards/COMMIT_STANDARDS.md) и содержать только заголовок и при необходимости тело сообщения в стандартном формате. См. также раздел «Commit Quality Standards» в `.cursorrules`. ### Неблокирующая проверка типов (при коммитах) Перед коммитом или пушем можно быстро проверить типы (frontend и/или backend) **без блокировки коммита** — скрипт всегда выходит с кодом 0 и только выводит ошибки в консоль. ```bash # Frontend + backend (полный отчёт) bash scripts/maintenance/typecheck-report.sh # Только изменённая часть (быстрее) bash scripts/maintenance/typecheck-report.sh frontend bash scripts/maintenance/typecheck-report.sh backend ``` Отдельно в пакетах: - `cd frontend && pnpm typecheck` — только frontend - `cd backend && pnpm typecheck` — только backend **Рекомендация:** запускать при коммитах бэкенда/фронта или перед пушем; коммит не блокируется, но ошибки видны в выводе. ### Проверка прав на .env после деплоя (SECRETS_AND_FILE_PERMISSIONS_POLICY.md §4) После деплоя бэкенда на production выполнить проверку прав на `.env.production` (и при необходимости `.env.staging`) на сервере. Встроена в скрипты `deploy-backend-core-only.sh` и `deploy-backend-core-only-via-proxy.sh`; при необходимости запустить вручную: ```bash # После деплоя backend на прод (или вручную) bash scripts/deployment/verify-env-permissions.sh hunab-prod # либо с явным хостом bash scripts/deployment/verify-env-permissions.sh hunab@209.38.32.21 ``` Ожидается: права `600` и владелец `hunab:hunab` для всех `.env*` в `/opt/app/`. При несоответствии скрипт выводит предупреждение и команду для исправления; деплой не блокируется (exit 0). **Чеклист коммитов/деплоя:** после деплоя backend на production — выполнить проверку прав (или убедиться, что она уже выполнена скриптом деплоя). См. [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md) §4. ### Метод 1: HTTPS с оптимизированными настройками ```bash # Настройка git для больших push git config http.postBuffer 524288000 git config http.lowSpeedLimit 0 git config http.lowSpeedTime 0 git config http.timeout 600 # Попытка push git push origin dev ``` **⚠️ На macOS:** HTTPS push часто не работает из-за отсутствия команды `timeout`. Скрипт автоматически пропускает HTTPS и использует bundle метод. ### Метод 2: Bundle метод (надежный fallback) Если HTTPS не работает, используется прямой доступ через файловую систему: ```bash # Создать bundle git bundle create /tmp/push.bundle origin/dev..dev # Скопировать на сервер scp /tmp/push.bundle hunab-prod:/tmp/ # Применить в Gitea ssh hunab-prod "docker cp /tmp/push.bundle gitea:/tmp/push.bundle && \ docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && \ git bundle unbundle /tmp/push.bundle && \ git update-ref refs/heads/dev \$(git bundle list-heads /tmp/push.bundle | grep dev | cut -d\" \" -f1) && \ rm /tmp/push.bundle' && rm /tmp/push.bundle" ``` **✅ Статус:** Bundle метод всегда работает и обходит все проблемы с таймаутами. ### Сравнение методов | Метод | Скорость | Надежность | Сложность | Платформа | |-------|----------|------------|-----------|-----------| | **SSH** | ⚡⚡⚡ Быстро | ✅✅✅ Высокая | ✅ Просто | Все | | **HTTPS (оптимизированный)** | ⚡⚡ Средне | ✅✅ Средняя | ✅ Просто | Linux | | **Bundle (прямой доступ)** | ⚡ Медленно | ✅✅✅ Всегда работает | ⚠️ Сложнее | Все | **Рекомендация:** Используйте автоматический скрипт - он выберет лучший метод автоматически. ### Миграция репозиториев **Для регулярных push используйте скрипт:** ```bash bash scripts/deployment/gitea/git-push-gitea.sh [branch] ``` #### Шаг 1: Создание bundle из локального репозитория ```bash cd /Users/eternal/code/hunabapp-dev git bundle create /tmp/hunabapp-migration.bundle --all ``` **Результат:** Bundle ~553M #### Шаг 2: Копирование bundle на сервер ```bash scp /tmp/hunabapp-migration.bundle hunab-prod:/tmp/hunabapp-migration.bundle ``` #### Шаг 3: Распаковка bundle на сервере ```bash ssh hunab-prod "cd /tmp && git clone --mirror hunabapp-migration.bundle hunabapp-mirror.git" ``` **Результат:** Bare репозиторий ~557M #### Шаг 4: Копирование в директорию Gitea ```bash ssh hunab-prod " docker exec gitea mkdir -p /data/git/repositories/hunabgit/ docker cp /tmp/hunabapp-mirror.git gitea:/data/git/repositories/hunabgit/hunabapp.git docker exec gitea chown -R git:git /data/git/repositories/hunabgit/hunabapp.git " ``` **Результат:** Репозиторий в Gitea (556.6M) #### Шаг 5: Распаковка refs из packed-refs **КРИТИЧЕСКИ ВАЖНО:** Gitea не видит ветки в `packed-refs`, нужно распаковать: ```bash ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'" ``` #### Шаг 6: Обновление базы данных ```bash ssh hunab-prod " docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'hunabapp';\" docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini docker restart gitea " ``` #### Шаг 7: Проверка ```bash # Проверка веток через API curl -s -H "Authorization: token YOUR_TOKEN" \ https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/branches | jq '.[] | .name' # Проверка файлов через API curl -s -H "Authorization: token YOUR_TOKEN" \ https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/git/trees/dev?recursive=0 | jq '.tree[] | .path' # Проверка через веб-интерфейс https://gitea.hunab.app/hunabgit/hunabapp ``` **✅ Результат:** Репозиторий полностью мигрирован, все файлы доступны через веб-интерфейс и API. ### Аутентификация #### Создание токена доступа 1. Откройте: https://gitea.hunab.app/user/settings/applications 2. Generate New Token 3. Название: `api-access` 4. Права: `write:repository`, `read:repository`, `read:user` 5. Скопируйте токен #### Использование токена в git ```bash # В локальном репозитории cd /Users/eternal/code/hunabapp-dev git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/hunabapp.git ``` --- ## 🏷️ Создание тегов и Release (СТАНДАРТ) ### ⚠️ КРИТИЧЕСКИ ВАЖНО: Gitea не обновляет индекс тегов автоматически **Проблема:** Если создать тег напрямую в git репозитории (`git tag -a v1.0.0`), Gitea **НЕ покажет его в веб-интерфейсе** автоматически. Gitea кэширует список тегов и обновляет его только при создании Release через веб-интерфейс или API. ### ✅ СТАНДАРТ: Создание тега через базу данных (РЕКОМЕНДУЕТСЯ) **Используйте этот метод для автоматического создания тегов:** ```bash # 1. Получить ID репозитория REPO_ID=$(ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -t -c \"SELECT id FROM repository WHERE lower_name = 'hunabapp';\" | tr -d ' '") # 2. Создать Release в базе данных ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \" INSERT INTO release ( repo_id, publisher_id, tag_name, lower_tag_name, target, sha1, title, note, is_draft, is_prerelease, is_tag, created_unix ) VALUES ( $REPO_ID, 1, 'v1.3.7-CRM', 'v1.3.7-crm', 'dev', '1542c302c36ad367281f8d9740e4d9f4a817ad23', 'v1.3.7-CRM: Полная реализация CRM Contacts Page', 'feat(CRM): Полная реализация CRM Contacts Page v1.3.7', false, false, true, EXTRACT(EPOCH FROM NOW())::bigint ) ON CONFLICT (repo_id, tag_name) DO NOTHING RETURNING id, tag_name, title; \"" # 3. Перезапустить Gitea для обновления индекса ssh hunab-prod "docker restart gitea" ``` **Параметры:** - `repo_id` - ID репозитория (получить через `SELECT id FROM repository WHERE lower_name = 'hunabapp';`) - `publisher_id` - ID пользователя (обычно 1 для первого пользователя) - `tag_name` - Имя тега (например, `v1.3.7-CRM`) - `lower_tag_name` - Имя тега в нижнем регистре (например, `v1.3.7-crm`) - `target` - Ветка или commit (например, `dev` или `1542c302c36ad367281f8d9740e4d9f4a817ad23`) - `sha1` - SHA коммита (40 символов) - `title` - Заголовок Release - `note` - Описание Release - `is_tag` - `true` для тега, `false` для Release ### ✅ Альтернатива: Создание через веб-интерфейс Если нужно создать тег вручную: 1. Откройте: https://gitea.hunab.app/hunabgit/hunabapp/releases/new 2. В поле "Tag" введите имя тега (например, `v1.3.7-CRM`) 3. В поле "Target" выберите commit из списка или введите SHA 4. Заполните Title и Description 5. Нажмите "Publish Release" **⚠️ Недостаток:** Требует ручного действия, не автоматизируется. ### ✅ Альтернатива: Создание через API (требует токен) ```bash # 1. Получить токен доступа (один раз) TOKEN=$(ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin user generate-access-token --username hunabgit --scopes 'write:repository' --name 'api-tag-creation' 2>&1 | grep -o '[a-z0-9]\{40\}' | head -1") # 2. Создать Release через API curl -X POST "https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/releases" \ -H "Authorization: token $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tag_name": "v1.3.7-CRM", "target_commitish": "1542c302c36ad367281f8d9740e4d9f4a817ad23", "name": "v1.3.7-CRM: Полная реализация CRM Contacts Page", "body": "feat(CRM): Полная реализация CRM Contacts Page v1.3.7", "draft": false, "prerelease": false }' ``` **⚠️ Недостаток:** Требует токен доступа, который нужно создать заранее. ### ❌ НЕ РАБОТАЕТ: Создание тега напрямую в git ```bash # ❌ НЕ РАБОТАЕТ - Gitea не увидит тег в веб-интерфейсе git tag -a v1.3.7-CRM -m "Message" git push origin v1.3.7-CRM # Даже после push тег не появится в веб-интерфейсе ``` **Почему не работает:** - Gitea кэширует список тегов в базе данных - Создание тега напрямую в git не обновляет таблицу `release` - Gitea не сканирует репозиторий автоматически для поиска новых тегов ### 📋 Чеклист создания тега - [ ] Получить ID репозитория из базы данных - [ ] Получить SHA коммита для тега - [ ] Создать Release в таблице `release` базы данных - [ ] Перезапустить Gitea для обновления индекса - [ ] Проверить что тег появился в веб-интерфейсе: https://gitea.hunab.app/hunabgit/hunabapp/releases ### 🔧 Скрипт для автоматического создания тега **Готовый скрипт:** `scripts/deployment/gitea/create-gitea-tag.sh` **🇷🇺 ДЛЯ РФ:** Используйте прокси-версию для ускорения в 5-10 раз: - `scripts/deployment/gitea/create-gitea-tag.sh` - обычный скрипт - `scripts/deployment/gitea/create-gitea-tag-via-proxy.sh` - **🇷🇺 ДЛЯ РФ** - через прокси **Использование:** ```bash # Обычный скрипт bash scripts/deployment/gitea/create-gitea-tag.sh \ \ \ \ [description] \ [branch] # 🇷🇺 ДЛЯ РФ: Через прокси (рекомендуется из РФ) bash scripts/deployment/gitea/create-gitea-tag-via-proxy.sh \ <tag-name> \ <commit-sha> \ <title> \ [description] \ [branch] ``` **Пример:** ```bash bash scripts/deployment/gitea/create-gitea-tag.sh \ v1.3.7-CRM \ 1542c302c36ad367281f8d9740e4d9f4a817ad23 \ "v1.3.7-CRM: Полная реализация CRM Contacts Page" \ "feat(CRM): Полная реализация CRM Contacts Page v1.3.7" \ dev ``` **Что делает скрипт:** 1. ✅ Валидирует параметры (формат тега, SHA коммита) 2. ✅ Получает ID репозитория из базы данных 3. ✅ Создает Release в таблице `release` 4. ✅ Перезапускает Gitea для обновления индекса 5. ✅ Выводит ссылку для проверки **Особенности:** - Автоматически обрабатывает конфликты (обновляет существующий тег) - Экранирует специальные символы в SQL - Проверяет существование репозитория - Выводит понятные сообщения об ошибках --- ## 📊 Мониторинг ### Проверка статуса ```bash # Контейнер ssh hunab-prod "docker ps | grep gitea" # Логи ssh hunab-prod "docker logs gitea --tail 50" # Ресурсы ssh hunab-prod "docker stats gitea --no-stream" # API curl https://gitea.hunab.app/api/v1/version # SSL openssl s_client -connect gitea.hunab.app:443 -servername gitea.hunab.app </dev/null 2>&1 | grep -E 'Protocol|Cipher' ``` ### Проверка репозитория ```bash # Ветки ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a | wc -l" # Размер ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/hunabapp.git" # Последний коммит ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git log -1 --pretty=format:'%H %s'" ``` --- ## 🔧 Troubleshooting Для детальной диагностики проблем см. **[GITEA_TROUBLESHOOTING.md](./GITEA_TROUBLESHOOTING.md)** - Полное руководство по решению проблем. Для диагностики зеркального сервера см. **[GITEA_MIRROR_TROUBLESHOOTING.md](./GITEA_MIRROR_TROUBLESHOOTING.md)**. ### Gitea не запускается ```bash # Проверка логов ssh hunab-prod "docker logs gitea --tail 100" # Проверка портов ssh hunab-prod "ss -tuln | grep -E '3000|2223'" # Перезапуск ssh hunab-prod "docker restart gitea" ``` ### Ветки не видны в веб-интерфейсе **Причина:** Refs в `packed-refs`, Gitea их не читает **Решение:** Распаковать refs (см. Шаг 5 миграции) **✅ Статус:** Проблема решена, все ветки видны в веб-интерфейсе ### Git hooks кажутся сломанными **Проблема:** Gitea показывает предупреждение "Git hooks of this repository seem to be broken" **Причина:** После прямой миграции репозитория hooks могут быть не синхронизированы с Gitea **Решение:** Регенерировать hooks для всех репозиториев: ```bash ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini" ``` **Проверка:** ```bash # Проверка прав на hooks ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/hunabapp.git/hooks/pre-receive" # Проверка содержимого hooks ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/hunabapp.git/hooks/pre-receive.d/gitea" ``` **Дополнительно:** Если проблема сохраняется, проверьте: 1. Файловая система поддерживает выполнение (`chmod +x` работает) 2. Файловая система не смонтирована с `noexec` 3. Docker версия >= 20.10.6 (у нас 28.0.1 ✅) **См. документацию Gitea:** https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running **✅ Статус:** Решено через `gitea admin regenerate hooks` ### Push через HTTPS зависает **Проблема:** Git получает refs (GET запрос успешен), но POST запрос с данными не отправляется или соединение обрывается. **Причина:** - Известная проблема с HTTP/2 и большими репозиториями в Gitea - Проблемы с keepalive соединениями через reverse proxy - Ограничения curl/git на стороне клиента (особенно на macOS) **Решение:** 1. ✅ **Используйте универсальный скрипт:** `bash scripts/deployment/gitea/git-push-gitea.sh [branch]` 2. ✅ Скрипт автоматически выбирает лучший метод (SSH > HTTPS > Bundle) 3. ✅ На macOS автоматически использует bundle метод (обходит проблемы с HTTPS) **✅ Статус:** Систематически решено через универсальный скрипт (2026-01-02) ### SSL сертификат не обновляется ```bash # Проверка автообновления ssh hunab-prod "systemctl status certbot.timer" # Ручное обновление ssh hunab-prod "sudo certbot renew --dry-run" # Копирование новых сертификатов ssh hunab-prod "sudo cp /etc/letsencrypt/live/gitea.hunab.app/*.pem /opt/app/ssl/ && docker restart hunab-prod-nginx" ``` --- ## 🔒 Безопасность ### Рекомендации 1. ✅ HTTPS через Let's Encrypt (настроено) 2. ✅ Modern SSL/TLS protocols (TLSv1.2, TLSv1.3) 3. ✅ Security headers (HSTS, X-Frame-Options, etc.) 4. ⚠️ Firewall для ограничения доступа (рекомендуется) 5. ✅ SSH ключи для git операций 6. ✅ Регулярные бэкапы данных ### Бэкапы ```bash # Полный бэкап Gitea ssh hunab-prod " tar -czf /opt/gitea-backup-$(date +%Y%m%d).tar.gz /opt/gitea " # Бэкап только репозиториев ssh hunab-prod " tar -czf /opt/gitea-repos-backup-$(date +%Y%m%d).tar.gz /opt/gitea/git/repositories " # Бэкап базы данных ssh hunab-prod " docker exec hunab-prod-postgres pg_dump -U hunabgit hunabgit > /opt/gitea-db-backup-$(date +%Y%m%d).sql " ``` --- ## 📈 Итоги и выводы ### Что было достигнуто 1. ✅ **Gitea развернут на production** (209.38.32.21) 2. ✅ **PostgreSQL настроен** (отдельная БД hunabgit) 3. ✅ **SSL/TLS настроен** (Let's Encrypt, A rating) 4. ✅ **Nginx reverse proxy** (с оптимизацией) 5. ✅ **Репозиторий hunabapp мигрирован** (556.8M, 23 ветки, все файлы доступны) 6. ✅ **DNS настроен** (gitea.hunab.app → 209.38.32.21) 7. ✅ **Веб-интерфейс работает** (файлы отображаются корректно) 8. ✅ **API полностью функционален** (все endpoints работают) ### Ключевые проблемы и решения #### Проблема 1: MySQL vs PostgreSQL - **Проблема:** Gitea пытался подключиться к несуществующему MySQL - **Решение:** Настроили PostgreSQL с отдельной БД #### Проблема 2: SSL сертификат для поддомена - **Проблема:** Существующий сертификат был для hunab.app, не gitea.hunab.app - **Решение:** Получили новый сертификат через certbot --standalone #### Проблема 3: Push через HTTPS зависает - **Проблема:** Git получает refs, но POST запрос с данными не отправляется или соединение обрывается - **Причина:** Известная проблема с HTTP/2 и большими репозиториями в Gitea, проблемы с keepalive соединениями - **Попытки:** Увеличение таймаутов Nginx до 600s, отключение HTTP/2, оптимизация Connection headers - **Решение:** Систематическое решение через универсальный скрипт `git-push-gitea.sh` (2026-01-02) - Автоматический выбор метода: SSH > HTTPS > Bundle - На macOS автоматически использует bundle метод - Всегда работает, обходит все проблемы с таймаутами #### Проблема 4: Gitea не видит ветки - **Проблема:** Refs в packed-refs, Gitea их не индексирует - **Решение:** Распаковали refs в refs/heads/ структуру #### Проблема 5: GitHub требует аутентификацию - **Проблема:** git clone --mirror с сервера не работает без токена - **Решение:** Использовали bundle, созданный локально ### Lessons Learned 1. **Прямое копирование эффективнее push** для больших репозиториев 2. **packed-refs не поддерживается Gitea** — нужна распаковка 3. **certbot --standalone** проще для новых сертификатов 4. **Docker network** критичен для PostgreSQL подключения 5. **Bundle** — универсальный способ переноса репозиториев ### Производительность - **Обычный git push через HTTPS:** ❌ Зависает (POST запрос не отправляется или соединение обрывается) - **Универсальный скрипт (git-push-gitea.sh):** ✅ Автоматически выбирает лучший метод - SSH: ⚡⚡⚡ Быстро (если доступен) - HTTPS: ⚡⚡ Средне (только на Linux) - Bundle: ⚡ Медленно, но всегда работает (особенно на macOS) - **🇷🇺 ДЛЯ РФ: Прокси-версия (git-push-gitea-via-proxy.sh):** ✅ Ускорение в 5-10 раз - Работает через российский прокси-сервер (149.154.64.19) - Стабильное соединение к Digital Ocean - Избежание таймаутов при работе из РФ - Bundle метод через прокси: ⚡⚡ Быстро и надежно - ✅ **Мгновенная верификация** - обновление tracking ref без сетевых запросов (2026-02-27) - **Прямое копирование через bundle:** ✅ ~1-2 минуты для инкрементальных обновлений - **Полная миграция:** ✅ ~5-10 минут (включая распаковку refs) - **Размер репозитория:** 556.8M (23 ветки, история с 2020 года) - **Текущий статус:** ✅ Все файлы доступны, веб-интерфейс работает корректно - **Скрипт для push:** ✅ `scripts/deployment/gitea/git-push-gitea.sh` - универсальное решение (2026-01-02) ### Следующие шаги (опционально) 1. **Настроить CI/CD** в Gitea (Gitea Actions) 2. **Настроить зеркалирование** GitHub ↔ Gitea (для резервирования) 3. **Настроить SSH ключи** для бесшовной работы 4. **Добавить DNS CAA запись** для дополнительной безопасности 5. **Настроить автоматические бэкапы** (cron job) 6. **Мигрировать deployment скрипты** на использование Gitea --- ## 📚 Полезные команды ```bash # Push репозитория (РЕКОМЕНДУЕТСЯ) bash scripts/deployment/gitea/git-push-gitea.sh [branch] # 🇷🇺 ДЛЯ РФ: Push через прокси (ускоряет в 5-10 раз) bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch] # Gitea Admin Commands docker exec gitea /usr/local/bin/gitea admin --help docker exec gitea /usr/local/bin/gitea admin regenerate hooks # Git операции в Gitea docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git log --oneline -10 # PostgreSQL операции docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c "SELECT * FROM repository;" # Проверка конфигурации базы данных docker exec gitea cat /data/gitea/conf/app.ini | grep -A 10 '\[database\]' # Должно быть: DB_TYPE = postgres, HOST = <postgres-container>:5432 # SSL проверка openssl s_client -connect gitea.hunab.app:443 -servername gitea.hunab.app curl -vI https://gitea.hunab.app 2>&1 | grep -E 'SSL|TLS' # Nginx docker exec hunab-prod-nginx nginx -t docker logs hunab-prod-nginx --tail 50 # Проверка логов Gitea при push ssh hunab-prod "docker logs gitea --tail 50 | grep -E '(receive-pack|upload-pack|error)'" ``` --- ## 🔄 Зеркалирование Для полного руководства по зеркалированию см. **[GITEA_MIRRORING_GUIDE.md](./GITEA_MIRRORING_GUIDE.md)**. ### Миграция Gitea на зеркальный сервер **📋 План миграции:** См. **[GITEA_MIGRATION_TO_MIRROR_SERVER.md](./GITEA_MIGRATION_TO_MIRROR_SERVER.md)** - Детальный план миграции Gitea с production сервера (209.38.32.21) на зеркальный сервер (206.189.35.205). **Причины миграции:** - Освобождение дискового пространства на production сервере (96% заполнено) - Изоляция рисков - проблемы Gitea не влияют на production - Улучшение производительности Git операций - Масштабируемость ### Зеркальный сервер (206.189.35.205) Для резервирования и высокой доступности настроено зеркалирование всех репозиториев на внешний сервер. **Документация:** - [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md) - Полный план зеркалирования - [Gitea Migration to Mirror Server](./GITEA_MIGRATION_TO_MIRROR_SERVER.md) - **План миграции Gitea на зеркальный сервер (206.189.35.205)** - [Gitea Mirror Troubleshooting](./GITEA_MIRROR_TROUBLESHOOTING.md) - Диагностика зеркального сервера - [Gitea Legal — сервер 206.189.35.205](./GITEA_LEGAL_206_SERVER_GUIDE.md) - Стандарт для репозитория legal **Быстрый старт:** ```bash # Развертывание зеркального сервера bash scripts/deployment/gitea/deploy-gitea-mirror-server.sh # Миграция всех репозиториев bash scripts/deployment/mirror-all-repos-to-external.sh ``` **Архитектура:** - **Основной сервер (209.38.32.21):** Primary Gitea, все репозитории - **Зеркальный сервер (206.189.35.205):** Mirror Gitea, автоматическая синхронизация --- ## 🔗 Ссылки - **Веб-интерфейс:** https://gitea.hunab.app - **API:** https://gitea.hunab.app/api/v1/ - **Репозиторий:** https://gitea.hunab.app/hunabgit/hunabapp - **Документация Gitea:** https://docs.gitea.io/ - **SSL Report:** https://www.ssllabs.com/ssltest/analyze.html?d=gitea.hunab.app - **Зеркалирование:** [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md) - **Миграция на зеркальный сервер:** [Gitea Migration Plan](./GITEA_MIGRATION_TO_MIRROR_SERVER.md) - **Индекс документации:** [Gitea Index](./INDEX.md) - Перелинкованный индекс всех документов Gitea --- ## ✅ Текущий статус (2025-12-27) ### Репозиторий hunabapp - ✅ **Размер:** 556.8M - ✅ **Ветки:** 23 (dev, main, и другие) - ✅ **Файлы:** Все файлы доступны через веб-интерфейс и API - ✅ **Веб-интерфейс:** Полностью функционален - ✅ **API:** Работает корректно - ✅ **Git операции:** Клонирование, push, pull работают ### Система - ✅ Gitea контейнер работает стабильно - ✅ PostgreSQL подключение стабильно - ✅ SSL/TLS работает корректно - ✅ Nginx reverse proxy настроен правильно - ✅ DNS настроен (gitea.hunab.app → 209.38.32.21) **Последнее обновление:** 2026-02-27 **Версия документа:** 1.4 **Статус:** ✅ Gitea полностью развернут и работает стабильно **Изменения:** - Добавлен резервный способ коммитов через временный скрипт (2026-02-16) - Добавлен гайд по очистке истории Git от build-артефактов (2026-02-27) --- ## 🧹 Очистка истории Git от build-артефактов ### 🎯 Когда нужна очистка vs простое удаление **99% случаев:** Файлы уже в `.gitignore`, но Git их отслеживает (закоммичены до добавления в `.gitignore`) **Решение:** ✅ **Простое удаление из tracking** (см. ниже) - БЕЗ переписывания истории! **1% случаев:** Нужно удалить файлы из ВСЕЙ истории (например, случайно закоммитили секреты) **Решение:** ⚠️ **Очистка истории** (см. раздел "Когда действительно нужна очистка истории") --- ## ✅ ПРАВИЛЬНЫЙ ПОДХОД: Простое удаление из tracking **Когда использовать:** Файлы уже в `.gitignore`, но Git их отслеживает. **Пример проблемы:** - Файлы в `.gitignore`: `frontend/dist-*` - Git все еще отслеживает: 4046 файлов из `dist-*` папок - `git status` показывает: `D frontend/dist-cache-busting/404.html` и т.д. ### ✅ Решение (ПРАВИЛЬНО): ```bash # 1. Убедиться что файлы в .gitignore cat frontend/.gitignore | grep dist- # 2. Удалить из tracking (файлы останутся в рабочей директории, если есть) git rm --cached -r frontend/dist-cache-busting/ \ frontend/dist-fast-no-image/ \ frontend/dist-via-proxy/ \ frontend/dist-zero-downtime/ 2>/dev/null || \ git add -u -- frontend/dist-*/ # Альтернатива если файлы уже удалены из рабочей директории # 3. Проверить что файлы в staging для удаления git status --short | grep "^D " | wc -l # Должно показать количество удаляемых файлов # 4. Закоммитить удаление git commit -m "chore(frontend): удаление dist-* build-артефактов из Git tracking - Удалены файлы из frontend/dist-cache-busting/, dist-fast-no-image/, dist-via-proxy/, dist-zero-downtime/ - Файлы были в .gitignore, но отслеживались Git (закоммичены до добавления в .gitignore) - Build-артефакты генерируются при деплое и не должны храниться в репозитории" # 5. Push в Gitea bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev # 🇷🇺 ДЛЯ РФ # 6. Проверка git ls-files | grep -E "^frontend/dist-(zero-downtime|fast-no-image|via-proxy|cache-busting)/" | wc -l # Должно быть: 0 ``` **✅ Результат:** - Файлы удалены из tracking (больше не отслеживаются Git) - История НЕ переписана (SHA коммитов не изменились) - Все коммиты сохранены - Работает быстро и безопасно **⚠️ ВАЖНО:** После этого файлы останутся в истории старых коммитов, но новые коммиты их не будут содержать. Это нормально для build-артефактов. --- ## ❌ КАК НЕ НАДО: git-filter-repo с --invert-paths ### ❌ КРИТИЧЕСКАЯ ОШИБКА (НЕ ДЕЛАТЬ ТАК!): ```bash # ❌ НЕПРАВИЛЬНО - УНИЧТОЖАЕТ ВСЕ КОММИТЫ! git filter-repo \ --path frontend/dist-zero-downtime/ \ --path frontend/dist-fast-no-image/ \ --path frontend/dist-via-proxy/ \ --path frontend/dist-cache-busting/ \ --invert-paths \ --force ``` **Что происходит:** - ❌ `git-filter-repo` с `--invert-paths` **УНИЧТОЖАЕТ ВСЕ КОММИТЫ** - ❌ Все tree objects становятся пустыми (0 файлов в коммитах) - ❌ Репозиторий полностью сломан - ❌ Нужно восстанавливать из бэкапа **Почему это происходит:** - `--invert-paths` означает "сохранить все, кроме указанных путей" - Но если указанные пути были в КАЖДОМ коммите, то все коммиты становятся пустыми - Git не может создать коммит без файлов **Реальный пример ошибки (2026-02-27):** ``` # После git-filter-repo: git ls-tree -r HEAD --name-only | wc -l # Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!) git log --oneline -5 # Коммиты есть, но в них НЕТ ФАЙЛОВ ``` **✅ Решение после ошибки:** ```bash # Восстановить из бэкапа rm -rf .git mv .git.backup-20260227-171445 .git # Использовать ПРАВИЛЬНЫЙ подход (см. выше) git rm --cached -r frontend/dist-*/ && git commit -m "..." ``` --- ## ⚠️ Когда действительно нужна очистка истории **Редкие случаи, когда нужна очистка истории:** 1. **Случайно закоммитили секреты** (пароли, API ключи, токены) - ⚠️ Критично: секреты в истории = уязвимость безопасности - ✅ Нужна полная очистка истории 2. **Огромные бинарные файлы** (видео, большие изображения, базы данных) - ⚠️ Размер репозитория > 2GB из-за истории - ✅ Нужна очистка для уменьшения размера 3. **Юридические требования** (удаление файлов по запросу) - ⚠️ Требуется полное удаление из истории - ✅ Нужна очистка истории **⚠️ ВАЖНО:** Для build-артефактов (`dist/`, `build/`) очистка истории **НЕ НУЖНА** - достаточно простого удаления из tracking! --- ## 🧹 Очистка истории от файлов с секретами (docs) **Когда применять:** после замены реальных секретов на плейсхолдеры в документации (см. [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md)) нужно удалить из истории Git старые версии этих файлов, чтобы секреты нельзя было извлечь из старых коммитов. **Гарантированно безопасный порядок (обязательно):** 1. **Бэкап репозитория (ОБЯЗАТЕЛЬНО)** — без бэкапа не приступать. 2. Запуск скрипта, который делает бэкап сам и выполняет очистку по списку путей. 3. Проверки целостности (в скрипте). 4. Применение очищенной версии на Gitea только после бэкапа репо на сервере. ### Скрипт очистки (бэкап + filter-repo + проверки) Используется готовый скрипт, который: - Создаёт резервную копию (`git clone --mirror`) в каталог рядом с репо. - Клонирует репо в bare, удаляет из истории пути из списка (`git filter-repo --paths-from-file ... --invert-paths`). - Проверяет целостность (`git ls-tree -r HEAD | wc -l` > 0, `git fsck`). - Восстанавливает в текущем tip почищенные версии файлов (один коммит с плейсхолдерами). **Запуск (из корня репозитория):** ```bash # Установить git-filter-repo (один раз) # macOS: brew install git-filter-repo # Linux: # pip3 install git-filter-repo # Запуск (бэкап создаётся скриптом автоматически) bash scripts/maintenance/git-history-cleanup-secrets.sh /path/to/hunabapp-dev ``` **Файлы:** - Список путей (файлы, которые удаляются из истории): `scripts/maintenance/git-history-cleanup-secrets-paths.txt` — соответствует таблице в [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md) (очистка 2026-03-03). - Скрипт: `scripts/maintenance/git-history-cleanup-secrets.sh`. **После скрипта:** выполнить шаги из раздела ниже «Правильная очистка истории»: создание bundle из очищенного bare-репо, загрузка на сервер, **бэкап Gitea-репо на сервере**, замена objects/refs, перезапуск Gitea, обновление локального репо. **Критично:** на production-сервере перед заменой репозитория в Gitea обязательно создать бэкап: ```bash GITEA_REPO="/data/git/repositories/hunabgit/hunabapp.git" ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)" ``` --- ## ✅ Правильная очистка истории (если действительно нужна) **⚠️ КРИТИЧЕСКИ ВАЖНО:** Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется. ### Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!) ```bash # Создать полную резервную копию репозитория cd /path/to/repo git clone --mirror . ../repo-backup-$(date +%Y%m%d-%H%M%S).git # Проверить размер du -sh ../repo-backup-*.git ``` **✅ Результат:** Резервная копия с полной историей (можно восстановить в любой момент) ### Шаг 2: Установка git-filter-repo ```bash # macOS brew install git-filter-repo # Linux pip3 install git-filter-repo # Проверка git filter-repo --version ``` ### Шаг 3: Удаление файлов из истории (ПРАВИЛЬНЫЙ СПОСОБ) **⚠️ КРИТИЧЕСКИ ВАЖНО:** Используйте `--path` БЕЗ `--invert-paths` для удаления конкретных файлов! ```bash # Создать bare clone для безопасной работы cd /tmp git clone --mirror /path/to/repo repo-clean.git # ✅ ПРАВИЛЬНО: Удалить конкретные файлы/папки cd repo-clean.git git filter-repo \ --path frontend/dist-zero-downtime/ \ --path frontend/dist-fast-no-image/ \ --path frontend/dist-via-proxy/ \ --path frontend/dist-cache-busting/ \ --path-glob '*.secret' \ --path-glob '*.key' \ --invert-paths \ --force # ❌ НЕПРАВИЛЬНО: --invert-paths может уничтожить все коммиты! # Используйте ТОЛЬКО если уверены что файлы НЕ в каждом коммите # Очистить reflog и неиспользуемые объекты git reflog expire --expire=now --all git gc --prune=now --aggressive ``` **⚠️ КРИТИЧЕСКОЕ ПРЕДУПРЕЖДЕНИЕ:** - `--invert-paths` **ОПАСЕН** - может уничтожить все коммиты если указанные пути были в каждом коммите - **Лучше использовать:** `--path` для удаления конкретных файлов БЕЗ `--invert-paths` - **Проверяйте результат:** `git ls-tree -r HEAD --name-only | wc -l` должно быть > 0! ### Шаг 4: Проверка целостности (КРИТИЧЕСКИ ВАЖНО!) ```bash cd repo-clean.git # ✅ КРИТИЧЕСКАЯ ПРОВЕРКА: Файлы в коммитах git ls-tree -r HEAD --name-only | wc -l # ДОЛЖНО БЫТЬ > 0! Если 0 - репозиторий сломан, восстанавливайте из бэкапа! # Проверить размер du -sh . # Должно быть меньше оригинала # Проверить количество коммитов git log --oneline --all | wc -l # Должно быть идентично оригиналу # Проверить что файлы удалены git rev-list --all --objects | grep -E "(dist-zero-downtime|dist-fast-no-image)" | wc -l # Должно быть: 0 # Проверить целостность git fsck --full # Не должно быть ошибок # Проверить важные ветки git branch -a | grep -E "(dev|main|staging)" # Все важные ветки должны быть сохранены ``` **✅ Гарантии:** - ✅ Все коммиты сохранены (количество идентично) - ✅ **Файлы в коммитах > 0** (критично!) - ✅ Все сообщения коммитов идентичны - ✅ Все авторы и даты сохранены - ✅ Все важные ветки сохранены - ✅ Все теги сохранены - ✅ Файлы удалены из истории **⚠️ КРИТИЧЕСКИ ВАЖНО:** Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется. #### Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!) ```bash # Создать полную резервную копию репозитория cd /path/to/repo git clone --mirror . ../repo-backup-$(date +%Y%m%d-%H%M%S).git # Проверить размер du -sh ../repo-backup-*.git ``` **✅ Результат:** Резервная копия с полной историей (можно восстановить в любой момент) #### Шаг 2: Установка git-filter-repo ```bash # macOS brew install git-filter-repo # Linux pip3 install git-filter-repo # Проверка git filter-repo --version ``` #### Шаг 3: Удаление файлов из истории ```bash # Создать bare clone для безопасной работы cd /tmp git clone --mirror /path/to/repo repo-clean.git # Удалить папки из истории cd repo-clean.git git filter-repo \ --path frontend/dist-zero-downtime/ \ --path frontend/dist-fast-no-image/ \ --path frontend/dist-via-proxy/ \ --path frontend/dist-cache-busting/ \ --invert-paths \ --force # Очистить reflog и неиспользуемые объекты git reflog expire --expire=now --all git gc --prune=now --aggressive ``` **Параметры:** - `--path` - путь к папке/файлу для удаления - `--path-glob` - glob паттерн для удаления (например, `*.secret`) - `--invert-paths` - ⚠️ **ОПАСНО!** Удалить указанные пути (остальное сохранить). Может уничтожить все коммиты! - `--force` - принудительное выполнение **✅ Результат:** Очищенный репозиторий без указанных файлов **⚠️ КРИТИЧЕСКОЕ ПРЕДУПРЕЖДЕНИЕ:** - `--invert-paths` **ОПАСЕН** - если указанные пути были в КАЖДОМ коммите, все коммиты станут пустыми! - **Всегда проверяйте:** `git ls-tree -r HEAD --name-only | wc -l` должно быть > 0 после очистки! - **Если результат = 0:** Репозиторий сломан, восстанавливайте из бэкапа! #### Шаг 4: Проверка целостности (ОБЯЗАТЕЛЬНО!) ```bash cd repo-clean.git # Проверить размер du -sh . # Должно быть меньше оригинала # Проверить количество коммитов git log --oneline --all | wc -l # Должно быть идентично оригиналу # Проверить что файлы удалены git rev-list --all --objects | grep -E "(dist-zero-downtime|dist-fast-no-image)" | wc -l # Должно быть: 0 # Проверить целостность git fsck --full # Не должно быть ошибок # Проверить важные ветки git branch -a | grep -E "(dev|main|staging)" # Все важные ветки должны быть сохранены ``` **✅ Гарантии:** - ✅ Все коммиты сохранены (количество идентично) - ✅ Все сообщения коммитов идентичны - ✅ Все авторы и даты сохранены - ✅ Все важные ветки сохранены - ✅ Все теги сохранены - ✅ Файлы удалены из истории #### Шаг 5: Применение очищенной версии **⚠️ ПРОБЛЕМА:** Прямой push в Gitea таймаутит из РФ, а скрипт через прокси не работает, потому что после замены `.git` нет remote tracking branches. **✅ РЕШЕНИЕ: Bundle метод + прямое копирование на сервер** ```bash # 1. Создать bundle из очищенного репозитория cd /tmp/repo-clean.git git bundle create /tmp/repo-clean.bundle --all # 2. Загрузить bundle на production сервер scp -i ~/.ssh/hunab_deploy_key /tmp/repo-clean.bundle hunab@209.38.32.21:/tmp/ # 3. Создать временный репозиторий из bundle на сервере ssh hunab-prod "cd /tmp && git clone --mirror /tmp/repo-clean.bundle repo-clean.git" # 4. Бэкап текущего Gitea репозитория GITEA_REPO="/data/git/repositories/hunabgit/hunabapp.git" ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)" # 5. Копировать очищенный репозиторий в контейнер ssh hunab-prod "docker cp /tmp/repo-clean.git gitea:/tmp/repo-clean.git" # 6. Заменить objects и refs в Gitea репозитории ssh hunab-prod "docker exec gitea sh -c ' cd $GITEA_REPO rm -rf objects packed-refs refs/heads refs/tags cp -r /tmp/repo-clean.git/objects . cp -r /tmp/repo-clean.git/packed-refs . 2>/dev/null || true cp -r /tmp/repo-clean.git/refs/heads refs/ 2>/dev/null || true cp -r /tmp/repo-clean.git/refs/tags refs/ 2>/dev/null || true echo \"ref: refs/heads/dev\" > HEAD rm -rf refs/original filter-repo git reflog expire --expire=now --all 2>/dev/null git gc --prune=now 2>&1 | tail -5 chown -R git:git . '" # 7. Перезапустить Gitea для обновления индексов ssh hunab-prod "docker restart gitea && sleep 5" # 8. Проверка ssh hunab-prod "docker exec gitea sh -c 'cd $GITEA_REPO && \ echo \"Размер: \$(du -sh .)\" && \ echo \"Коммитов: \$(git log --oneline --all | wc -l)\" && \ echo \"Файлов dist в истории: \$(git rev-list --all --objects | grep -cE \"dist-zero-downtime|dist-fast-no-image|dist-via-proxy|dist-cache-busting\" || echo 0)\" && \ echo \"Веток: \$(git branch | wc -l)\" && \ echo \"Тегов: \$(git tag | wc -l)\" && \ git log --oneline -3 dev'" ``` **✅ Результат:** Gitea репозиторий обновлен очищенной версией #### Шаг 6: Обновление локального репозитория ```bash # Заменить .git папку очищенной версией cd /path/to/repo rm -rf .git cp -r /tmp/repo-clean.git .git # Преобразовать bare в обычный репозиторий git config core.bare false git config core.worktree . # Проверить статус git status git log --oneline -5 ``` **⚠️ ВАЖНО:** После замены `.git` могут появиться артефакты в `git status` (untracked файлы из `.git`). Это не критично - репозиторий работает корректно. ### 📊 Результаты: Простое удаление из tracking (ПРАВИЛЬНЫЙ подход) **До удаления:** - Файлов отслеживается: 13,281 - Файлов dist-* в tracking: 4,046 - Коммитов: 6,218 **После удаления:** - Файлов отслеживается: 13,281 - 4,046 = 9,235 - Файлов dist-* в tracking: 0 ✅ - Коммитов: 6,218 (все сохранены, SHA не изменились) ✅ - Веток: 27 (все важные сохранены) ✅ - Тегов: 162 (все сохранены) ✅ - Время выполнения: ~30 секунд ✅ - Риск: Минимальный (простой коммит) ✅ **⚠️ ВАЖНО:** Файлы останутся в истории старых коммитов, но новые коммиты их не будут содержать. Это нормально для build-артефактов. ### 📊 Результаты: Очистка истории (если действительно нужна) **До очистки:** - Размер: 968 MB - Файлов в истории: 4,046 из `dist-*` папок - Коммитов: 6,218 **После очистки (если все прошло правильно):** - Размер: 391 MB (экономия 577 MB, 60%) - Файлов в истории: 0 из `dist-*` папок - Коммитов: 6,218 (все сохранены, но SHA изменились) - Веток: 27 (все важные сохранены) - Тегов: 162 (все сохранены) - Время выполнения: ~30-60 минут (с бэкапом и проверками) - Риск: Высокий (переписывание истории) ⚠️ **❌ Результаты после ОШИБКИ (git-filter-repo с --invert-paths):** - Файлов в коммитах: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!) ❌ - Репозиторий: СЛОМАН ❌ - Решение: Восстановление из бэкапа ✅ ### 🛡️ Механизмы защиты **Для простого удаления из tracking:** 1. ✅ **Проверка .gitignore:** Убедиться что файлы в `.gitignore` 2. ✅ **Проверка staging:** `git status --short | grep "^D "` показывает удаляемые файлы 3. ✅ **Проверка после коммита:** `git ls-files | grep dist- | wc -l` = 0 **Для очистки истории (если действительно нужна):** 1. ✅ **Резервная копия:** Полная копия оригинального репозитория (ОБЯЗАТЕЛЬНО!) 2. ✅ **Бэкап на сервере:** Копия Gitea репозитория перед заменой 3. ✅ **КРИТИЧЕСКАЯ проверка:** `git ls-tree -r HEAD --name-only | wc -l` > 0 после очистки! 4. ✅ **Проверка целостности:** `git fsck --full` перед применением 5. ✅ **Валидация:** Проверка количества коммитов, веток, тегов, файлов в коммитах ### ⚠️ Критические ошибки и решения #### ❌ Ошибка 0: git-filter-repo уничтожил все коммиты (КРИТИЧЕСКАЯ!) **Проблема:** После `git-filter-repo --invert-paths` все коммиты стали пустыми (0 файлов). **Симптомы:** ```bash git ls-tree -r HEAD --name-only | wc -l # Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!) git log --oneline -5 # Коммиты есть, но в них НЕТ ФАЙЛОВ git status # Показывает ВСЕ файлы как untracked ``` **Причина:** `--invert-paths` удалил указанные пути из ВСЕХ коммитов. Если эти пути были в каждом коммите, все коммиты стали пустыми. **✅ Решение:** ```bash # 1. Восстановить из бэкапа rm -rf .git mv .git.backup-20260227-171445 .git # Используйте ваш бэкап # 2. Использовать ПРАВИЛЬНЫЙ подход (простое удаление из tracking) git rm --cached -r frontend/dist-*/ && git commit -m "..." # 3. НЕ использовать git-filter-repo для build-артефактов! ``` **Профилактика:** - ✅ **ВСЕГДА** проверяйте `git ls-tree -r HEAD --name-only | wc -l` после git-filter-repo - ✅ **ВСЕГДА** создавайте полный бэкап перед очисткой истории - ✅ **НЕ используйте** `--invert-paths` для файлов, которые были в каждом коммите - ✅ **Для build-артефактов** используйте простое удаление из tracking (см. "ПРАВИЛЬНЫЙ ПОДХОД" выше) #### Ошибка 1: Прямой push таймаутит **Проблема:** `git push --force` зависает с таймаутом из РФ. **Решение:** Использовать bundle метод + прямое копирование на сервер (см. Шаг 5). #### Ошибка 2: Скрипт через прокси не работает **Проблема:** После замены `.git` нет remote tracking branches (`origin/dev` не существует). **Решение:** Bundle метод обходит эту проблему, так как работает напрямую с файловой системой. #### Ошибка 3: Файлы из `.git` в корне проекта **Проблема:** После замены `.git` git status показывает untracked файлы (COMMIT_EDITMSG, HEAD, config и т.д.). **Решение:** Это артефакты git status - файлы не существуют в корне, можно игнорировать. Репозиторий работает корректно. #### Ошибка 4: Gitea не видит изменения **Проблема:** После замены объектов Gitea не обновляет индекс. **Решение:** 1. Удалить `refs/original` и `filter-repo` из репозитория 2. Очистить reflog: `git reflog expire --expire=now --all` 3. Перезапустить Gitea: `docker restart gitea` ### 🔄 Восстановление из резервной копии Если что-то пошло не так: ```bash # Восстановить локальный репозиторий cd /path/to/repo rm -rf .git git clone --mirror /path/to/backup/repo-backup-*.git .git git config core.bare false git config core.worktree . # Восстановить Gitea репозиторий на сервере ssh hunab-prod "docker exec gitea rm -rf $GITEA_REPO && \ docker exec gitea cp -r ${GITEA_REPO}.backup-* $GITEA_REPO && \ docker exec gitea chown -R git:git $GITEA_REPO && \ docker restart gitea" ``` ### 📋 Чеклист: Простое удаление из tracking (99% случаев) **Для build-артефактов (`dist/`, `build/`) - используйте этот чеклист:** - [ ] Проверено что файлы в `.gitignore` - [ ] Удалены файлы из tracking: `git rm --cached -r frontend/dist-*/` - [ ] Проверено что файлы в staging для удаления: `git status --short | grep "^D "` - [ ] Создан коммит с описанием удаления - [ ] Push в Gitea через скрипт: `bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev` - [ ] Проверено что файлы больше не отслеживаются: `git ls-files | grep dist- | wc -l` = 0 - [ ] Обновлен CHANGELOG с описанием операции **✅ Результат:** Файлы удалены из tracking, история НЕ переписана, все работает быстро и безопасно. --- ### 📋 Чеклист: Очистка истории (1% случаев - только для секретов/юридических требований) **⚠️ ВАЖНО:** Используйте ТОЛЬКО если действительно нужно удалить файлы из ВСЕЙ истории! - [ ] Определено что простая очистка tracking НЕДОСТАТОЧНА (секреты, юридические требования) - [ ] Создана резервная копия оригинального репозитория - [ ] Установлен `git-filter-repo` - [ ] Создан bare clone для безопасной работы - [ ] Удалены файлы из истории через `git-filter-repo` (БЕЗ `--invert-paths` если возможно!) - [ ] **КРИТИЧЕСКАЯ ПРОВЕРКА:** `git ls-tree -r HEAD --name-only | wc -l` > 0 (если 0 - репозиторий сломан!) - [ ] Очищены reflog и неиспользуемые объекты - [ ] Проверена целостность (коммиты, ветки, теги, файлы в коммитах > 0) - [ ] Создан bundle из очищенного репозитория - [ ] Загружен bundle на production сервер (в `/home/hunab/`, НЕ в `/tmp/` - tmpfs только 2GB!) - [ ] Создан бэкап Gitea репозитория на сервере - [ ] Заменены objects и refs в Gitea репозитории - [ ] Удалены `refs/original` и `filter-repo` - [ ] Перезапущен Gitea - [ ] Проверен результат (размер, коммиты, файлы в коммитах > 0) - [ ] Обновлен локальный репозиторий - [ ] Обновлен CHANGELOG с описанием операции ### 📚 Связанные документы - **[Deployment Changelog](../CHANGELOG.md)** - История изменений deployment (включает очистку истории) - **[Git Filter Repo Documentation](https://github.com/newren/git-filter-repo)** - Официальная документация git-filter-repo --- **Последнее обновление:** 2026-02-27 **Версия документа:** 1.6 **Статус:** ✅ Gitea полностью развернут и работает стабильно **Изменения:** - Добавлен резервный способ коммитов через временный скрипт (2026-02-16) - Добавлен гайд по очистке истории Git от build-артефактов (2026-02-27) - **КРИТИЧЕСКОЕ ОБНОВЛЕНИЕ (2026-02-27):** Добавлены четкие предупреждения о том, как НЕ надо делать очистку истории: - ❌ `git-filter-repo --invert-paths` УНИЧТОЖАЕТ все коммиты (реальный пример ошибки 2026-02-27) - ✅ Правильный подход: простое удаление из tracking (`git rm --cached`) для build-артефактов (99% случаев) - ✅ Очистка истории нужна ТОЛЬКО для секретов/юридических требований (1% случаев) - ✅ Добавлены критические проверки (`git ls-tree -r HEAD --name-only | wc -l` > 0) и механизмы защиты - **ОПТИМИЗАЦИЯ (2026-02-27):** Исправлена медленная верификация push - заменен `git fetch origin` на `git update-ref` для мгновенного обновления tracking ref без сетевых запросов