Add proxy/VPN/telegram launchd and emergency runbooks; reorganize apps docs; document JA3 CrossOver runbook and Wine troubleshooting; add GOG/HoMM game scripts, disk cleanup guides, and gitea push-via-proxy helper. Ignore temp/. Co-authored-by: Cursor <cursoragent@cursor.com>
82 KiB
Gitea Complete Guide — local_machine
Этот гайд — справочник по использованию Gitea для репозитория local_machine (документация и скрипты локальной машины).
Сервер: gitea.hunab.app (209.38.32.21).
Быстрый старт для local_machine: LOCAL_MACHINE_GITEA_SETUP.md.
Для других репозиториев: legal на 206.189.35.205 — GITEA_LEGAL_206_SERVER_GUIDE.md; hunabapp — свои скрипты пуша в том репо.
Дата: 2026-01-02
Версия Gitea: 1.25.3
Статус: ✅ Полностью развернут и работает
Последнее обновление: Адаптация под проект local_machine (2026-03-14)
📋 Обзор
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 не используются.
🚀 Развертывание
Быстрый старт для local_machine
Репозиторий local_machine не разворачивает Gitea — он только пушит в уже работающий сервер. Первая настройка и пуш:
cd /Users/eternal/code/local_machine
# 1. Создать репо на https://gitea.hunab.app (New Repository → local_machine)
# 2. Инициализация и пуш:
bash scripts/deployment/gitea/setup-and-push.sh
Подробно: LOCAL_MACHINE_GITEA_SETUP.md.
Развертывание самого Gitea (для админов сервера)
Скрипты развертывания Gitea на сервере находятся в других репозиториях (например hunabapp). Пример для справки:
# Развертывание 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)
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):
[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):
[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.
Решение:
Автоматический скрипт (рекомендуется)
# На сервере 206.189.35.205
bash scripts/deployment/fix-postgres-utils-user.sh
Ручное создание пользователя
# Определение суперпользователя 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();"
Диагностика проблем:
-
"role "utils" does not exist" - Пользователь не создан:
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD';" -
"password authentication failed" - Пароль неверный:
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD';" -
"permission denied for database" - Нет прав:
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;"
Проверка после создания:
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
# Получение сертификата
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:
# 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 репозиториев (КРИТИЧЕСКИ ВАЖНО)
✅ Для репозитория local_machine
В этом проекте пуш делается из корня local_machine:
cd /Users/eternal/code/local_machine
git push -u origin main
Если из РФ push по HTTPS зависает — переключите remote на SSH (порт 2223):
git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
git push -u origin main
Если и SSH недоступен — используйте метод через bundle ниже. Полная настройка: LOCAL_MACHINE_GITEA_SETUP.md.
❌ Проблема: Push через HTTPS зависает
При попытке git push через HTTPS возникают таймауты:
fatal: unable to access 'https://gitea.hunab.app/...': Recv failure: Operation timed out
Корневые причины:
- HTTP/2 несовместим с git операциями (git использует HTTP/1.1)
- Недостаточные таймауты для больших репозиториев
- Проблемы с keepalive соединениями через reverse proxy
- Ограничения curl/git на стороне клиента (особенно на macOS)
Диагностика в логах Gitea:
Fail to serve RPC(receive-pack): exit status 128
fatal: the remote end hung up unexpectedly
POST запрос доходит до Gitea, но соединение обрывается во время передачи данных.
✅ Универсальный скрипт пуша (в других репо)
В репозиториях с полной инфраструктурой (например hunabapp) используется скрипт, который автоматически выбирает метод (SSH → HTTPS → bundle):
# Обычный скрипт (для работы не из РФ)
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
# 🇷🇺 ДЛЯ РФ: Скрипт через прокси (ускоряет в 5-10 раз)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]
Для local_machine такого скрипта нет — используйте git push origin main или SSH (см. выше).
🇷🇺 Из РФ:
- Предпочтительно SSH (порт 2223) — часто стабильнее HTTPS.
- Либо метод через bundle (см. раздел ниже).
✅ Резервный способ: Временный скрипт для коммита и пуша
Когда использовать:
- Терминал недоступен из-за проблем с zsh/shell
- Агент не может выполнить команды напрямую
- Нужно подготовить все команды заранее
Процесс:
- Агент создает временный скрипт в
temp/commit-and-push.shс полным коммитом и push командой - Пользователь выполняет скрипт вручную в терминале
- Скрипт автоматически делает
git add,git commitиgit push
Формат временного скрипта (для local_machine):
#!/bin/bash
set -e
echo "📦 Committing changes..."
git add -A
echo "💾 Creating commit..."
git commit -m "docs: описание изменений"
echo "🚀 Pushing to Gitea..."
git push origin main
echo "✅ Done!"
Требования к скрипту:
- ✅ Должен быть исполняемым (
chmod +x) - ✅ Должен содержать полное сообщение коммита с описанием изменений
- ✅ Для local_machine:
git push origin main(или SSH remote, см. выше) - ✅ Должен быть в папке
temp/для временных скриптов - ✅ Должен иметь понятные echo сообщения для пользователя
Использование:
# Выполнить скрипт
bash temp/commit-and-push.sh
Очистка: После успешного выполнения скрипт можно удалить:
rm temp/commit-and-push.sh
⚠️ ВАЖНО:
- Скрипт должен быть создан агентом ПЕРЕД запросом пользователя на коммит
- Агент должен ВСЕГДА готовить скрипт, если терминал недоступен
- Скрипт должен содержать ПОЛНОЕ описание изменений в коммите
Стандарт: без трейлеров в коммитах
В проекте не используются git-трейлеры в сообщениях коммитов (строки вида Key: Value в конце сообщения). Не добавлять:
Made-with: Cursorи аналогичные метки инструментаCo-authored-by:,Signed-off-by:и т.п., если это не требуется явно политикой репозитория
Коммиты должны содержать только заголовок и при необходимости тело сообщения в читаемом формате.
Проверки перед коммитом (опционально)
Если в проекте есть проверка типов или линтеры — их можно запускать перед коммитом по желанию. Для local_machine (документация и скрипты) обычно достаточно git add и git commit.
(В проекте local_machine нет frontend/backend — при необходимости добавьте свои проверки в скрипты.)
Для других репо: проверка прав на .env после деплоя и typecheck-скрипты — в документации того проекта.
Метод 1: HTTPS с оптимизированными настройками
# Настройка git для больших push
git config http.postBuffer 524288000
git config http.lowSpeedLimit 0
git config http.lowSpeedTime 0
git config http.timeout 600
# Попытка push (для local_machine — ветка main)
git push origin main
⚠️ На macOS: HTTPS push часто не работает из-за отсутствия команды timeout. Скрипт автоматически пропускает HTTPS и использует bundle метод.
Метод 2: Bundle метод (надежный fallback)
Если HTTPS не работает, используется прямой доступ через файловую систему:
# Создать bundle (для local_machine — ветка main)
git bundle create /tmp/push.bundle origin/main..main
# Скопировать на сервер (hunab-prod — хост с доступом к Docker Gitea)
scp /tmp/push.bundle hunab-prod:/tmp/
# Применить в Gitea (репо local_machine)
ssh hunab-prod "docker cp /tmp/push.bundle gitea:/tmp/push.bundle && \
docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && \
git bundle unbundle /tmp/push.bundle && \
git update-ref refs/heads/main \$(git bundle list-heads /tmp/push.bundle | grep main | cut -d\" \" -f1) && \
rm /tmp/push.bundle' && rm /tmp/push.bundle"
✅ Статус: Bundle метод всегда работает и обходит все проблемы с таймаутами.
Сравнение методов
| Метод | Скорость | Надежность | Сложность | Платформа |
|---|---|---|---|---|
| SSH | ⚡⚡⚡ Быстро | ✅✅✅ Высокая | ✅ Просто | Все |
| HTTPS (оптимизированный) | ⚡⚡ Средне | ✅✅ Средняя | ✅ Просто | Linux |
| Bundle (прямой доступ) | ⚡ Медленно | ✅✅✅ Всегда работает | ⚠️ Сложнее | Все |
Рекомендация: Используйте автоматический скрипт - он выберет лучший метод автоматически.
Миграция репозиториев
Для регулярных push в local_machine: git push origin main или SSH (см. раздел «Для репозитория local_machine» выше). В других репо может быть скрипт git-push-gitea.sh.
Шаг 1: Создание bundle из локального репозитория
cd /Users/eternal/code/local_machine
git bundle create /tmp/local_machine-migration.bundle --all
Шаг 2: Копирование bundle на сервер
scp /tmp/local_machine-migration.bundle hunab-prod:/tmp/local_machine-migration.bundle
Шаг 3: Распаковка bundle на сервере
ssh hunab-prod "cd /tmp && git clone --mirror local_machine-migration.bundle local_machine-mirror.git"
Шаг 4: Копирование в директорию Gitea
ssh hunab-prod "
docker exec gitea mkdir -p /data/git/repositories/hunabgit/
docker cp /tmp/local_machine-mirror.git gitea:/data/git/repositories/hunabgit/local_machine.git
docker exec gitea chown -R git:git /data/git/repositories/hunabgit/local_machine.git
"
Шаг 5: Распаковка refs из packed-refs
КРИТИЧЕСКИ ВАЖНО: Gitea не видит ветки в packed-refs, нужно распаковать:
ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'"
Шаг 6: Обновление базы данных
ssh hunab-prod "
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'local_machine';\"
docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini
docker restart gitea
"
Шаг 7: Проверка
# Проверка веток через API
curl -s -H "Authorization: token YOUR_TOKEN" \
https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/branches | jq '.[] | .name'
# Проверка файлов через API (ветка main)
curl -s -H "Authorization: token YOUR_TOKEN" \
https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/git/trees/main?recursive=0 | jq '.tree[] | .path'
# Веб-интерфейс
# https://gitea.hunab.app/hunabgit/local_machine
✅ Результат: Репозиторий полностью мигрирован, все файлы доступны через веб-интерфейс и API.
Аутентификация
Создание токена доступа
- Откройте: https://gitea.hunab.app/user/settings/applications
- Generate New Token
- Название:
api-access - Права:
write:repository,read:repository,read:user - Скопируйте токен
Использование токена в git
# В локальном репозитории local_machine
cd /Users/eternal/code/local_machine
git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/local_machine.git
🏷️ Создание тегов и Release (СТАНДАРТ)
⚠️ КРИТИЧЕСКИ ВАЖНО: Gitea не обновляет индекс тегов автоматически
Проблема: Если создать тег напрямую в git репозитории (git tag -a v1.0.0), Gitea НЕ покажет его в веб-интерфейсе автоматически. Gitea кэширует список тегов и обновляет его только при создании Release через веб-интерфейс или API.
✅ СТАНДАРТ: Создание тега через базу данных (РЕКОМЕНДУЕТСЯ)
Используйте этот метод для автоматического создания тегов:
# 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 = 'local_machine';\" | 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 = 'local_machine';)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- Заголовок Releasenote- Описание Releaseis_tag-trueдля тега,falseдля Release
✅ Альтернатива: Создание через веб-интерфейс
Если нужно создать тег вручную:
- Откройте: https://gitea.hunab.app/hunabgit/local_machine/releases/new
- В поле "Tag" введите имя тега (например,
v1.3.7-CRM) - В поле "Target" выберите commit из списка или введите SHA
- Заполните Title и Description
- Нажмите "Publish Release"
⚠️ Недостаток: Требует ручного действия, не автоматизируется.
✅ Альтернатива: Создание через API (требует токен)
# 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/local_machine/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
# ❌ НЕ РАБОТАЕТ - 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/local_machine/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 scripts/deployment/gitea/create-gitea-tag.sh \
<tag-name> \
<commit-sha> \
<title> \
[description] \
[branch]
# 🇷🇺 ДЛЯ РФ: Через прокси (рекомендуется из РФ)
bash scripts/deployment/gitea/create-gitea-tag-via-proxy.sh \
<tag-name> \
<commit-sha> \
<title> \
[description] \
[branch]
Пример:
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
Что делает скрипт:
- ✅ Валидирует параметры (формат тега, SHA коммита)
- ✅ Получает ID репозитория из базы данных
- ✅ Создает Release в таблице
release - ✅ Перезапускает Gitea для обновления индекса
- ✅ Выводит ссылку для проверки
Особенности:
- Автоматически обрабатывает конфликты (обновляет существующий тег)
- Экранирует специальные символы в SQL
- Проверяет существование репозитория
- Выводит понятные сообщения об ошибках
📊 Мониторинг
Проверка статуса
# Контейнер
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'
Проверка репозитория
# Ветки
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a | wc -l"
# Размер
ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/local_machine.git"
# Последний коммит
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git log -1 --pretty=format:'%H %s'"
🔧 Troubleshooting
Для детальной диагностики проблем см. GITEA_TROUBLESHOOTING.md - Полное руководство по решению проблем.
Для диагностики зеркального сервера см. GITEA_MIRROR_TROUBLESHOOTING.md.
Gitea не запускается
# Проверка логов
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 для всех репозиториев:
ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini"
Проверка:
# Проверка прав на hooks
ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive"
# Проверка содержимого hooks
ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive.d/gitea"
Дополнительно: Если проблема сохраняется, проверьте:
- Файловая система поддерживает выполнение (
chmod +xработает) - Файловая система не смонтирована с
noexec - 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)
Решение:
- ✅ Используйте универсальный скрипт:
bash scripts/deployment/gitea/git-push-gitea.sh [branch] - ✅ Скрипт автоматически выбирает лучший метод (SSH > HTTPS > Bundle)
- ✅ На macOS автоматически использует bundle метод (обходит проблемы с HTTPS)
✅ Статус: Систематически решено через универсальный скрипт (2026-01-02)
SSL сертификат не обновляется
# Проверка автообновления
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"
🔒 Безопасность
Рекомендации
- ✅ HTTPS через Let's Encrypt (настроено)
- ✅ Modern SSL/TLS protocols (TLSv1.2, TLSv1.3)
- ✅ Security headers (HSTS, X-Frame-Options, etc.)
- ⚠️ Firewall для ограничения доступа (рекомендуется)
- ✅ SSH ключи для git операций
- ✅ Регулярные бэкапы данных
Бэкапы
# Полный бэкап 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
"
📈 Итоги и выводы
Что было достигнуто
- ✅ Gitea развернут на production (209.38.32.21)
- ✅ PostgreSQL настроен (отдельная БД hunabgit)
- ✅ SSL/TLS настроен (Let's Encrypt, A rating)
- ✅ Nginx reverse proxy (с оптимизацией)
- ✅ Репозиторий local_machine (и при необходимости другие репо) доступен на Gitea
- ✅ DNS настроен (gitea.hunab.app → 209.38.32.21)
- ✅ Веб-интерфейс работает (файлы отображаются корректно)
- ✅ 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
- Прямое копирование эффективнее push для больших репозиториев
- packed-refs не поддерживается Gitea — нужна распаковка
- certbot --standalone проще для новых сертификатов
- Docker network критичен для PostgreSQL подключения
- 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)
Следующие шаги (опционально)
- Настроить CI/CD в Gitea (Gitea Actions)
- Настроить зеркалирование GitHub ↔ Gitea (для резервирования)
- Настроить SSH ключи для бесшовной работы
- Добавить DNS CAA запись для дополнительной безопасности
- Настроить автоматические бэкапы (cron job)
- Мигрировать deployment скрипты на использование Gitea
📚 Полезные команды
Для local_machine:
cd /Users/eternal/code/local_machine
git push origin main
# или SSH (если HTTPS таймаутит): git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
Для админов сервера (Gitea Admin):
docker exec gitea /usr/local/bin/gitea admin --help
docker exec gitea /usr/local/bin/gitea admin regenerate hooks
# Git операции в Gitea (репо local_machine)
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.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 на зеркальный сервер
📋 План миграции: См. 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 Migration to Mirror Server - План миграции Gitea на зеркальный сервер (206.189.35.205)
- Gitea Mirror Troubleshooting - Диагностика зеркального сервера
- Gitea Legal — сервер 206.189.35.205 - Стандарт для репозитория legal
Быстрый старт:
# Развертывание зеркального сервера
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/
- Репозиторий local_machine: https://gitea.hunab.app/hunabgit/local_machine
- Быстрый старт local_machine: LOCAL_MACHINE_GITEA_SETUP.md
- Документация Gitea: https://docs.gitea.io/
- SSL Report: https://www.ssllabs.com/ssltest/analyze.html?d=gitea.hunab.app
- Зеркалирование: Gitea Mirroring Guide (если есть)
- Миграция на зеркальный сервер: Gitea Migration Plan (если есть)
✅ Текущий статус
Репозиторий local_machine
- URL: https://gitea.hunab.app/hunabgit/local_machine
- Ветка по умолчанию: main
- Пуш:
git push origin mainили SSH (порт 2223) — см. LOCAL_MACHINE_GITEA_SETUP.md
Система (сервер Gitea)
- ✅ Gitea контейнер на 209.38.32.21
- ✅ PostgreSQL, SSL/TLS, Nginx
- ✅ DNS: gitea.hunab.app → 209.38.32.21
Последнее обновление: 2026-03-14
Версия документа: 2.0 (адаптация под проект local_machine)
Изменения:
- Гайд переориентирован на репозиторий local_machine; примеры и команды приведены к этому проекту
- Добавлена ссылка на LOCAL_MACHINE_GITEA_SETUP.md для быстрого старта
🧹 Очистка истории Git от build-артефактов
🎯 Когда нужна очистка vs простое удаление
99% случаев: Файлы уже в .gitignore, но Git их отслеживает (закоммичены до добавления в .gitignore)
Решение: ✅ Простое удаление из tracking (см. ниже) — БЕЗ переписывания истории!
Пример ниже — для репо с frontend (build-артефакты). В local_machine: убедиться, что путь в .gitignore, затем git rm --cached -r путь/ и коммит.
1% случаев: Нужно удалить файлы из ВСЕЙ истории (например, случайно закоммитили секреты)
Решение: ⚠️ Очистка истории (см. раздел "Когда действительно нужна очистка истории")
✅ ПРАВИЛЬНЫЙ ПОДХОД: Простое удаление из tracking
Когда использовать: Файлы уже в .gitignore, но Git их отслеживает.
Пример проблемы:
- Файлы в
.gitignore:frontend/dist-* - Git все еще отслеживает: 4046 файлов из
dist-*папок git statusпоказывает:D frontend/dist-cache-busting/404.htmlи т.д.
✅ Решение (ПРАВИЛЬНО):
# 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 (для local_machine: git push origin main или SSH)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev # в других репо; для local_machine: git push origin main
# 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
❌ КРИТИЧЕСКАЯ ОШИБКА (НЕ ДЕЛАТЬ ТАК!):
# ❌ НЕПРАВИЛЬНО - УНИЧТОЖАЕТ ВСЕ КОММИТЫ!
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
# Коммиты есть, но в них НЕТ ФАЙЛОВ
✅ Решение после ошибки:
# Восстановить из бэкапа
rm -rf .git
mv .git.backup-20260227-171445 .git
# Использовать ПРАВИЛЬНЫЙ подход (см. выше)
git rm --cached -r frontend/dist-*/ && git commit -m "..."
⚠️ Когда действительно нужна очистка истории
Редкие случаи, когда нужна очистка истории:
-
Случайно закоммитили секреты (пароли, API ключи, токены)
- ⚠️ Критично: секреты в истории = уязвимость безопасности
- ✅ Нужна полная очистка истории
-
Огромные бинарные файлы (видео, большие изображения, базы данных)
- ⚠️ Размер репозитория > 2GB из-за истории
- ✅ Нужна очистка для уменьшения размера
-
Юридические требования (удаление файлов по запросу)
- ⚠️ Требуется полное удаление из истории
- ✅ Нужна очистка истории
⚠️ ВАЖНО: Для build-артефактов (dist/, build/) очистка истории НЕ НУЖНА - достаточно простого удаления из tracking!
🧹 Очистка истории от файлов с секретами (docs)
Когда применять: после замены реальных секретов на плейсхолдеры в документации (см. SECRETS_AND_FILE_PERMISSIONS_POLICY.md) нужно удалить из истории Git старые версии этих файлов, чтобы секреты нельзя было извлечь из старых коммитов.
Гарантированно безопасный порядок (обязательно):
- Бэкап репозитория (ОБЯЗАТЕЛЬНО) — без бэкапа не приступать.
- Запуск скрипта, который делает бэкап сам и выполняет очистку по списку путей.
- Проверки целостности (в скрипте).
- Применение очищенной версии на 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 почищенные версии файлов (один коммит с плейсхолдерами).
Запуск (из корня репозитория):
# Установить 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/local_machine
Файлы:
- Список путей (файлы, которые удаляются из истории):
scripts/maintenance/git-history-cleanup-secrets-paths.txt— соответствует таблице в SECRETS_AND_FILE_PERMISSIONS_POLICY.md (очистка 2026-03-03). - Скрипт:
scripts/maintenance/git-history-cleanup-secrets.sh.
После скрипта: выполнить шаги из раздела ниже «Правильная очистка истории»: создание bundle из очищенного bare-репо, загрузка на сервер, бэкап Gitea-репо на сервере, замена objects/refs, перезапуск Gitea, обновление локального репо.
Критично: на production-сервере перед заменой репозитория в Gitea обязательно создать бэкап:
GITEA_REPO="/data/git/repositories/hunabgit/local_machine.git"
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"
✅ Правильная очистка истории (если действительно нужна)
⚠️ КРИТИЧЕСКИ ВАЖНО: Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется.
Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!)
# Создать полную резервную копию репозитория
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
# macOS
brew install git-filter-repo
# Linux
pip3 install git-filter-repo
# Проверка
git filter-repo --version
Шаг 3: Удаление файлов из истории (ПРАВИЛЬНЫЙ СПОСОБ)
⚠️ КРИТИЧЕСКИ ВАЖНО: Используйте --path БЕЗ --invert-paths для удаления конкретных файлов!
# Создать 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: Проверка целостности (КРИТИЧЕСКИ ВАЖНО!)
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: Резервная копия (ОБЯЗАТЕЛЬНО!)
# Создать полную резервную копию репозитория
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
# macOS
brew install git-filter-repo
# Linux
pip3 install git-filter-repo
# Проверка
git filter-repo --version
Шаг 3: Удаление файлов из истории
# Создать 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: Проверка целостности (ОБЯЗАТЕЛЬНО!)
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 метод + прямое копирование на сервер
# 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/local_machine.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/main\" > 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 main'"
✅ Результат: Gitea репозиторий обновлен очищенной версией
Шаг 6: Обновление локального репозитория
# Заменить .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:
- ✅ Проверка .gitignore: Убедиться что файлы в
.gitignore - ✅ Проверка staging:
git status --short | grep "^D "показывает удаляемые файлы - ✅ Проверка после коммита:
git ls-files | grep dist- | wc -l= 0
Для очистки истории (если действительно нужна):
- ✅ Резервная копия: Полная копия оригинального репозитория (ОБЯЗАТЕЛЬНО!)
- ✅ Бэкап на сервере: Копия Gitea репозитория перед заменой
- ✅ КРИТИЧЕСКАЯ проверка:
git ls-tree -r HEAD --name-only | wc -l> 0 после очистки! - ✅ Проверка целостности:
git fsck --fullперед применением - ✅ Валидация: Проверка количества коммитов, веток, тегов, файлов в коммитах
⚠️ Критические ошибки и решения
❌ Ошибка 0: git-filter-repo уничтожил все коммиты (КРИТИЧЕСКАЯ!)
Проблема: После git-filter-repo --invert-paths все коммиты стали пустыми (0 файлов).
Симптомы:
git ls-tree -r HEAD --name-only | wc -l
# Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!)
git log --oneline -5
# Коммиты есть, но в них НЕТ ФАЙЛОВ
git status
# Показывает ВСЕ файлы как untracked
Причина: --invert-paths удалил указанные пути из ВСЕХ коммитов. Если эти пути были в каждом коммите, все коммиты стали пустыми.
✅ Решение:
# 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 не обновляет индекс.
Решение:
- Удалить
refs/originalиfilter-repoиз репозитория - Очистить reflog:
git reflog expire --expire=now --all - Перезапустить Gitea:
docker restart gitea
🔄 Восстановление из резервной копии
Если что-то пошло не так:
# Восстановить локальный репозиторий
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 (local_machine:
git push origin mainили SSH) - Проверено что файлы больше не отслеживаются:
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 - История изменений deployment (включает очистку истории)
- Git Filter Repo Documentation - Официальная документация 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 без сетевых запросов