Нормализация в сборщике делалась через sed -i, а на Windows в Git Bash это ненадёжно. Замена на Python сначала не сработала по неожиданной причине: python3 здесь — заглушка Microsoft Store, которая печатает "Python" и не выполняет ничего. Нормализация тихо становилась пустой операцией, а сборщик при этом рапортовал об успехе. Теперь интерпретатор выбирается проверкой: python3, python, py — берётся первый, который действительно исполняет код. Не нашёлся ни один — сборка прерывается с объяснением, потому что установщик без нормализации ломается на Linux с "$'\r': command not found". Проверено побайтово на собранной поставке: в прологе ноль байтов 0d, маркер на своём месте, среди .sh и .py файлов поставки ни одного с возвратом каретки. Отдельно отмечу для истории: тревога о возврате CRLF была поднята по ошибке измерения — grep -c с шаблоном возврата каретки в этой оболочке давал ложные срабатывания. Сама поставка была исправна и до правки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
5.5 KiB
Bash
126 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
||
# Собирает ОДИН самодостаточный файл dist/hermes-hub-setup.sh.
|
||
#
|
||
# Зачем: install-linux.sh читает исходники из каталога репозитория, поэтому на
|
||
# каждую машину приходилось копировать весь клон git. Здесь исходники
|
||
# упакованы внутрь самого установщика — файл переносится один и работает там,
|
||
# где git не установлен вовсе.
|
||
#
|
||
# Устройство: текстовый пролог, затем строка-маркер, затем tar.gz побайтово.
|
||
# Пролог обрезает себя по маркеру и распаковывает хвост во временный каталог.
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
DIST_DIR="$REPO_ROOT/dist"
|
||
OUT="$DIST_DIR/hermes-hub-setup.sh"
|
||
|
||
COMMIT="$(cd "$REPO_ROOT" && git rev-parse --short HEAD 2>/dev/null || echo 'не определён')"
|
||
echo "Сборка установщика Linux, коммит: $COMMIT"
|
||
|
||
STAGE="$(mktemp -d)"
|
||
trap 'rm -rf "$STAGE"' EXIT
|
||
|
||
# Тот же состав, что в виндовом payload, плюс installer/ — внутри него лежит
|
||
# install-linux.sh, который и выполняет установку.
|
||
for item in src launcher assets config scripts installer; do
|
||
[ -e "$REPO_ROOT/$item" ] || continue
|
||
cp -r "$REPO_ROOT/$item" "$STAGE/$item"
|
||
done
|
||
printf '%s' "$COMMIT" > "$STAGE/BUILD_COMMIT"
|
||
|
||
# Нормализация переводов строк — обязательный шаг, а не гигиена.
|
||
#
|
||
# Сборка идёт на Windows, где рабочая копия хранится с CRLF (core.autocrlf).
|
||
# В git объекты лежат с LF, но сборщик копирует из РАБОЧЕЙ КОПИИ, поэтому
|
||
# .gitattributes её не спасает. Установщик с возвратом каретки падает на
|
||
# первой же строке, а строка-маркер перестаёт совпадать, и пролог не находит
|
||
# границу вложенных данных. Именно так сломалась сборка 44808bd.
|
||
#
|
||
# Делается на Python, а не sed: sed -i из Git Bash на Windows молча не
|
||
# убирает возврат каретки — после его работы в install-linux.sh оставалось
|
||
# 207 таких строк, и в поставку снова уходил битый установщик.
|
||
# python3 на Windows — заглушка Microsoft Store: она печатает "Python" и
|
||
# ничего не выполняет, из-за чего нормализация тихо не срабатывала.
|
||
PYBIN=""
|
||
for cand in python3 python py; do
|
||
if command -v "$cand" >/dev/null 2>&1 && "$cand" -c "import sys" >/dev/null 2>&1; then
|
||
PYBIN="$cand"; break
|
||
fi
|
||
done
|
||
if [ -z "$PYBIN" ]; then
|
||
echo "ОШИБКА: не найден рабочий Python для нормализации переводов строк." >&2
|
||
echo "Собирать установщик без неё нельзя: он сломается на Linux." >&2
|
||
exit 1
|
||
fi
|
||
echo "Нормализация переводов строк..."
|
||
"$PYBIN" - "$STAGE" <<'NORMALIZE'
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
CRLF = bytes([13, 10])
|
||
LF = bytes([10])
|
||
root = Path(sys.argv[1])
|
||
suffixes = {".sh", ".py", ".yaml", ".yml", ".json"}
|
||
fixed = 0
|
||
for path in root.rglob("*"):
|
||
if not path.is_file() or path.suffix not in suffixes:
|
||
continue
|
||
raw = path.read_bytes()
|
||
clean = raw.replace(CRLF, LF)
|
||
if clean != raw:
|
||
path.write_bytes(clean)
|
||
fixed += 1
|
||
print(" переводы строк исправлены в %d файлах" % fixed)
|
||
NORMALIZE
|
||
|
||
find "$STAGE" -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true
|
||
find "$STAGE" -name '*.pyc' -delete 2>/dev/null || true
|
||
# Виндовые бинарники в линуксовой поставке бесполезны и весят больше мегабайта.
|
||
find "$STAGE/launcher" -name '*.exe' -delete 2>/dev/null || true
|
||
|
||
PAYLOAD="$(mktemp)"
|
||
tar -czf "$PAYLOAD" -C "$STAGE" .
|
||
|
||
mkdir -p "$DIST_DIR"
|
||
cat > "$OUT" <<'STUB'
|
||
#!/usr/bin/env bash
|
||
# Hermes Hub — самодостаточный установщик. Репозиторий и git не нужны.
|
||
# bash hermes-hub-setup.sh
|
||
set -euo pipefail
|
||
|
||
if ! command -v tar >/dev/null 2>&1; then
|
||
echo "Нужен tar. Установите его: apt install tar" >&2
|
||
exit 1
|
||
fi
|
||
|
||
MARKER='__HERMES_HUB_PAYLOAD_BELOW__'
|
||
SELF="$0"
|
||
LINE="$(grep -a -n "^$MARKER\$" "$SELF" | head -1 | cut -d: -f1)"
|
||
if [ -z "${LINE:-}" ]; then
|
||
echo "Файл повреждён: не найдена граница вложенных данных." >&2
|
||
exit 1
|
||
fi
|
||
|
||
TMP="$(mktemp -d)"
|
||
trap 'rm -rf "$TMP"' EXIT
|
||
|
||
echo "Распаковка..."
|
||
tail -n +$((LINE + 1)) "$SELF" | tar -xzf - -C "$TMP"
|
||
|
||
if [ ! -f "$TMP/installer/install-linux.sh" ]; then
|
||
echo "Файл повреждён: во вложенных данных нет installer/install-linux.sh." >&2
|
||
exit 1
|
||
fi
|
||
|
||
chmod +x "$TMP/installer/install-linux.sh" 2>/dev/null || true
|
||
exec bash "$TMP/installer/install-linux.sh" "$@"
|
||
STUB
|
||
|
||
echo '__HERMES_HUB_PAYLOAD_BELOW__' >> "$OUT"
|
||
cat "$PAYLOAD" >> "$OUT"
|
||
rm -f "$PAYLOAD"
|
||
chmod +x "$OUT"
|
||
|
||
SIZE_KB=$(( $(wc -c < "$OUT") / 1024 ))
|
||
echo "Готово: $OUT (${SIZE_KB} КБ)"
|