Compare commits

..
4 Commits
14 changed files with 579 additions and 25 deletions
+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+18
View File
@@ -0,0 +1,18 @@
# Что нового в сборке форка
Здесь записи о наших версиях. Файл автора (`CHANGELOG.md`) мы не трогаем — так обновления
автора не конфликтуют с нашими записями.
## v1.2.1: сборка форка поверх 3X-UI KIT 1.2
* Источник файлов и обновлений — свой репозиторий Gitea: установка и `kit update` берут релиз
оттуда, а подпись релиза проверяется ключом форка. Авторский ночной апдейт приходит в наш
канал, поэтому наши доработки не стираются ночью.
* Дополнения форка живут отдельным файлом `/usr/local/lib/kit/kit-local.sh` и подключаются одной
строкой: `kit local help | version | channel | seams | masking`.
* Наш файл едет в том же подписанном релизе, что и код автора: он входит в `SHA256SUMS`,
проверяется по подписи и обновляется вместе с `kit`.
* Швы в коде автора описаны в `tools/fork_apply.py` (источник правды), синхронизация с автором и
проверки — `tools/fork-sync.sh`.
Основа — 3X-UI KIT 1.2 (панель 3X-UI v3.9.0, ядро Xray v26.6.27). Что нового у автора — в `CHANGELOG.md`.
+85
View File
@@ -0,0 +1,85 @@
# Форк 3X-UI KIT (hacker07)
Это личный форк [3X-UI KIT](https://github.com/itsnotkubrick/3X-UI_KIT): наш код живёт рядом с
кодом автора и не ломается, когда автор обновляет свой. Здесь описано, как это устроено и что
делать после каждого обновления автора.
## Что где лежит
| Что | Где | Правит |
|---|---|---|
| Наши доработки (команды, хуки) | `scripts/kit-local.sh` | только мы |
| Описание «швов» в коде автора | `tools/fork_apply.py` | только мы |
| Синхронизация с автором и проверки | `tools/fork-sync.sh` | только мы |
| Файлы автора (`scripts/kit.sh`, `scripts/3x-ui.sh`, …) | как есть | только автор |
Ключевая идея: **в файлах автора мы правим только «швы»** — несколько констант и одну строку
подключения. Всё, что мы дописываем по существу, лежит в нашем файле, которого автор не знает.
Швы описаны в одном месте (`tools/fork_apply.py`) и накатываются только на чистое дерево автора;
если автор изменил нужную строку — инструмент останавливается и говорит об этом, а не делает
полу-патч.
## Ветки
- `upstream` — точная копия ветки автора (`main`), только вперёд.
- `dev` — наша сборка: `upstream` + швы + наш файл. Из неё делаются релизы.
- релизы — теги `v<номера>`; номер = `<версия автора>.<наш уровень>` (например, автор 1.2 → наш
первый релиз 1.2.1, следующий наш — 1.2.2, после релиза автора 1.3 — 1.3.1).
Номер обязан строго расти: на серверах `kit update` не принимает откат назад.
## Обновление автора (по шагам)
```bash
cd H:/My_Proj/Fork_3X-UI_KIT
bash tools/fork-sync.sh # подтянуть автора, перенести наши правки, прогнать проверки
# при конфликте — разобрать его вручную, инструмент сам ничего не публикует
bash tools/release.sh ~/.ssh/3x-ui-kit-fork/release_key # SHA256SUMS + подпись нашим ключом
git add -A && git commit -m "Релиз v$(cat VERSION): швы форка + подпись"
git tag v$(cat VERSION)
git push origin dev v$(cat VERSION) upstream
```
## Установка и обновление из этого репозитория
```bash
# установка на чистый сервер (вместо команды автора с GitHub)
bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main/scripts/3x-ui.sh)
# обновление уже стоящего сервера (тянет наш релиз и проверяет нашу подпись)
kit update --force && kit check
```
Внутри `kit` источником файлов служит `KIT_RAW`/`kit_ref_raw()` — они указывают на этот
репозиторий, а подпись релиза проверяется ключом из `KIT_SIGNERS` (только ключ форка).
## Подпись релизов
- Открытая часть ключа — `tools/fork-signer.pub` и в `KIT_SIGNERS` (в `scripts/kit.sh`).
- Закрытая часть — только на рабочей станции владельца (`~/.ssh/3x-ui-kit-fork/release_key`),
с паролем; в репозиторий, CI и на серверы не попадает. Кто получит ключ — получит root на всех
серверах, которые обновляются из этого репозитория.
- Смена ключа: подпиши новый релиз старым ключом, иначе серверы его не примут.
## Наши команды
```bash
kit local help # справка по дополнениям форка
kit local version # версия, источник обновлений, дата сборки нашего файла
kit local channel # откуда берутся обновления и чем проверяется подпись
kit local seams # какие швы форка сейчас в установленном /usr/local/bin/kit
```
## Грабли, о которых стоит помнить
1. **Ночное автообновление** (`kit-update.timer`, 03:00) перезаписывает `/usr/local/bin/kit`
подписанным релизом из настроенного репозитория. Поэтому канал обновления и швы — один шаг:
сервер должен смотреть в этот репозиторий, иначе наши правки исчезнут ночью.
2. **Переводы строк.** Релиз подписывается по контрольным суммам файлов в рабочей копии, а сервер
сверяет их с файлами из Gitea. При `core.autocrlf=true` суммы не сойдутся (CRLF против LF):
в этом клоне должно быть `git config core.autocrlf false`.
3. **Наш файл обязан быть в списке релиза** (`tools/release.sh`, `FILES=(…)`) и в списке загрузки
`fetch_release()` — иначе при обновлении `kit.sh` приедет новый, а `kit-local.sh` останется старым.
4. **Панель 3X-UI ставится с GitHub** по закреплённой версии и SHA256 — это шаг автора, форк его не
трогает. Для установки с нуля серверу нужен доступ к GitHub.
+7 -6
View File
@@ -1,6 +1,7 @@
# 3X-UI KIT 1.2
2d7a0f0dcc26ea4f8bab3d60e1900e7046a9673a88fef85c03630502120961f0 scripts/3x-ui.sh
ec3d62ecee39d14ea98ccc367ebd34febbb2e57a28a4e2405f70bcc7b26e45ab scripts/hysteria2.sh
ce4889af8e9d1ce70508166f96986d33a5f4e3829b8223f8035e380f45ca7760 scripts/kit.sh
eac3e47491ce86487f05160952ff5b4ad8a3821954f082eee3516e40575fb574 scripts/kit-sub.py
44804414f85bef9588f60086587fd6e8871b39123c831ec129624f4d81a95fea VERSION
# 3X-UI KIT 1.2.1
8297a98ad8b4250d96be25e7f18d1ead2becc8a5d5d28030bad59e4d71cc27f3 *scripts/3x-ui.sh
5dfb3cda1ffe87225b85646e5540b90b09996d69b6726e92cb772b11a8bb965d *scripts/hysteria2.sh
bfe610d57488b450367f8a9a7e3d3f5e95f8a3c815ccace94392d12e642439de *scripts/kit.sh
eac3e47491ce86487f05160952ff5b4ad8a3821954f082eee3516e40575fb574 *scripts/kit-sub.py
368d37f562acfaa9835443f93391fe909bc3eb11cc18f16f71313d8cf1d067ee *scripts/kit-local.sh
6cf4e084b47f33c9b02ef79279d157833868f8f70514169a768be353ee328fea *VERSION
+4 -4
View File
@@ -1,6 +1,6 @@
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgatMYIOGGebxc7V6Sk8m5JZFUIm
IWK6kSyl9DFxsQGbMAAAARM3gtdWkta2l0LXJlbGVhc2UAAAAAAAAABnNoYTUxMgAAAFMA
AAALc3NoLWVkMjU1MTkAAABAdFL7PZOiEBqM7mNXcIUk96bj1YnqSR6BijHEd07ij1IG05
Vjb8nVmTjlvGMbhxZw0lNAbdeQLSDpBgBS2JFGBQ==
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgozRJ+f9RB6qO9S9j3BWCDtC0bV
FHakRSSqtQPljLoQUAAAARM3gtdWkta2l0LXJlbGVhc2UAAAAAAAAABnNoYTUxMgAAAFMA
AAALc3NoLWVkMjU1MTkAAABA/YJoDX/KgYYh+1ZPkWKGZJ0KAqVIw/LeACznLObQgiBNnz
j3mFH/L2Nb4ctlRlqrSlCuhYQTTyA4n4N0f4qyDA==
-----END SSH SIGNATURE-----
+1 -1
View File
@@ -1 +1 @@
1.2
1.2.1
+12 -3
View File
@@ -30,9 +30,9 @@ wait_apt_idle() {
XUI_VERSION="v3.9.0"
# SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30).
XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6"
KIT_VERSION="1.2"
KIT_VERSION="1.2.1"
# kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main.
KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$KIT_VERSION"
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION"
# Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash,
# Clash Verge, Mihomo в XKeen) не проходят REALITY – проверено 2026-09-25.
# 26.6.27 – последняя версия, с которой работают все клиенты и которую принимает 3X-UI.
@@ -1270,6 +1270,7 @@ awg_attach() { # имя subId [лимит-байт] [срок-мс] [устро
}
KIT_CLI_URL="$KIT_RAW/scripts/kit.sh"
KIT_LOCAL_URL="$KIT_RAW/scripts/kit-local.sh"
install_kit_cli() {
install -d -m 700 /etc/kit
@@ -1293,10 +1294,18 @@ install_kit_file() {
[[ -f $d/kit.sh && ${BASH_SOURCE[0]} != /dev/fd/* ]] && src=$d/kit.sh
if [[ -n $src ]]; then install -m 755 "$src" /usr/local/bin/kit
else curl -fsSL --retry 3 -o /usr/local/bin/kit "$KIT_CLI_URL" && chmod 755 /usr/local/bin/kit; fi
# Форк: ставим наши локальные дополнения (см. scripts/kit-local.sh).
install -d -m 755 /usr/local/lib/kit
if [[ -n $src && -f $d/kit-local.sh ]]; then
install -m 644 "$d/kit-local.sh" /usr/local/lib/kit/kit-local.sh
else
curl -fsSL --retry 3 -o /usr/local/lib/kit/kit-local.sh "$KIT_LOCAL_URL" \
|| warn "Локальные дополнения kit не скачались: kit local не будет работать"
fi
bash -n /usr/local/bin/kit || die "Команда kit скачалась повреждённой"
}
KIT_INSTALL_CMD="bash <(curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main/scripts/3x-ui.sh)"
KIT_INSTALL_CMD="bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main/scripts/3x-ui.sh)"
# После «x-ui → Uninstall» меню подсказывает команду официального установщика –
# меняем её на нашу. Только в echo: вызов установщика в «Update» не трогаем.
+1 -1
View File
@@ -8,7 +8,7 @@
# Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём
# останавливается и не заменяет команду hy2.
KIT_VERSION="1.2"
KIT_VERSION="1.2.1"
cat >&2 <<'MSG'
Отдельный установщик Hysteria2 больше не поддерживается.
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# 3X-UI_KIT (форк hacker07) — локальные дополнения.
#
# Этот файл наш: автор его не знает и не правит, поэтому обновления автора его не ломают.
# kit подключает его одной строкой из kit.sh (шов в tools/fork_apply.py) и передаёт сюда
# аргументы командной строки. Всё, что мы дописываем к kit, живёт ЗДЕСЬ.
#
# Команды: kit local <команда> (справка: kit local help)
#
# Правила работы с файлом: только функции и никакого кода на верхнем уровне (он выполняется
# при каждом запуске kit). Всё, что нужно от автора, читается лениво — сам kit на момент
# подключения ещё не объявил свои функции.
kit_local_help() {
cat <<'EOF'
3X-UI KIT — дополнения форка (hacker07):
kit local help эта справка
kit local version версия kit, источник обновлений и номер нашего патч-уровня
kit local channel откуда берутся обновления и чем проверяется подпись
kit local seams какие места форк правит в файлах автора (метки в /usr/local/bin/kit)
kit local masking состояние маскировки: REALITY (свой домен) и прочие входы
Файл дополнений: /usr/local/lib/kit/kit-local.sh (ставится установщиком и обновляется
вместе с релизом форка). Правки автора этот файл не перезаписывают.
EOF
}
kit_local_version() {
printf 'kit: %s (форк hacker07)\n' "${KIT_VERSION:-?}"
printf 'источник: %s\n' "${KIT_RAW:-?}"
local lib=${KIT_LOCAL_LIB:-/usr/local/lib/kit/kit-local.sh}
printf 'наш файл: %s\n' "$lib"
[[ -r $lib ]] && printf 'собран: %s\n' "$(date -r "$lib" '+%Y-%m-%d %H:%M' 2>/dev/null || echo '?')"
local latest
latest=$(cat /etc/kit/latest-version 2>/dev/null || true)
[[ -n $latest ]] && printf 'последняя: %s\n' "$latest"
return 0
}
kit_local_channel() {
printf 'файлы релиза: %s\n' "${KIT_RAW:-?}"
printf 'подпись: id=%s namespace=%s\n' "${KIT_SIG_ID:-?}" "${KIT_SIG_NS:-?}"
local n=0 k
for k in "${KIT_SIGNERS[@]:-}"; do [[ -n $k ]] && n=$((n + 1)); done
printf 'ключей в KIT_SIGNERS: %d (должен быть только ключ форка)\n' "$n"
if [[ $n -eq 0 ]]; then
printf 'ВНИМАНИЕ: список ключей пуст — обновление не пройдёт проверку подписи.\n'
fi
return 0
}
kit_local_seams() {
local bin=/usr/local/bin/kit
[[ -r $bin ]] || { printf 'нет %s\n' "$bin"; return 1; }
grep -n '3X-UI_KIT (форк hacker07)' "$bin" 2>/dev/null \
|| printf 'метки форка в %s не найдены — сборка не наша?\n' "$bin"
return 0
}
# Короткая сводка по маскировке: у REALITY должно быть «свой домен», у остальных — сосед.
kit_local_masking() {
command -v kit >/dev/null 2>&1 || { printf 'нет команды kit\n'; return 1; }
kit net site 2>/dev/null || printf 'посмотреть: kit net site (в этой версии — kit sni rotate)\n'
return 0
}
kit_local_cmd() {
case ${1:-help} in
help | -h | --help) kit_local_help ;;
version | -v) kit_local_version ;;
channel) kit_local_channel ;;
seams) kit_local_seams ;;
masking | mask) kit_local_masking ;;
*)
printf 'Неизвестная команда форка: %s\n' "$1" >&2
kit_local_help >&2
return 2
;;
esac
}
# Точка входа: вызывается из kit.sh с аргументами командной строки.
# Если команда не наша — возвращаем 0, и kit работает ровно как раньше.
kit_local_dispatch() {
case ${1:-} in
local)
shift
kit_local_cmd "$@"
exit $?
;;
*) return 0 ;;
esac
}
+17 -7
View File
@@ -12,10 +12,17 @@
set -Eeuo pipefail
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
KIT_VERSION="1.2"
KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main"
KIT_VERSION="1.2.1"
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main"
# Файлы новой версии берём из её тега, а не из меняющейся ветки main.
kit_ref_raw() { echo "https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$1"; }
kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; }
# --- 3X-UI_KIT (форк hacker07): локальные команды и хуки ---
# Подключаем scripts/kit-local.sh, если он установлен. Наш файл: правки автора его не касаются.
# Все команды форка — под «kit local …» (см. kit local help).
KIT_LOCAL_LIB="${KIT_LOCAL_LIB:-/usr/local/lib/kit/kit-local.sh}"
if [[ -r $KIT_LOCAL_LIB ]]; then . "$KIT_LOCAL_LIB"; kit_local_dispatch "$@"; fi
XUI_ENV=/etc/x-ui/install-result.env
KIT_ENV=/etc/kit/kit.env
@@ -454,8 +461,8 @@ cmd_del() {
# есть только у автора, поэтому подменить обновление, взломав один GitHub, не выйдет.
# Новый ключ приходит только в релизе, подписанном старым.
KIT_SIGNERS=(
# SHA256:VDuGgJ8dOeCNXB4nBZf3+kRlWthw3+vh8rfMxzMSRIM (itsnotkubrick, 2026-09-30)
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrTGCDhhnm8XO1ekpPJuSWRVCJiFiupEspfQxcbEBmz 3X-UI KIT releases"
# Ключ подписи форка (владелец: hacker07). Закрытая часть — только на рабочей станции.
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM0Sfn/UQeqjvUvY9wVgg7QtG1RR2pEUkqrUD5Yy6EF 3X-UI KIT fork releases (hacker07)"
)
KIT_SIG_NS="3x-ui-kit-release"
KIT_SIG_ID="releases@3x-ui-kit"
@@ -498,7 +505,7 @@ cmd_version() {
# Что нового в версии $1 – из CHANGELOG.md, без разметки.
changelog_of() {
curl -fsS -m 5 "$KIT_RAW/CHANGELOG.md" 2>/dev/null | awk -v v="## v$1" '
{ curl -fsS -m 5 "$KIT_RAW/CHANGELOG.md" 2>/dev/null; curl -fsS -m 5 "$KIT_RAW/FORK-CHANGELOG.md" 2>/dev/null; } | awk -v v="## v$1" '
index($0, v) == 1 { on = 1; t = substr($0, length(v) + 1); sub(/^[: ]+/, "", t); if (t != "") print t; next } on && /^## / { exit } on' | sed -E 's/ ?Спасибо .*$//; s/\[([^]]*)\]\([^)]*\)/\1/g; s/\*\*//g; s/`//g' | grep -v '^[[:space:]]*$' | head -40 || true
}
@@ -519,7 +526,7 @@ fetch_release() { # версия каталог
fi
# Версия записана внутри подписанного файла: старый подписанный релиз под видом нового не пройдёт.
grep -qx "# 3X-UI KIT $v" "$d/SHA256SUMS" || { warn "Подписанный релиз не той версии – ничего не ставлю."; return 1; }
for f in scripts/kit.sh scripts/kit-sub.py; do
for f in scripts/kit.sh scripts/kit-sub.py scripts/kit-local.sh; do
sum=$(awk -v f="$f" '$2 == f {print $1}' "$d/SHA256SUMS")
[[ $sum =~ ^[0-9a-f]{64}$ ]] || { warn "В подписанном списке нет $f."; return 1; }
curl -fsSL --retry 3 -o "$d/${f##*/}" "$raw/$f" || { warn "Не удалось скачать $f."; return 1; }
@@ -715,6 +722,9 @@ cmd_update() {
# Через rename: bash дочитывает текущий kit по ходу работы, его файл трогать нельзя.
install -m 755 "$tmp/kit.sh" /usr/local/bin/kit.new && mv -f /usr/local/bin/kit.new /usr/local/bin/kit
# Форк: ставим наши локальные дополнения из того же подписанного релиза.
install -d -m 755 /usr/local/lib/kit
install -m 644 "$tmp/kit-local.sh" /usr/local/lib/kit/kit-local.sh
# Новые пути подписки (Clash и JSON) подключает уже новая версия kit.
/usr/local/bin/kit __subextra >/dev/null 2>&1 || warn "Дополнительные пути подписки не подключились: kit update --force"
echo
+1
View File
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM0Sfn/UQeqjvUvY9wVgg7QtG1RR2pEUkqrUD5Yy6EF 3X-UI KIT fork releases (hacker07)
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# 3X-UI_KIT (форк hacker07): подтянуть обновление автора в ветку dev и прогнать проверки.
#
# Ничего не публикует и ничего не обновляет на серверах: после проверок печатает, что делать
# дальше (версия, подпись, тег, push). Запускать с рабочей станции, по шагам.
#
# bash tools/fork-sync.sh # подтянуть автора и проверить
# bash tools/fork-sync.sh --apply # ещё и вписать VERSION/KIT_VERSION (номер версии)
#
# Логика версии: у автора 1.3 → наш номер 1.3.1; если база та же, а мы что-то доработали —
# 1.3.2. Номер обязан строго расти: kit у серверов не принимает откат назад.
set -Eeuo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
PY=${PY:-$(command -v python3 || command -v python || true)}
[[ -n $PY ]] || { echo "! нужен python3" >&2; exit 1; }
apply=no
case ${1:-} in --apply) apply=yes ;; "") ;; *) echo "Использование: bash tools/fork-sync.sh [--apply]" >&2; exit 1 ;; esac
say() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
die() { printf '! %s\n' "$*" >&2; exit 1; }
[[ -z $(git status --porcelain) ]] || die "в рабочей копии есть незакоммиченные правки — сначала закоммить или убери их"
say "Забираю ветку автора"
git fetch --prune upstream 'refs/heads/main:refs/heads/upstream'
author_version=$(git show upstream:VERSION | tr -d '[:space:]')
echo "у автора: $author_version"
say "Переношу наши правки на свежую ветку автора"
git checkout -q dev
if ! git rebase upstream; then
echo
echo "Конфликт при переносе. Это ожидаемо, если автор переписал те же строки."
echo " 1) разбери конфликт: git status, правь файлы, git add, git rebase --continue"
echo " 2) если проще собрать швы заново: git rebase --abort, затем"
echo " git checkout -B dev upstream && $PY tools/fork_apply.py && git commit -am 'fork: швы'"
die "остановился, ничего не публикую"
fi
say "Проверяю, что наши швы ещё на месте"
"$PY" tools/fork_apply.py --check
say "Проверки кода (как на стороне сервера и в CI автора)"
for f in scripts/*.sh tools/*.sh; do bash -n "$f" || die "$f: синтаксис"; done
echo " ✓ bash -n"
"$PY" -m py_compile scripts/kit-sub.py && echo " ✓ python-скрипты собираются"
if grep -qU $'\r' scripts/*.sh tools/*.sh VERSION 2>/dev/null; then
die "в файлах есть CRLF — сервер сверяет SHA256, и его сумма не сойдётся. Проверь core.autocrlf (нужен false)"
fi
echo " ✓ переводы строк LF"
if command -v shellcheck >/dev/null; then
shellcheck -S warning -e SC1090,SC2034,SC2211 scripts/*.sh tools/release.sh tools/fork-sync.sh && echo " ✓ shellcheck"
else
echo " – shellcheck не установлен, пропускаю"
fi
if command -v node >/dev/null; then
node --test tools/test/*.test.js >/dev/null && echo " ✓ node --test"
else
echo " – node нет, пропускаю тесты генераторов"
fi
"$PY" tools/test/restore_env_test.py >/dev/null && "$PY" tools/test/restore_archive_test.py >/dev/null \
&& echo " ✓ тесты копии (--restore)"
say "Готово: наши правки лежат на свежем коде автора"
git --no-pager log --oneline upstream..dev
echo
echo "Версия автора: $author_version · наша сейчас: $(tr -d '[:space:]' <VERSION)"
if [[ $apply == yes ]]; then
base=${author_version%%.*}
cur=$(tr -d '[:space:]' <VERSION)
if [[ ${cur%%.*} == "$base" ]]; then
new="$base.$(( ${cur##*.} + 1 ))"
else
new="$author_version.1"
fi
printf '%s\n' "$new" >VERSION
"$PY" tools/fork_apply.py >/dev/null
echo "Версия сборки → $new (VERSION и KIT_VERSION в трёх скриптах)"
fi
cat <<'NEXT'
Дальше вручную, по шагам:
1) подпись и релиз: bash tools/release.sh ~/.ssh/3x-ui-kit-fork/release_key
2) коммит и тег: git add -A && git commit -m "Релиз v$(cat VERSION): швы форка + подпись"
git tag v$(cat VERSION)
3) публикация: git push origin dev v$(cat VERSION) upstream
4) на сервере: kit update --force && kit check
NEXT
+243
View File
@@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""3X-UI_KIT (форк hacker07): накатывает наши «швы» на чистый код автора.
Это источник правды для форка: единственное место, где описано, ЧТО именно мы правим в
файлах автора. Всё, что мы дописываем по существу, живёт в scripts/kit-local.sh — файле,
который автор не трогает, поэтому обновления автора его не ломают.
Правила:
* каждый якорь обязан найтись РОВНО один раз, иначе — стоп (автор изменил код, правку
надо пересмотреть руками);
* при любой ошибке ничего не пишется наполовину: сначала прогоняй --check;
* открытый ключ подписи берётся из tools/fork-signer.pub (закрытая часть — только на
рабочей станции владельца, в репозиторий не попадает).
Использование:
python3 tools/fork_apply.py --check [корень-репозитория]
python3 tools/fork_apply.py [корень-репозитория]
"""
import os
import pathlib
import re
import sys
GITEA = os.environ.get("FORK_GITEA_BASE", "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS")
SIGNER_FILE = os.environ.get("FORK_SIGNER_PUB", "tools/fork-signer.pub")
HOOK = """
# --- 3X-UI_KIT (форк hacker07): локальные команды и хуки ---
# Подключаем scripts/kit-local.sh, если он установлен. Наш файл: правки автора его не касаются.
# Все команды форка — под «kit local …» (см. kit local help).
KIT_LOCAL_LIB="${KIT_LOCAL_LIB:-/usr/local/lib/kit/kit-local.sh}"
if [[ -r $KIT_LOCAL_LIB ]]; then . "$KIT_LOCAL_LIB"; kit_local_dispatch "$@"; fi
"""
def die(msg):
print("! " + msg, file=sys.stderr)
sys.exit(1)
def ok(msg):
print("==> " + msg)
def count(text, anchor, regex):
if regex:
return len(re.findall(anchor, text, re.MULTILINE))
return text.count(anchor)
def main():
args = [a for a in sys.argv[1:]]
check = False
if "--check" in args:
check = True
args.remove("--check")
root = pathlib.Path(args[0] if args else ".").resolve()
if not (root / "VERSION").exists():
die("%s не похож на репозиторий 3X-UI KIT (нет VERSION)" % root)
os.chdir(root)
version = (root / "VERSION").read_text(encoding="utf-8").strip()
if not re.fullmatch(r"[0-9]+(\.[0-9]+)+", version):
die("VERSION=%r: нужен числовой номер вида 1.2.1" % version)
signer_path = pathlib.Path(SIGNER_FILE)
if not signer_path.exists():
die(
"нет %s — сначала создай ключ подписи:\n"
" ssh-keygen -t ed25519 -C \"3X-UI KIT fork releases\" -f ~/.ssh/3x-ui-kit-fork/release_key\n"
" mkdir -p tools && ssh-keygen -y -f ~/.ssh/3x-ui-kit-fork/release_key > tools/fork-signer.pub"
% SIGNER_FILE
)
pub = ""
for line in signer_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if line.startswith(("ssh-ed25519 ", "ssh-rsa ")):
pub = line
break
if not pub:
die("%s: не нашёл строку открытого ключа (ssh-ed25519 …)" % signer_path)
version_re = r'^KIT_VERSION="[0-9.]+"$'
edits = [
# --- scripts/kit.sh -------------------------------------------------
("scripts/kit.sh", version_re, True, 'KIT_VERSION="%s"' % version, "версия сборки форка"),
(
"scripts/kit.sh",
'KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main"',
False,
'KIT_RAW="%s/raw/branch/main"' % GITEA,
"источник VERSION и подсказки об обновлении",
),
(
"scripts/kit.sh",
'kit_ref_raw() { echo "https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$1"; }',
False,
'kit_ref_raw() { echo "%s/raw/tag/v$1"; }' % GITEA + "\n" + HOOK,
"файлы релиза из нашего тега + подключение kit-local.sh",
),
(
"scripts/kit.sh",
r' # SHA256:[^\n]*\n "ssh-ed25519 [^\n]*"\n',
True,
' # Ключ подписи форка (владелец: hacker07). Закрытая часть — только на рабочей станции.\n'
' "%s"\n' % pub,
"ключ проверки подписи релизов",
),
(
"scripts/kit.sh",
"for f in scripts/kit.sh scripts/kit-sub.py; do",
False,
"for f in scripts/kit.sh scripts/kit-sub.py scripts/kit-local.sh; do",
"наш файл в списке проверяемых и скачиваемых",
),
(
"scripts/kit.sh",
' install -m 755 "$tmp/kit.sh" /usr/local/bin/kit.new && mv -f /usr/local/bin/kit.new /usr/local/bin/kit',
False,
' install -m 755 "$tmp/kit.sh" /usr/local/bin/kit.new && mv -f /usr/local/bin/kit.new /usr/local/bin/kit\n'
" # Форк: ставим наши локальные дополнения из того же подписанного релиза.\n"
" install -d -m 755 /usr/local/lib/kit\n"
' install -m 644 "$tmp/kit-local.sh" /usr/local/lib/kit/kit-local.sh',
"установка kit-local.sh при обновлении",
),
# --- scripts/3x-ui.sh -----------------------------------------------
("scripts/3x-ui.sh", version_re, True, 'KIT_VERSION="%s"' % version, "версия сборки форка"),
(
"scripts/3x-ui.sh",
'KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$KIT_VERSION"',
False,
'KIT_RAW="%s/raw/tag/v$KIT_VERSION"' % GITEA,
"запасная загрузка kit из нашего релиза",
),
(
"scripts/3x-ui.sh",
'KIT_CLI_URL="$KIT_RAW/scripts/kit.sh"',
False,
'KIT_CLI_URL="$KIT_RAW/scripts/kit.sh"\nKIT_LOCAL_URL="$KIT_RAW/scripts/kit-local.sh"',
"адрес нашего файла для установщика",
),
(
"scripts/3x-ui.sh",
'KIT_INSTALL_CMD="bash <(curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main/scripts/3x-ui.sh)"',
False,
'KIT_INSTALL_CMD="bash <(curl -fsSL %s/raw/branch/main/scripts/3x-ui.sh)"' % GITEA,
"команда установки, которую показывает панель",
),
(
"scripts/3x-ui.sh",
' bash -n /usr/local/bin/kit || die "Команда kit скачалась повреждённой"',
False,
" # Форк: ставим наши локальные дополнения (см. scripts/kit-local.sh).\n"
" install -d -m 755 /usr/local/lib/kit\n"
' if [[ -n $src && -f $d/kit-local.sh ]]; then\n'
' install -m 644 "$d/kit-local.sh" /usr/local/lib/kit/kit-local.sh\n'
" else\n"
' curl -fsSL --retry 3 -o /usr/local/lib/kit/kit-local.sh "$KIT_LOCAL_URL" \\\n'
' || warn "Локальные дополнения kit не скачались: kit local не будет работать"\n'
" fi\n"
' bash -n /usr/local/bin/kit || die "Команда kit скачалась повреждённой"',
"установка kit-local.sh установщиком",
),
# --- scripts/hysteria2.sh -------------------------------------------
("scripts/hysteria2.sh", version_re, True, 'KIT_VERSION="%s"' % version, "версия сборки форка"),
# --- tools/release.sh -----------------------------------------------
(
"tools/release.sh",
"FILES=(scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit.sh scripts/kit-sub.py VERSION)",
False,
"FILES=(scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit.sh scripts/kit-sub.py scripts/kit-local.sh VERSION)",
"наш файл в подписанном списке релиза",
),
# --- свои записи о версии (наш файл, авторский CHANGELOG не трогаем) ----
(
"tools/release.sh",
'grep -q "^## v$v" CHANGELOG.md || die "В CHANGELOG.md нет раздела ## v$v"',
False,
'(grep -q "^## v$v" CHANGELOG.md || grep -q "^## v$v" FORK-CHANGELOG.md)'
' || die "Нет раздела ## v$v ни в CHANGELOG.md, ни в FORK-CHANGELOG.md (форк ведёт свои записи в FORK-CHANGELOG.md)"',
"раздел версии форка — в нашем файле",
),
(
"tools/release.sh",
'[[ -z $(git status --porcelain -- "${FILES[@]}" CHANGELOG.md) ]]',
False,
'[[ -z $(git status --porcelain -- "${FILES[@]}" CHANGELOG.md FORK-CHANGELOG.md) ]]',
"наш файл записей тоже должен быть закоммичен",
),
(
"scripts/kit.sh",
' curl -fsS -m 5 "$KIT_RAW/CHANGELOG.md" 2>/dev/null | awk -v v="## v$1" \'',
False,
' { curl -fsS -m 5 "$KIT_RAW/CHANGELOG.md" 2>/dev/null; curl -fsS -m 5 "$KIT_RAW/FORK-CHANGELOG.md" 2>/dev/null; } | awk -v v="## v$1" \'',
"«что нового» ищет и в наших записях",
),
]
bad = 0
for path, anchor, is_re, new, why in edits:
p = pathlib.Path(path)
if not p.exists():
print("✗ %s: файла нет" % path)
bad += 1
continue
text = p.read_text(encoding="utf-8", newline="")
if check:
# --check смотрит на готовое дерево dev: наш шов обязан быть на месте и ровно один раз
n = text.count(new)
what, shown = "наш шов", new
else:
# применение — только на чистое дерево автора: якорь обязан найтись ровно один раз
n = count(text, anchor, is_re)
what, shown = "якорь автора", anchor
short = shown if len(shown) <= 54 else shown[:51] + "…"
short = short.replace("\n", "⏎")
if n != 1:
print("✗ %-20s %-56s %s найден %d раз (%s)" % (path, short, what, n, why))
bad += 1
continue
if check:
print("✓ %-20s %-56s (%s)" % (path, short, why))
continue
if is_re:
text = re.sub(anchor, new, text, count=1, flags=re.MULTILINE)
else:
text = text.replace(anchor, new, 1)
p.write_text(text, encoding="utf-8", newline="")
print("✓ %-20s %-56s (%s)" % (path, short, why))
if bad:
if check:
die("якорей не найдено: %d — автор изменил код, пересмотри правки в tools/fork_apply.py" % bad)
die("якорей не найдено: %d — правки не применены" % bad)
if check:
ok("все якоря на месте (версия сборки %s)" % version)
else:
ok("швы накатаны, версия сборки %s" % version)
if __name__ == "__main__":
main()
+3 -3
View File
@@ -19,17 +19,17 @@ key=${1:-$HOME/.ssh/3xui_kit_release}
[[ -f $key ]] || die "Нет закрытого ключа $key. Создать: ssh-keygen -t ed25519 -C \"3X-UI KIT 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 VERSION)
FILES=(scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit.sh scripts/kit-sub.py scripts/kit-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 || die "В CHANGELOG.md нет раздела ## v$v"
(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"
[[ -z $(git status --porcelain -- "${FILES[@]}" CHANGELOG.md) ]] || die "Есть незакоммиченные изменения в файлах релиза – сначала коммит."
[[ -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)