145 lines
8.8 KiB
Bash
145 lines
8.8 KiB
Bash
#!/usr/bin/env bash
|
||
# Робот ежесуточной синхронизации: проверяет обновления у автора проекта и, если они есть,
|
||
# переносит их в нашу ветку dev — но только когда всё сошлось.
|
||
#
|
||
# Где живёт: запускается из .gitea/workflows/fork-sync.yml на раннере (CT 114 «runner»).
|
||
# Что НЕ делает: не выпускает релиз и не подписывает его — закрытый ключ подписи остаётся
|
||
# только на рабочей станции владельца, поэтому версия автора публикуется вручную.
|
||
#
|
||
# Переменные окружения (все имеют значения по умолчанию, кроме TOKEN):
|
||
# TOKEN токен Gitea с правом записи в репозиторий (берётся из секретов workflow)
|
||
# GITEA_URL внутренний адрес Gitea (снаружи контейнер её не видит: имя резолвится в публичный IP)
|
||
# REPO владелец/репозиторий
|
||
# UPSTREAM адрес авторского репозитория
|
||
# WORK рабочая копия на раннере
|
||
# BRANCH наша ветка для синхронизации
|
||
# LOG файл отчёта последнего запуска (его читают из контейнера руками)
|
||
|
||
set -Eeuo pipefail
|
||
|
||
GITEA_URL=${GITEA_URL:-http://10.10.10.54:3000}
|
||
REPO=${REPO:-hacker07/3X-UI_autoVPS}
|
||
UPSTREAM=${UPSTREAM:-https://github.com/itsnotkubrick/3X-UI_KIT}
|
||
WORK=${WORK:-/opt/kit-fork-sync/repo}
|
||
BRANCH=${BRANCH:-dev}
|
||
LOG=${LOG:-/opt/kit-fork-sync/last-run.log}
|
||
TOKEN=${TOKEN:-}
|
||
|
||
say() { printf '\n==> %s\n' "$*"; }
|
||
ok() { printf ' ✓ %s\n' "$*"; }
|
||
die() { printf '✗ %s\n' "$*" >&2; exit 1; }
|
||
|
||
mkdir -p "$(dirname "$LOG")"
|
||
exec > >(tee -a "$LOG") 2>&1
|
||
printf '\n===== запуск %s =====\n' "$(date '+%F %T %Z')"
|
||
|
||
[[ -n $TOKEN ]] || die "нет TOKEN: workflow не передал секрет GITEA_TOKEN"
|
||
command -v git >/dev/null || die "нет git"
|
||
PY=${PY:-$(command -v python3 || command -v python || true)}
|
||
[[ -n $PY ]] || die "нет python3 — без него не применить швы"
|
||
|
||
auth_url="${GITEA_URL}/${REPO}.git"
|
||
auth_url="${auth_url/http:\/\//http://x-access-token:${TOKEN}@}"
|
||
|
||
# ---------------------------------------------------------------- рабочая копия
|
||
|
||
if [[ ! -d $WORK/.git ]]; then
|
||
say "Первая синхронизация: клонирую репозиторий"
|
||
git clone -q "$auth_url" "$WORK" || die "не смог склонировать $REPO"
|
||
git -C "$WORK" remote set-url origin "$auth_url"
|
||
git -C "$WORK" remote add upstream "$UPSTREAM" 2>/dev/null || git -C "$WORK" remote set-url upstream "$UPSTREAM"
|
||
else
|
||
git -C "$WORK" remote set-url origin "$auth_url"
|
||
git -C "$WORK" remote set-url upstream "$UPSTREAM" 2>/dev/null || git -C "$WORK" remote add upstream "$UPSTREAM"
|
||
fi
|
||
cd "$WORK"
|
||
|
||
say "Забираю ветки: наша $BRANCH и авторскую main"
|
||
git fetch -q origin --prune
|
||
git fetch -q upstream 'refs/heads/main:refs/heads/upstream' --prune
|
||
git checkout -q "$BRANCH"
|
||
git reset -q --hard "origin/$BRANCH"
|
||
|
||
author_version=$(git show upstream:VERSION 2>/dev/null | tr -d '[:space:]' || true)
|
||
[[ -n $author_version ]] || die "у автора не читается VERSION — проверьте ветку upstream"
|
||
ours=$(tr -d '[:space:]' <VERSION)
|
||
echo " у автора: $author_version · у нас: $ours"
|
||
|
||
new_commits=$(git rev-list --count "$BRANCH..upstream")
|
||
if [[ $new_commits == 0 ]]; then
|
||
say "У автора нет новых коммитов — публиковать нечего"
|
||
ok "проверка выполнена, ветка $BRANCH не изменялась"
|
||
exit 0
|
||
fi
|
||
say "У автора $new_commits новых коммитов — переношу наши правки на свежий код"
|
||
git --no-pager log --oneline "$BRANCH..upstream" | sed 's/^/ /'
|
||
|
||
# ---------------------------------------------------------------- перенос и проверки
|
||
|
||
# Скрипт форка делает всё сам: rebase на ветку автора, проверка швов, синтаксис, LF,
|
||
# тесты, подъём номера версии (--apply). На конфликте он останавливается и ничего не публикует.
|
||
if ! bash tools/fork-sync.sh --apply; then
|
||
die "перенос не прошёл (конфликт в местах наших правок). Ветка $BRANCH не изменена. Разберите вручную на рабочей станции"
|
||
fi
|
||
|
||
new_version=$(tr -d '[:space:]' <VERSION)
|
||
[[ -n $new_version ]] || die "после переноса не читается VERSION"
|
||
|
||
# Заглушка записи в нашем журнале версий: без раздела «## v<версия>» сборщик релиза откажется
|
||
# подписывать. Текст — на владельца, он правит его перед выпуском.
|
||
if ! grep -q "^## v$new_version" FORK-CHANGELOG.md; then
|
||
say "Добавляю запись в FORK-CHANGELOG для версии $new_version"
|
||
"$PY" - "$new_version" "$author_version" <<'PY'
|
||
import sys, pathlib
|
||
new, author = sys.argv[1], sys.argv[2]
|
||
p = pathlib.Path("FORK-CHANGELOG.md")
|
||
text = p.read_text(encoding="utf-8")
|
||
block = (f"## v{new}: обновление от автора {author}\n\n"
|
||
f"* Автоматическая синхронизация подтянула код автора {author} в нашу сборку: наши правки (швы)\n"
|
||
f" перенесены на свежий код, проверки прошли. Описать, что изменилось у автора, до выпуска релиза.\n\n")
|
||
marker = "## v"
|
||
i = text.find(marker)
|
||
p.write_text(text[:i] + block + text[i:], encoding="utf-8", newline="\n")
|
||
print(f" ✓ раздел «## v{new}» добавлен")
|
||
PY
|
||
fi
|
||
|
||
# Финальный контроль: швы на месте, синтаксис и LF — как того требует сборщик релиза.
|
||
"$PY" tools/fork_apply.py --check >/dev/null || die "после переноса швы не проверяются — ничего не публикую"
|
||
for f in scripts/*.sh tools/*.sh; do bash -n "$f" || die "$f: синтаксис"; done
|
||
if grep -qU $'\r' scripts/*.sh tools/*.sh VERSION 2>/dev/null; then
|
||
die "в файлах CRLF — сервер сверяет SHA256, сумма не сойдётся. Ничего не публикую"
|
||
fi
|
||
ok "швы на месте, синтаксис и переводы строк в порядке"
|
||
|
||
# ---------------------------------------------------------------- публикация
|
||
|
||
if git diff --quiet origin/"$BRANCH" -- VERSION scripts tools .gitea 2>/dev/null && [[ $new_version == "$ours" ]]; then
|
||
say "Содержимое не изменилось — публиковать нечего"
|
||
exit 0
|
||
fi
|
||
|
||
say "Публикую в $BRANCH"
|
||
git add -A
|
||
git -c user.name="fork-sync-bot" -c user.email="fork-sync@rukavishnikoff.ru" \
|
||
commit -q -m "Синхронизация с автором ${author_version}: швы и версия ${new_version}" \
|
||
-m "Автоматика CT 114. Проверки: швы на месте, bash -n, LF. Релиз и подпись — вручную с рабочей станции." \
|
||
|| die "не смог закоммитить"
|
||
curl -fsS -o /dev/null -H "Authorization: token ${TOKEN}" \
|
||
"${GITEA_URL}/api/v1/repos/${REPO}/branches/${BRANCH}" || die "Gitea недоступна перед отправкой"
|
||
# Перенос идёт rebase'ом: наша ветка переписывается на свежий код автора, поэтому отправляем
|
||
# с --force-with-lease — если за это время в ветку что-то попало с рабочей станции, отправка
|
||
# откажется, а не затрёт чужой коммит.
|
||
git push -q --force-with-lease=refs/heads/"$BRANCH" origin "$BRANCH" \
|
||
|| die "не смог отправить ветку $BRANCH (возможно, в ней есть ваши свежие коммиты — тогда синхронизируйте вручную)"
|
||
ok "ветка $BRANCH обновлена: $(git --no-pager log --oneline -1)"
|
||
|
||
cat <<EOF
|
||
|
||
Готово. Дальше вручную, с рабочей станции:
|
||
1) git fetch origin && git checkout $BRANCH && git reset --hard origin/$BRANCH
|
||
2) описать изменения в FORK-CHANGELOG.md (раздел уже добавлен)
|
||
3) bash tools/release.sh ~/.ssh/3x-ui-kit-fork/release_key — подпись и релиз
|
||
4) git tag v$new_version && git push origin $BRANCH v$new_version
|
||
EOF
|