Первая загрузка с образа: пароли учётных записей показываются сразу после создания, крупно, с просьбой сфотографировать или записать, и не исчезают до Enter (раньше при ошибке установки — через 5 секунд); установка bj — на экране в оформлении Bridge&Join (bj-firstboot-screen: шаги, полоса хода, итог с адресом и кодом), весь вывод — в /var/log/bj/firstboot-install.log, при сбое — одна автоматическая повторная попытка; bj-installer с BJ_SCREEN печатает план шагов; шрифт консоли установщика — для экранов первой загрузки
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -169,6 +169,14 @@ func (in *installer) run() error {
|
||||
todo = append(todo, s)
|
||||
}
|
||||
}
|
||||
if os.Getenv("BJ_SCREEN") != "" { // экран установки (bj-firstboot-screen): весь план шагов заранее
|
||||
var titles []string
|
||||
for _, s := range todo {
|
||||
titles = append(titles, s.title)
|
||||
}
|
||||
fmt.Fprintf(in.out.w, "@@plan %s\n", strings.Join(titles, "|"))
|
||||
in.out.flush()
|
||||
}
|
||||
for i, s := range todo {
|
||||
in.out.step(i+1, len(todo), s.title)
|
||||
detail, err := s.run()
|
||||
|
||||
+59
-18
@@ -9,16 +9,24 @@ SERVER=https://lic.zetit.ru
|
||||
CHANNEL=stable
|
||||
BUNDLE=/var/lib/bj/bundle
|
||||
[ -s /etc/bj/channel ] && CHANNEL=$(tr -d ' \n' < /etc/bj/channel)
|
||||
finish() {
|
||||
# установка прошла: отметка и выключение первой загрузки (приветственный экран — после Enter, иначе он
|
||||
# сотрёт с экрана итог и пароли)
|
||||
finish_quiet() {
|
||||
mkdir -p "$(dirname "$MARK")" && date -Iseconds > "$MARK"
|
||||
systemctl disable bj-firstboot.service >/dev/null 2>&1
|
||||
[ -x /usr/local/sbin/bj-welcome ] && /usr/local/sbin/bj-welcome
|
||||
}
|
||||
finish_screen() { [ -x /usr/local/sbin/bj-welcome ] && /usr/local/sbin/bj-welcome; }
|
||||
urls() { ip -4 -o addr show scope global 2>/dev/null | awk '{split($4,a,"/"); printf " https://%s:8443%s\n", a[1], "'"$1"'"}'; }
|
||||
# Пока идут вопросы, сообщения загрузки на консоль не выводятся (ядро — только аварийные,
|
||||
# systemd — без строк «[ OK ] Started …»), иначе они перемешиваются с текстом на экране.
|
||||
quiet_console() { dmesg -n 1 2>/dev/null; kill -s RTMIN+21 1 2>/dev/null; sleep 2; }
|
||||
quiet_console
|
||||
# шрифт консоли установщика (кириллица, рамки, ✓ ▶ █): его кладёт образ; нет — экраны рисуются простыми символами
|
||||
FONT=/usr/local/share/bj/console.psf
|
||||
if [ -f "$FONT" ] && setfont "$FONT" 2>/dev/null; then export BJ_SCREEN_FONT=1; fi
|
||||
if [ "${BJ_SCREEN_FONT:-}" = 1 ]; then BOX_T='╔══'; BOX_B='╚══'; BOX_H='═'; else BOX_T='+=='; BOX_B='+=='; BOX_H='='; fi
|
||||
LOG=/var/log/bj/firstboot-install.log
|
||||
SCREEN=/usr/local/sbin/bj-firstboot-screen
|
||||
ACC_DONE=/var/lib/bj/.bj/os-accounts-set
|
||||
ACCOUNTS=""
|
||||
genpw() { local p=""; while [ ${#p} -lt 12 ]; do p="$p$(head -c 48 /dev/urandom | tr -dc 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789')"; done; printf '%s-%s-%s' "${p:0:4}" "${p:4:4}" "${p:8:4}"; }
|
||||
@@ -42,19 +50,48 @@ setup_accounts() {
|
||||
printf ' root_bj — администратор сервера (команды через sudo)\n user_bj — обычный вход (журналы, без прав администратора)\n'
|
||||
printf ' Работа с bj — в браузере; эти учётные записи нужны для обслуживания ОС.\n\n'
|
||||
printf ' Шаг 1 из 2 — пароли. Для каждой учётной записи предложен пароль:\n'
|
||||
printf ' • Enter — оставить предложенный (он будет показан ещё раз в конце, запишите его);\n'
|
||||
printf ' • Enter — оставить предложенный (на следующем экране его нужно будет записать или сфотографировать);\n'
|
||||
printf ' • или наберите свой (не короче 10 символов, при вводе не отображается) и Enter.\n\n'
|
||||
PA=$(askpw root_bj "$(genpw)"); PU=$(askpw user_bj "$(genpw)")
|
||||
printf 'root_bj:%s\nuser_bj:%s\n' "$PA" "$PU" | chpasswd
|
||||
mkdir -p "$(dirname "$ACC_DONE")" && date -Iseconds > "$ACC_DONE"
|
||||
ACCOUNTS=$(printf ' root_bj (администратор, sudo) пароль: %s\n user_bj (обычный вход) пароль: %s\n' "$PA" "$PU")
|
||||
ACCOUNTS=$(printf ' root_bj (администратор, sudo) пароль: %s\n user_bj (обычный вход) пароль: %s\n' "$PA" "$PU")
|
||||
unset PA PU
|
||||
clear
|
||||
printf '\n Bridge&Join: учётные записи сервера\n'
|
||||
show_accounts
|
||||
confirm_accounts
|
||||
}
|
||||
# пароли учётных записей — крупно, с просьбой сфотографировать или записать: нигде не сохраняются
|
||||
show_accounts() {
|
||||
[ -n "$ACCOUNTS" ] || return
|
||||
printf '\n ┌── ЗАПИШИТЕ: учётные записи сервера ──────────────────────────┐\n%s\n' "$ACCOUNTS"
|
||||
printf ' └── пароли нигде не сохранены и больше не будут показаны ──────┘\n'
|
||||
printf ' Сменить пароль: passwd (под своей учётной записью)\n\n'
|
||||
[ -n "$ACCOUNTS" ] || return 0
|
||||
printf '\n %s СФОТОГРАФИРУЙТЕ ИЛИ ЗАПИШИТЕ ЭТИ ПАРОЛИ %s\n\n%s\n\n' "$BOX_T" "$(printf '%*s' 24 '' | tr ' ' "$BOX_H")" "$ACCOUNTS"
|
||||
printf ' %s они показаны только здесь и нигде не сохраняются %s\n' "$BOX_B" "$(printf '%*s' 14 '' | tr ' ' "$BOX_H")"
|
||||
printf ' Вход в консоль — для обслуживания сервера; работа с bj — в браузере. Сменить пароль: passwd\n\n'
|
||||
}
|
||||
# пока не нажат Enter — экран не меняется (без таймера: пароли не должны пропасть, пока их не записали)
|
||||
confirm_accounts() {
|
||||
[ -n "$ACCOUNTS" ] || return 0
|
||||
read -r -p " Записали или сфотографировали пароли? Нажмите Enter. " _ < /dev/tty || true
|
||||
}
|
||||
# установка bj: экран хода (bj-firstboot-screen), весь вывод — в журнал $LOG; не вышло — ещё одна попытка:
|
||||
# на первой загрузке что-то могло не успеть запуститься, пройденные шаги повторяются безопасно
|
||||
install_bj() {
|
||||
mkdir -p /var/log/bj
|
||||
for try in 1 2; do
|
||||
if command -v python3 >/dev/null 2>&1 && [ -x "$SCREEN" ]; then
|
||||
python3 "$SCREEN" --log "$LOG" -- /usr/local/sbin/bj-installer install "$@" && return 0
|
||||
else
|
||||
/usr/local/sbin/bj-installer install "$@" 2>&1 | tee -a "$LOG"
|
||||
[ "${PIPESTATUS[0]}" = 0 ] && return 0
|
||||
fi
|
||||
if [ "$try" = 1 ]; then
|
||||
printf '\n Не получилось с первого раза — через 10 секунд попробую ещё раз.\n'
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
printf '\n Подробный журнал установки: %s\n' "$LOG"
|
||||
return 1
|
||||
}
|
||||
setup_accounts
|
||||
# постоянный адрес: DHCP может выдать серверу другой адрес, и bj «потеряется» (только Ubuntu/netplan)
|
||||
@@ -88,26 +125,30 @@ while true; do
|
||||
CODE=$(printf '%s' "${CODE:-}" | tr -d ' ' | tr '[:lower:]' '[:upper:]')
|
||||
if [ -z "$CODE" ]; then
|
||||
if [ -f "$BUNDLE/manifest.json" ]; then
|
||||
if /usr/local/sbin/bj-installer install --bundle "$BUNDLE" --server "$SERVER" --channel "$CHANNEL"; then
|
||||
finish # адрес, код и подсказку про ключ продукта уже вывел bj-installer
|
||||
if install_bj --bundle "$BUNDLE" --server "$SERVER" --channel "$CHANNEL"; then
|
||||
finish_quiet # адрес и код первичной настройки — на экране установки выше
|
||||
show_accounts
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ || true
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true
|
||||
finish_screen
|
||||
exit 0
|
||||
fi
|
||||
show_accounts
|
||||
printf '\n Установка с образа не завершилась. Подробности выше; повторить:\n sudo bj-installer install --bundle %s\n\n' "$BUNDLE"
|
||||
sleep 5; exit 0
|
||||
printf ' Установка bj не завершилась. Она повторится при следующей загрузке, или сейчас вручную:\n sudo bj-installer install --bundle %s\n\n' "$BUNDLE"
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true
|
||||
exit 0
|
||||
fi
|
||||
show_accounts
|
||||
printf '\n Установка bj отложена. Вопрос повторится при следующей загрузке; вручную:\n sudo bj-installer install --code КЛЮЧ-ПРОДУКТА\n\n'
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ || true
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true
|
||||
exit 0
|
||||
fi
|
||||
if /usr/local/sbin/bj-installer install --code "$CODE" --server "$SERVER" --channel "$CHANNEL"; then
|
||||
finish
|
||||
if install_bj --code "$CODE" --server "$SERVER" --channel "$CHANNEL"; then
|
||||
finish_quiet
|
||||
show_accounts
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ || true
|
||||
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true
|
||||
finish_screen
|
||||
exit 0
|
||||
fi
|
||||
printf '\n Установка не завершилась. Проверьте ключ продукта и сеть и попробуйте ещё раз.\n\n'
|
||||
show_accounts
|
||||
printf ' Установка не завершилась. Проверьте ключ продукта и сеть и попробуйте ещё раз.\n\n'
|
||||
done
|
||||
|
||||
Executable
+248
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/python3
|
||||
# Экран установки bj при первой загрузке сервера (bj-firstboot) — в том же оформлении, что экран установки
|
||||
# системы (bj-install-progress): рамка Bridge&Join, шаги установщика с отметками, полоса хода. Запускает
|
||||
# установщик, весь его вывод пишет в журнал (--log) и по этому выводу рисует экран; код возврата — как у
|
||||
# установщика. Последний кадр остаётся на экране (сверху) — под ним bj-firstboot выводит учётные записи.
|
||||
#
|
||||
# bj-firstboot-screen --log /var/log/bj/firstboot-install.log -- bj-installer install --bundle …
|
||||
#
|
||||
# Установщик с BJ_SCREEN=1 печатает в начале «@@plan шаг|шаг|…», дальше — «[i/n] Шаг готово|ошибка»,
|
||||
# строки-подробности с отступом, в конце «bj установлен» с адресом и кодом или «Установка не завершена.».
|
||||
# Знаки ✓ ▶ █ ░ ═ — только со шрифтом консоли установщика (BJ_SCREEN_FONT=1), иначе простые символы.
|
||||
# Проверка без установки: bj-firstboot-screen --demo <файл вывода установщика> [--html кадр.html]
|
||||
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
FANCY = os.environ.get("BJ_SCREEN_FONT") == "1"
|
||||
G = dict(ok="✓", run="▶", wait="·", err="✗", full="█", empty="░", h="═", v="║", tl="╔", tr="╗", bl="╚", br="╝") if FANCY else \
|
||||
dict(ok="+", run=">", wait=".", err="x", full="#", empty="-", h="=", v="|", tl="+", tr="+", bl="+", br="+")
|
||||
RESET, BOLD, DIM = "\033[0m", "\033[1m", "\033[2m"
|
||||
GREEN, CYAN, RED, WHITE, GREY, YELLOW = "\033[32m", "\033[36m", "\033[31m", "\033[97m", "\033[90m", "\033[33m"
|
||||
STEP_RE = re.compile(r"\[(\d+)/(\d+)\]\s+(.*?)\s*(готово|ошибка)?\s*$")
|
||||
|
||||
|
||||
class State:
|
||||
def __init__(self):
|
||||
self.plan, self.status, self.cur = [], {}, 0 # status: номер шага → ok|err
|
||||
self.cur_started = time.time()
|
||||
self.started = time.time()
|
||||
self.caption = ""
|
||||
self.done = False
|
||||
self.error = ""
|
||||
self.urls, self.code = [], ""
|
||||
self.buf = ""
|
||||
|
||||
def feed(self, text):
|
||||
self.buf += text
|
||||
while "\n" in self.buf:
|
||||
line, self.buf = self.buf.split("\n", 1)
|
||||
self.line(line)
|
||||
m = STEP_RE.search(self.buf) # строка шага без конца — шаг идёт
|
||||
if m:
|
||||
self.start_step(int(m.group(1)), int(m.group(2)), m.group(3))
|
||||
|
||||
def start_step(self, i, n, title):
|
||||
if not self.plan:
|
||||
self.plan = [""] * n
|
||||
if i <= len(self.plan) and not self.plan[i - 1]:
|
||||
self.plan[i - 1] = title
|
||||
if i != self.cur:
|
||||
self.cur, self.cur_started, self.caption = i, time.time(), ""
|
||||
|
||||
def line(self, line):
|
||||
s = line.strip()
|
||||
if s.startswith("@@plan "):
|
||||
self.plan = s[7:].split("|")
|
||||
return
|
||||
m = STEP_RE.search(line)
|
||||
if m:
|
||||
i = int(m.group(1))
|
||||
self.start_step(i, int(m.group(2)), m.group(3))
|
||||
if m.group(4) == "готово":
|
||||
self.status[i] = "ok"
|
||||
elif m.group(4) == "ошибка":
|
||||
self.status[i] = "err"
|
||||
return
|
||||
if s in ("готово", "ошибка") and self.cur: # отметка шага пришла отдельной строкой
|
||||
self.status[self.cur] = "ok" if s == "готово" else "err"
|
||||
return
|
||||
if s.startswith("Установка не завершена."):
|
||||
self.error = s[len("Установка не завершена."):].strip()
|
||||
elif s == "bj установлен":
|
||||
self.done = True
|
||||
elif "/first-run" in s and s.startswith("http"):
|
||||
self.urls.append(s)
|
||||
elif s.startswith("Одноразовый код первичной настройки:"):
|
||||
self.code = s.split(":", 1)[1].strip()
|
||||
elif line.startswith(" ") and s and self.cur and not self.done:
|
||||
self.caption = s # подробность последнего шага
|
||||
|
||||
def percent(self):
|
||||
n = max(1, len(self.plan))
|
||||
k = sum(1 for v in self.status.values() if v == "ok")
|
||||
if self.cur and self.cur not in self.status: # текущий шаг — доля по времени, не больше 90 %
|
||||
k += min(0.9, (time.time() - self.cur_started) / 60.0)
|
||||
return min(100, int(100 * k / n))
|
||||
|
||||
|
||||
def mmss(sec):
|
||||
sec = int(max(0, sec))
|
||||
return "%d:%02d" % (sec // 60, sec % 60)
|
||||
|
||||
|
||||
def frame(st, cols, rows, final=False):
|
||||
W = 70
|
||||
inner = W - 2
|
||||
lines = []
|
||||
|
||||
def row(parts=()):
|
||||
parts, text = list(parts), ""
|
||||
for i, (t, c) in enumerate(parts):
|
||||
if len(text) + len(t) > inner:
|
||||
parts = parts[:i] + [(t[: inner - len(text)], c)]
|
||||
text += parts[-1][0]
|
||||
break
|
||||
text += t
|
||||
lines.append([(G["v"], GREY)] + parts + [(" " * (inner - len(text)), ""), (G["v"], GREY)])
|
||||
|
||||
lines.append([(G["tl"] + G["h"] * inner + G["tr"], GREY)])
|
||||
row()
|
||||
row([(" Bridge&Join", BOLD + WHITE)])
|
||||
row([(" шлюз, объединяющий системы финансовой организации", DIM)])
|
||||
row()
|
||||
row([(" Установка bj на сервер", BOLD)])
|
||||
row()
|
||||
for i, title in enumerate(st.plan, 1):
|
||||
title = title or "…"
|
||||
stv = st.status.get(i)
|
||||
if stv == "ok":
|
||||
row([(" " + G["ok"] + " ", GREEN), (title, "")])
|
||||
elif stv == "err":
|
||||
row([(" " + G["err"] + " ", RED), (title, BOLD + RED)])
|
||||
elif i == st.cur and not final:
|
||||
t = mmss(time.time() - st.cur_started)
|
||||
row([(" " + G["run"] + " ", CYAN), (title, BOLD + CYAN), (" " * max(1, inner - 9 - len(title) - len(t)), ""), (t, DIM)])
|
||||
else:
|
||||
row([(" " + G["wait"] + " ", GREY), (title, GREY)])
|
||||
row()
|
||||
if st.error or (final and not st.done):
|
||||
row([(" " + G["err"] + " Установка не завершена.", BOLD + RED)])
|
||||
for chunk in wrap(st.error or "подробности — в журнале установки", inner - 6):
|
||||
row([(" " + chunk, "")])
|
||||
elif st.done:
|
||||
row([(" " + G["ok"] + " bj установлен", BOLD + GREEN)])
|
||||
row()
|
||||
row([(" Откройте в браузере на своём компьютере:", "")])
|
||||
for u in st.urls[:3]:
|
||||
row([(" " + u, BOLD + WHITE)])
|
||||
if st.code:
|
||||
row([(" Одноразовый код первичной настройки: ", ""), (st.code, BOLD + YELLOW)])
|
||||
row()
|
||||
row([(" Браузер предупредит о сертификате — это сертификат самого", DIM)])
|
||||
row([(" сервера bj: подтвердите исключение.", DIM)])
|
||||
else:
|
||||
pct = st.percent()
|
||||
bar_w = 48
|
||||
full = int(bar_w * pct / 100)
|
||||
row([(" ", ""), (G["full"] * full, CYAN), (G["empty"] * (bar_w - full), GREY), (" %3d %%" % pct, BOLD)])
|
||||
row([(" " + st.caption, DIM)] if st.caption else [])
|
||||
row()
|
||||
row([(" Не выключайте сервер: обычно это занимает несколько минут.", "")])
|
||||
row()
|
||||
lines.append([(G["bl"] + G["h"] * inner + G["br"], GREY)])
|
||||
foot = "прошло " + mmss(time.time() - st.started)
|
||||
lines.append([(" " * max(0, (W - len(foot)) // 2) + foot, GREY)])
|
||||
left = max(0, (cols - W) // 2)
|
||||
top = 1 if final else max(0, (rows - len(lines)) // 2) # итог — сверху: ниже bj-firstboot выведет учётные записи
|
||||
return [[("", "")]] * top + [[(" " * left, "")] + ln for ln in lines]
|
||||
|
||||
|
||||
def wrap(text, width):
|
||||
out, cur = [], ""
|
||||
for w in text.split():
|
||||
if cur and len(cur) + 1 + len(w) > width:
|
||||
out.append(cur)
|
||||
cur = w
|
||||
else:
|
||||
cur = (cur + " " + w).strip()
|
||||
if cur:
|
||||
out.append(cur)
|
||||
return out[:4] or [""]
|
||||
|
||||
|
||||
def to_ansi(fr):
|
||||
return "\033[H\033[2J" + "".join("".join((c + t + RESET) if c else t for t, c in ln) + "\n" for ln in fr)
|
||||
|
||||
|
||||
def to_html(fr):
|
||||
css = {GREEN: "#4e9a06", CYAN: "#06989a", RED: "#cc0000", GREY: "#888", DIM: "#9a9a9a", WHITE: "#fff", YELLOW: "#c4a000"}
|
||||
rows = []
|
||||
for ln in fr:
|
||||
h = ""
|
||||
for t, c in ln:
|
||||
t = t.replace("&", "&").replace("<", "<")
|
||||
if not c:
|
||||
h += t
|
||||
continue
|
||||
color = next((css[k] for k in (GREEN, CYAN, RED, GREY, WHITE, YELLOW, DIM) if k in c), "#ddd")
|
||||
h += '<span style="color:%s;font-weight:%s">%s</span>' % (color, "700" if BOLD in c else "400", t)
|
||||
rows.append(h)
|
||||
return ('<!doctype html><meta charset="utf-8"><body style="margin:0;background:#000"><pre style="margin:0;padding:8px;'
|
||||
'font:16px/1 \'DejaVu Sans Mono\',monospace;color:#ddd;background:#000">' + "\n".join(rows) + "</pre>")
|
||||
|
||||
|
||||
def main():
|
||||
a = sys.argv[1:]
|
||||
if len(a) >= 2 and a[0] == "--demo":
|
||||
st = State()
|
||||
st.feed(open(a[1], encoding="utf-8").read())
|
||||
st.started -= 95
|
||||
st.cur_started -= 24
|
||||
fr = frame(st, 100, 40, final=st.done or bool(st.error))
|
||||
if len(a) >= 4 and a[2] == "--html":
|
||||
open(a[3], "w", encoding="utf-8").write(to_html(fr))
|
||||
else:
|
||||
sys.stdout.write(to_ansi(fr))
|
||||
return 0
|
||||
if len(a) < 4 or a[0] != "--log" or a[2] != "--":
|
||||
sys.stderr.write("bj-firstboot-screen --log ФАЙЛ -- команда …\n")
|
||||
return 2
|
||||
log_path, cmd = a[1], a[3:]
|
||||
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
||||
log = open(log_path, "ab", buffering=0)
|
||||
log.write(("\n==== %s: %s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), " ".join(cmd))).encode())
|
||||
env = dict(os.environ, BJ_SCREEN="1", NO_COLOR="1")
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, env=env, bufsize=0)
|
||||
st = State()
|
||||
sys.stdout.write("\033[?25l")
|
||||
try:
|
||||
while True:
|
||||
r, _, _ = select.select([p.stdout], [], [], 1.0)
|
||||
if r:
|
||||
chunk = os.read(p.stdout.fileno(), 65536)
|
||||
if not chunk:
|
||||
break
|
||||
log.write(chunk)
|
||||
st.feed(chunk.decode("utf-8", "replace"))
|
||||
cols, rows = shutil.get_terminal_size((100, 40))
|
||||
sys.stdout.write(to_ansi(frame(st, cols, rows)))
|
||||
sys.stdout.flush()
|
||||
code = p.wait()
|
||||
if code != 0 and not st.error:
|
||||
st.error = "установщик завершился с кодом %d" % code
|
||||
cols, rows = shutil.get_terminal_size((100, 40))
|
||||
sys.stdout.write(to_ansi(frame(st, cols, rows, final=True)))
|
||||
return code
|
||||
finally:
|
||||
sys.stdout.write("\033[?25h")
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -28,6 +28,7 @@ mkdir -p "$WORK/bj/bundle" "$WORK/bj/validata" "$WORK/nocloud" "$WORK/grub"
|
||||
|
||||
install -m 0755 "$INSTALLER" "$WORK/bj/bj-installer"
|
||||
install -m 0755 "$HERE/bj-firstboot" "$WORK/bj/bj-firstboot"
|
||||
install -m 0755 "$HERE/bj-firstboot-screen" "$WORK/bj/bj-firstboot-screen" # экран установки bj при первой загрузке
|
||||
install -m 0755 "$HERE/bj-install-progress" "$HERE/bj-install-progress-start" "$WORK/bj/" # экран установки на консоли
|
||||
cp "$HERE/bj-firstboot.service" "$WORK/bj/"
|
||||
install -m 0755 "$HERE/../linux/sbin/bj-welcome" "$WORK/bj/bj-welcome"
|
||||
|
||||
@@ -24,6 +24,7 @@ mkdir -p "$WORK/bj" "$WORK/preseed" "$WORK/isolinux" "$WORK/grub"
|
||||
cp "$HERE/preseed.cfg" "$WORK/preseed/bj.cfg"
|
||||
install -m 0755 "$INSTALLER" "$WORK/bj/bj-installer"
|
||||
install -m 0755 "$HERE/bj-firstboot" "$WORK/bj/bj-firstboot"
|
||||
install -m 0755 "$HERE/bj-firstboot-screen" "$WORK/bj/bj-firstboot-screen" # экран установки bj при первой загрузке
|
||||
cp "$HERE/bj-firstboot.service" "$WORK/bj/bj-firstboot.service"
|
||||
echo "$CHANNEL" > "$WORK/bj/channel"
|
||||
echo "$VERSION" > "$WORK/bj/version"
|
||||
|
||||
@@ -72,6 +72,7 @@ d-i grub-installer/bootdev string default
|
||||
d-i preseed/late_command string \
|
||||
install -m 0755 /cdrom/bj/bj-installer /target/usr/local/sbin/bj-installer; \
|
||||
install -m 0755 /cdrom/bj/bj-firstboot /target/usr/local/sbin/bj-firstboot; \
|
||||
if [ -f /cdrom/bj/bj-firstboot-screen ]; then install -m 0755 /cdrom/bj/bj-firstboot-screen /target/usr/local/sbin/bj-firstboot-screen; fi; \
|
||||
install -m 0644 /cdrom/bj/bj-firstboot.service /target/etc/systemd/system/bj-firstboot.service; \
|
||||
mkdir -p /target/etc/bj; \
|
||||
if [ -f /cdrom/bj/channel ]; then install -m 0644 /cdrom/bj/channel /target/etc/bj/channel; fi; \
|
||||
|
||||
@@ -41,7 +41,9 @@ autoinstall:
|
||||
- cp -r /cdrom/bj/validata /target/var/lib/bj/validata
|
||||
- sh -c 'if [ -d /cdrom/bj/debs ]; then cp -r /cdrom/bj/debs /target/var/lib/bj/debs; fi'
|
||||
- sh -c 'if [ -d /cdrom/bj/igate ]; then mkdir -p /target/var/lib/bj/igate-dist && cp /cdrom/bj/igate/igate-*-amd64.zip /target/var/lib/bj/igate-dist/; fi'
|
||||
- install -m 0755 /cdrom/bj/bj-installer /cdrom/bj/bj-firstboot /cdrom/bj/bj-welcome /cdrom/bj/bj-network /target/usr/local/sbin/
|
||||
- install -m 0755 /cdrom/bj/bj-installer /cdrom/bj/bj-firstboot /cdrom/bj/bj-firstboot-screen /cdrom/bj/bj-welcome /cdrom/bj/bj-network /target/usr/local/sbin/
|
||||
# шрифт консоли установщика (кириллица, рамки, ✓ ▶ █) — для экранов первой загрузки
|
||||
- sh -c 'mkdir -p /target/usr/local/share/bj && cp /snap/subiquity/current/subiquity.psf /target/usr/local/share/bj/console.psf || true'
|
||||
- install -m 0644 /cdrom/bj/bj-firstboot.service /cdrom/bj/bj-welcome.service /cdrom/bj/bj-welcome.timer /target/etc/systemd/system/
|
||||
- install -m 0644 /cdrom/bj/channel /target/etc/bj/channel
|
||||
- curtin in-target --target=/target -- systemctl enable bj-firstboot.service bj-welcome.service bj-welcome.timer
|
||||
|
||||
Reference in New Issue
Block a user