Files
3X-UI_autoVPS/tools/release.sh
T

117 lines
8.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Подпись релиза 3X-UI_autoVPS. Запускает только автор, на своём компьютере, перед тегом:
#
# tools/release.sh [~/.ssh/3xui_kit_release]
#
# Проверяет, что версии сходятся и установщик 3X-UI не подменили, пишет SHA256SUMS,
# подписывает его закрытым ключом (ssh-keygen спросит пароль ключа) и сразу проверяет
# подпись открытым ключом, записанным в kit. Сервер 3X-UI_autoVPS ставит обновление, только
# если эта проверка у него тоже сошлась. Закрытый ключ никуда не отправляется.
set -euo pipefail
cd "$(dirname "$0")/.."
die() { printf '✗ %s\n' "$*" >&2; exit 1; }
ok() { printf '✓ %s\n' "$*"; }
sha() { if command -v sha256sum >/dev/null; then sha256sum "$@"; else shasum -a 256 "$@"; fi; }
key=${1:-$HOME/.ssh/3xui_kit_release}
[[ -f $key ]] || die "Нет закрытого ключа $key. Создать: ssh-keygen -t ed25519 -C \"3X-UI_autoVPS releases\" -f $key"
NS="3x-ui-kit-release"
ID="releases@3x-ui-kit"
FILES=(scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit.sh scripts/kit-sub.py scripts/kit-local.sh scripts/3x-ui-local.sh VERSION)
v=$(tr -d '[:space:]' <VERSION)
[[ $v =~ ^[0-9]+(\.[0-9]+)+$ ]] || die "VERSION: $v"
for f in scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit.sh; do
grep -qx "KIT_VERSION=\"$v\"" "$f" || die "$f: KIT_VERSION не $v"
done
(grep -q "^## v$v" CHANGELOG.md || grep -q "^## v$v" FORK-CHANGELOG.md) || die "Нет раздела ## v$v ни в CHANGELOG.md, ни в FORK-CHANGELOG.md (форк ведёт свои записи в FORK-CHANGELOG.md)"
ok "Версия $v во всех скриптах и в CHANGELOG"
# Переводы строк. Сервер получает файл из git (там LF), а суммы считаются по рабочей копии:
# если в рабочей копии окажутся CR, суммы не сойдутся и kit update откажется ставить релиз.
for f in "${FILES[@]}"; do
[[ -z $(tr -cd "\r" <"$f") ]] || die "$f: переводы строк Windows (CR). Уберите: tr -d '\r' < \"$f\" > \"$f.lf\" && mv \"$f.lf\" \"$f\""
done
ok "Переводы строк во всех файлах релиза — LF"
export PY
bash tools/check-help.sh || die "справка kit help отстала от набора команд"
# «Строки-сироты»: строка с флагом без команды (например потерянный "#" в комментарии) не ловится
# ни bash -n, ни справкой, но при каждом запуске печатает «command not found». Проверяем холостым прогоном.
err=$(bash scripts/3x-ui-local.sh --dry-run 2>&1 >/dev/null || true)
if grep -qE "command not found|syntax error|bad substitution" <<<"$err"; then
printf '%s\n' "$err" | grep -E "command not found|syntax error|bad substitution" | head -3
die "установщик ругается на строку без команды — поправьте файл (см. выше)"
fi
ok "Установщик не ругается на пустые строки и опечатки"
# Наши функции в установщике обязаны быть определены РАНЬШЕ первого вызова: `bash -n` и холостой
# прогон этого не видят, а человек видит «command not found» посреди установки (так и было 13.10).
for fn in ask_yes ask_ssh_port ssh_ports_now ask_protocols proto_parse proto_menu interactive clean_previous installer_failed; do
# || true обязателен: без него grep, не найдя совпадений, возвращает 1 и set -e молча обрывает релиз.
d=$(grep -n "^$fn()" scripts/3x-ui-local.sh | head -1 | cut -d: -f1 || true)
c=$(grep -n "^[^#]*[^a-z_]$fn[ ;\"]" scripts/3x-ui-local.sh | grep -v "^${d:-0}:" | head -1 | cut -d: -f1 || true)
[[ -n $d ]] || die "scripts/3x-ui-local.sh: функция $fn не определена, а вызывается"
[[ -z $c || $c -gt $d ]] || die "scripts/3x-ui-local.sh: $fn вызывается (строка $c) раньше, чем определена ($d)"
done
ok "Все наши функции определены раньше своих вызовов"
[[ -z $(git status --porcelain -- "${FILES[@]}" CHANGELOG.md FORK-CHANGELOG.md) ]] || die "Есть незакоммиченные изменения в файлах релиза – сначала коммит."
# Установщик 3X-UI: хеш в скрипте должен совпадать с тем, что сейчас лежит на GitHub.
xv=$(sed -n 's/^XUI_VERSION="\(.*\)"/\1/p' scripts/3x-ui.sh)
xs=$(sed -n 's/^XUI_INSTALL_SHA256="\(.*\)"/\1/p' scripts/3x-ui.sh)
got=$(curl -fsSL "https://raw.githubusercontent.com/MHSanaei/3x-ui/$xv/install.sh" | sha | awk '{print $1}')
[[ $got == "$xs" ]] || die "Установщик 3X-UI $xv изменился: $got вместо $xs. Проверьте, что поменялось, прежде чем выпускать."
ok "Установщик 3X-UI $xv совпадает с закреплённым SHA256"
# Суммы архивов ядра Xray в скрипте должны совпадать с официальными (.dgst релиза Xray-core).
xc=$(sed -n 's/^XRAY_CORE="\(.*\)"/\1/p' scripts/3x-ui.sh)
for a in 64 arm64-v8a; do
pin=$(sed -n "s/^ \[$a\]=\([0-9a-f]\{64\}\)\$/\1/p" scripts/3x-ui.sh)
off=$(curl -fsSL "https://github.com/XTLS/Xray-core/releases/download/$xc/Xray-linux-$a.zip.dgst" | awk '/^SHA2-256=/ {print $2}')
[[ -n $pin && $pin == "$off" ]] || die "Сумма ядра Xray $xc ($a) в 3x-ui.sh не совпадает с официальной: «$pin» вместо «$off»."
done
ok "Суммы архивов ядра Xray $xc совпадают с официальными"
# Проверенная панель и ядро в kit.sh (kit panel update, kit check): те же версии, что в установщике,
# а суммы архива панели и скрипта меню x-ui – официальные.
kp=$(sed -n 's/^XUI_PIN="\(.*\)"/\1/p' scripts/kit.sh)
[[ $kp == "$xv" ]] || die "XUI_PIN в kit.sh ($kp) не совпадает с XUI_VERSION в 3x-ui.sh ($xv)."
kx=$(sed -n 's/^XRAY_PIN="\(.*\)"/\1/p' scripts/kit.sh)
[[ $kx == "$xc" ]] || die "XRAY_PIN в kit.sh ($kx) не совпадает с XRAY_CORE в 3x-ui.sh ($xc)."
for a in amd64 arm64; do
pin=$(sed -n "s/^ \[$a\]=\([0-9a-f]\{64\}\)\$/\1/p" scripts/kit.sh)
off=$(curl -fsSL "https://github.com/MHSanaei/3x-ui/releases/download/$kp/x-ui-linux-$a.tar.gz.sha256" | awk 'NR == 1 {print $1}')
[[ -n $pin && $pin == "$off" ]] || die "Сумма архива панели $kp ($a) в kit.sh не совпадает с официальной: «$pin» вместо «$off»."
done
shs=$(sed -n 's/^XUI_SH_SHA256=\(.*\)/\1/p' scripts/kit.sh)
got=$(curl -fsSL "https://raw.githubusercontent.com/MHSanaei/3x-ui/$kp/x-ui.sh" | sha | awk '{print $1}')
[[ $shs == "$got" ]] || die "Сумма x-ui.sh $kp в kit.sh не совпадает с официальной: «$shs» вместо «$got»."
ok "Панель $kp и ядро $kx в kit.sh совпадают с установщиком и официальными суммами"
# В kit записан открытый ключ, которым подписываем.
signers() { awk '/^KIT_SIGNERS=\(/ {on = 1; next} on && /^\)/ {exit} on && /^ *"/ {gsub(/^ *"|"$/, ""); print}' "$1"; }
[[ -n $(signers scripts/kit.sh) ]] || die "В scripts/kit.sh пустой KIT_SIGNERS: впишите туда открытую часть ключа ($key.pub)."
pub=$(ssh-keygen -y -f "$key" | awk '{print $1, $2}')
signers scripts/kit.sh | awk '{print $1, $2}' | grep -qxF "$pub" || die "Ключа $key нет в KIT_SIGNERS – серверы не примут такую подпись."
ok "Ключ подписи записан в kit"
{ echo "# 3X-UI KIT $v"; echo "# 3X-UI_autoVPS $v"; sha "${FILES[@]}" | awk '{ sub(/^\*/, "", $2); print $1" "$2 }'; } >SHA256SUMS
rm -f SHA256SUMS.sig
ssh-keygen -Y sign -f "$key" -n "$NS" SHA256SUMS >/dev/null
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
signers scripts/kit.sh | while read -r k; do printf '%s namespaces="%s" %s\n' "$ID" "$NS" "$k"; done >"$tmp"
ssh-keygen -Y verify -f "$tmp" -I "$ID" -n "$NS" -s SHA256SUMS.sig <SHA256SUMS >/dev/null || die "Подпись не прошла проверку."
ok "SHA256SUMS подписан и проверен так же, как это сделает сервер"
echo
cat SHA256SUMS
echo
echo "Дальше:"
echo " git add SHA256SUMS SHA256SUMS.sig && git commit -m \"Релиз v$v: подпись\""
echo " git tag v$v && git push origin main v$v"