Улучши лог установки VPN
This commit is contained in:
16
README.md
16
README.md
@@ -157,6 +157,22 @@ Detected state:
|
||||
|
||||
В неинтерактивной среде скрипт не задаёт вопросов и использует выбранные флаги/дефолты. Для CI или повторяемой установки лучше явно указывать `--non-interactive`.
|
||||
|
||||
## Логи установщика
|
||||
|
||||
Установщик печатает пошаговый лог с emoji, цветом в интерактивном терминале и коротким пояснением, зачем нужен каждый шаг. Например, перед сборкой Swift-приложения он отдельно пишет, что `swift build` может занять время и что строки компилятора вида `[2/5] Write swift-version...` являются нормальным выводом.
|
||||
|
||||
Отключить цвет:
|
||||
|
||||
```sh
|
||||
NO_COLOR=1 sh install.sh
|
||||
```
|
||||
|
||||
Отключить emoji:
|
||||
|
||||
```sh
|
||||
LEMANA_VPN_NO_EMOJI=1 sh install.sh
|
||||
```
|
||||
|
||||
## Модули
|
||||
|
||||
### Core
|
||||
|
||||
214
install.sh
214
install.sh
@@ -31,6 +31,42 @@ CONFIGURE_KEYCHAIN_FORCED=0
|
||||
APP_DIR="${LEMANA_VPN_APP_DIR:-$HOME/Applications/LemanaVPN.app}"
|
||||
LAUNCH_AGENT="$HOME/Library/LaunchAgents/ru.dokops.LemanaVPN.plist"
|
||||
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-}" != "dumb" ]; then
|
||||
C_RESET="$(printf '\033[0m')"
|
||||
C_BOLD="$(printf '\033[1m')"
|
||||
C_DIM="$(printf '\033[2m')"
|
||||
C_RED="$(printf '\033[31m')"
|
||||
C_GREEN="$(printf '\033[32m')"
|
||||
C_YELLOW="$(printf '\033[33m')"
|
||||
C_BLUE="$(printf '\033[34m')"
|
||||
C_CYAN="$(printf '\033[36m')"
|
||||
else
|
||||
C_RESET=""
|
||||
C_BOLD=""
|
||||
C_DIM=""
|
||||
C_RED=""
|
||||
C_GREEN=""
|
||||
C_YELLOW=""
|
||||
C_BLUE=""
|
||||
C_CYAN=""
|
||||
fi
|
||||
|
||||
if [ "${LEMANA_VPN_NO_EMOJI:-0}" = "1" ]; then
|
||||
E_STEP=">"
|
||||
E_INFO="i"
|
||||
E_OK="+"
|
||||
E_WARN="!"
|
||||
E_SKIP="-"
|
||||
E_ERROR="x"
|
||||
else
|
||||
E_STEP="➡️"
|
||||
E_INFO="ℹ️"
|
||||
E_OK="✅"
|
||||
E_WARN="⚠️"
|
||||
E_SKIP="⏭️"
|
||||
E_ERROR="❌"
|
||||
fi
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
@@ -155,8 +191,38 @@ log() {
|
||||
printf '%s\n' "$*"
|
||||
}
|
||||
|
||||
color_line() {
|
||||
color="$1"
|
||||
shift
|
||||
printf '%s%s%s\n' "$color" "$*" "$C_RESET"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
color_line "$C_BOLD$C_CYAN" "$E_STEP $*"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
color_line "$C_BLUE" "$E_INFO $*"
|
||||
}
|
||||
|
||||
log_detail() {
|
||||
color_line "$C_DIM" " $*"
|
||||
}
|
||||
|
||||
log_ok() {
|
||||
color_line "$C_GREEN" "$E_OK $*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
color_line "$C_YELLOW" "$E_WARN $*"
|
||||
}
|
||||
|
||||
log_skip() {
|
||||
color_line "$C_DIM" "$E_SKIP $*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
printf '%s%s ERROR: %s%s\n' "$C_RED" "$E_ERROR" "$*" "$C_RESET" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -206,7 +272,7 @@ yes_no() {
|
||||
fi
|
||||
|
||||
while :; do
|
||||
printf '%s %s ' "$prompt" "$suffix" > /dev/tty
|
||||
printf '%s%s %s%s ' "$C_BOLD" "$prompt" "$suffix" "$C_RESET" > /dev/tty
|
||||
IFS= read -r answer < /dev/tty || answer=""
|
||||
case "$answer" in
|
||||
"") [ "$default_answer" = "y" ]; return $? ;;
|
||||
@@ -234,6 +300,8 @@ zsh_aliases_installed() {
|
||||
}
|
||||
|
||||
print_detected_state() {
|
||||
log_step "Проверяю установленное окружение"
|
||||
log_detail "Так установщик понимает, какие модули уже есть, а по каким нужно спросить."
|
||||
log "Detected state:"
|
||||
log " openconnect: $(bool_word command -v openconnect)"
|
||||
log " pipx: $(bool_word command -v pipx)"
|
||||
@@ -254,11 +322,12 @@ choose_modules() {
|
||||
print_detected_state
|
||||
|
||||
if ! interactive_enabled; then
|
||||
log "Interactive prompts: off"
|
||||
log_skip "Interactive prompts: off"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Interactive prompts: on"
|
||||
log_info "Interactive prompts: on"
|
||||
log_detail "Будут вопросы только по отсутствующим опциональным модулям; флаги командной строки имеют приоритет."
|
||||
|
||||
if [ "$BITWARDEN_FORCED" -eq 0 ] && ! command -v bw >/dev/null 2>&1; then
|
||||
if yes_no "Bitwarden CLI не найден. Поставить модуль Bitwarden?" y; then
|
||||
@@ -365,53 +434,71 @@ write_file() {
|
||||
install_homebrew_packages() {
|
||||
need_cmd brew
|
||||
|
||||
log_step "Проверяю Homebrew-зависимости"
|
||||
log_detail "openconnect нужен как VPN-клиент, pipx — для изолированной установки openconnect-lite."
|
||||
|
||||
for pkg in openconnect pipx; do
|
||||
if brew list "$pkg" >/dev/null 2>&1; then
|
||||
log "Homebrew package already installed: $pkg"
|
||||
log_ok "Homebrew package already installed: $pkg"
|
||||
else
|
||||
log "Installing Homebrew package: $pkg"
|
||||
log_info "Installing Homebrew package: $pkg"
|
||||
run brew install "$pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$USE_BITWARDEN" -eq 1 ]; then
|
||||
if brew list bitwarden-cli >/dev/null 2>&1; then
|
||||
log "Homebrew package already installed: bitwarden-cli"
|
||||
log_ok "Homebrew package already installed: bitwarden-cli"
|
||||
else
|
||||
log "Installing Homebrew package: bitwarden-cli"
|
||||
log_info "Installing Homebrew package: bitwarden-cli"
|
||||
log_detail "Bitwarden CLI нужен, чтобы брать LDAP-пароль и TOTP seed из item '$BW_ITEM'."
|
||||
run brew install bitwarden-cli
|
||||
fi
|
||||
else
|
||||
log_skip "Bitwarden module disabled; пропускаю bitwarden-cli."
|
||||
fi
|
||||
}
|
||||
|
||||
install_openconnect_lite() {
|
||||
need_cmd pipx
|
||||
|
||||
log_step "Проверяю openconnect-lite"
|
||||
log_detail "openconnect-lite открывает SAML/Keycloak SSO и передаёт полученную сессию в openconnect."
|
||||
|
||||
if [ -x "$HOME/.local/bin/openconnect-lite" ] && [ "$FORCE" -eq 0 ]; then
|
||||
log "openconnect-lite already installed"
|
||||
log_ok "openconnect-lite already installed"
|
||||
else
|
||||
log "Installing openconnect-lite via pipx"
|
||||
log_info "Installing openconnect-lite via pipx"
|
||||
run pipx install openconnect-lite
|
||||
fi
|
||||
|
||||
if pipx --help 2>/dev/null | grep -q ' pin '; then
|
||||
log_detail "Закрепляю pipx package, чтобы случайное обновление не сломало SSO-патчи."
|
||||
run pipx pin openconnect-lite >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
install_cli() {
|
||||
tmp="$1"
|
||||
log_step "Обновляю CLI и uninstall helper"
|
||||
log_detail "CLI управляет подключением, статусом модулей, Bitwarden/Keychain sync и runtime-патчами."
|
||||
|
||||
run mkdir -p "$INSTALL_BIN_DIR"
|
||||
|
||||
download_file "bin/vpn-lemanapro.sh" "$tmp/vpn-lemanapro.sh"
|
||||
run install -m 755 "$tmp/vpn-lemanapro.sh" "$INSTALL_BIN_DIR/vpn-lemanapro.sh"
|
||||
log_ok "CLI installed: $INSTALL_BIN_DIR/vpn-lemanapro.sh"
|
||||
|
||||
download_file "uninstall.sh" "$tmp/uninstall.sh"
|
||||
run install -m 755 "$tmp/uninstall.sh" "$INSTALL_BIN_DIR/uninstall-lemana-vpn.sh"
|
||||
log_ok "Uninstall helper installed: $INSTALL_BIN_DIR/uninstall-lemana-vpn.sh"
|
||||
}
|
||||
|
||||
install_config() {
|
||||
tmp="$1"
|
||||
log_step "Записываю конфигурацию"
|
||||
log_detail "Здесь сохраняются выбранные модули, LDAP username и профиль openconnect-lite для Keycloak SSO."
|
||||
|
||||
run mkdir -p "$CONFIG_DIR" "$OC_CONFIG_DIR"
|
||||
|
||||
download_file "templates/openconnect-lite-config.toml" "$tmp/openconnect-lite-config.toml"
|
||||
@@ -429,20 +516,32 @@ LEMANA_VPN_USE_TOUCHID=\"$USE_TOUCHID\"
|
||||
LEMANA_VPN_DNS_CLEANUP=\"$DNS_CLEANUP\""
|
||||
write_file "$tmp/env" "$env_content"
|
||||
run install -m 600 "$tmp/env" "$CONFIG_DIR/env"
|
||||
log_ok "Config installed: $CONFIG_DIR/env"
|
||||
}
|
||||
|
||||
install_dns_cleanup() {
|
||||
tmp="$1"
|
||||
dns_cleanup_dir="$(dirname "$DNS_CLEANUP")"
|
||||
|
||||
log_step "Устанавливаю DNS cleanup wrapper"
|
||||
log_detail "Wrapper сбрасывает только похожие на корпоративные 10.x DNS после аварийного завершения VPN."
|
||||
log_detail "macOS может запросить sudo-пароль для записи в $dns_cleanup_dir."
|
||||
|
||||
download_file "libexec/lemana-vpn-dns-cleanup" "$tmp/lemana-vpn-dns-cleanup"
|
||||
run sudo install -d -m 755 -o root -g wheel "$dns_cleanup_dir"
|
||||
log "Installing DNS cleanup wrapper: $DNS_CLEANUP"
|
||||
log_info "Installing DNS cleanup wrapper: $DNS_CLEANUP"
|
||||
run sudo install -m 755 -o root -g wheel "$tmp/lemana-vpn-dns-cleanup" "$DNS_CLEANUP"
|
||||
log_ok "DNS cleanup wrapper installed"
|
||||
}
|
||||
|
||||
install_sudoers() {
|
||||
[ "$INSTALL_SUDOERS" -eq 1 ] || return 0
|
||||
if [ "$INSTALL_SUDOERS" -ne 1 ]; then
|
||||
log_skip "Sudoers module disabled; openconnect/DNS cleanup могут спрашивать пароль sudo."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_step "Настраиваю sudoers"
|
||||
log_detail "Это убирает повторный sudo-пароль при подключении, но разрешает только openconnect и DNS cleanup wrapper."
|
||||
|
||||
openconnect_bin="$(brew --prefix)/bin/openconnect"
|
||||
[ -x "$openconnect_bin" ] || openconnect_bin="$(command -v openconnect || true)"
|
||||
@@ -456,17 +555,25 @@ install_sudoers() {
|
||||
write_file "$tmp/sudoers-openconnect" "$current_user ALL=(ALL) NOPASSWD: $openconnect_bin"
|
||||
run sudo install -m 440 -o root -g wheel "$tmp/sudoers-openconnect" /etc/sudoers.d/lemana-vpn-openconnect
|
||||
run sudo visudo -c -f /etc/sudoers.d/lemana-vpn-openconnect
|
||||
log_ok "sudoers openconnect rule is valid"
|
||||
|
||||
write_file "$tmp/sudoers-dns" "$current_user ALL=(ALL) NOPASSWD: $DNS_CLEANUP"
|
||||
run sudo install -m 440 -o root -g wheel "$tmp/sudoers-dns" /etc/sudoers.d/lemana-vpn-dns
|
||||
run sudo visudo -c -f /etc/sudoers.d/lemana-vpn-dns
|
||||
log_ok "sudoers DNS cleanup rule is valid"
|
||||
}
|
||||
|
||||
install_touchid_helper() {
|
||||
[ "$USE_TOUCHID" -eq 1 ] || return 0
|
||||
if [ "$USE_TOUCHID" -ne 1 ]; then
|
||||
log_skip "Touch ID module disabled; мастер-пароль Bitwarden будет вводиться вручную при необходимости."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_step "Проверяю Touch ID helper"
|
||||
log_detail "Helper добавляет локальный Touch ID prompt перед чтением мастер-пароля Bitwarden из Keychain."
|
||||
|
||||
if [ -x "$INSTALL_BIN_DIR/keychain-fingerprint" ] && [ "$FORCE" -eq 0 ]; then
|
||||
log "Touch ID helper already installed: $INSTALL_BIN_DIR/keychain-fingerprint"
|
||||
log_ok "Touch ID helper already installed: $INSTALL_BIN_DIR/keychain-fingerprint"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -474,26 +581,34 @@ install_touchid_helper() {
|
||||
need_cmd swiftc
|
||||
|
||||
tmp="$1"
|
||||
log "Building keychain-fingerprint helper"
|
||||
log_info "Building keychain-fingerprint helper"
|
||||
run git clone --depth 1 https://github.com/dss99911/keychain-fingerprint.git "$tmp/keychain-fingerprint"
|
||||
run swiftc -o "$tmp/keychain-fingerprint-bin" "$tmp/keychain-fingerprint/main.swift" -framework LocalAuthentication -framework Security
|
||||
run install -m 700 "$tmp/keychain-fingerprint-bin" "$INSTALL_BIN_DIR/keychain-fingerprint"
|
||||
log_ok "Touch ID helper installed"
|
||||
}
|
||||
|
||||
install_menu_bar_app() {
|
||||
[ "$INSTALL_APP" -eq 1 ] || return 0
|
||||
if [ "$INSTALL_APP" -ne 1 ]; then
|
||||
log_skip "Menu Bar app disabled; CLI и алиасы останутся основным интерфейсом."
|
||||
return 0
|
||||
fi
|
||||
|
||||
need_cmd swift
|
||||
|
||||
tmp="$1"
|
||||
app_src="$tmp/app"
|
||||
log_step "Собираю Swift Menu Bar app"
|
||||
log_detail "Приложение живёт в status bar и вызывает $INSTALL_BIN_DIR/vpn-lemanapro.sh для подключения и статуса модулей."
|
||||
log_detail "Swift build может занять минуту; строки вида '[2/5] Write swift-version...' — нормальный вывод компилятора."
|
||||
|
||||
run mkdir -p "$app_src/Sources/LemanaVPN"
|
||||
|
||||
download_file "app/Package.swift" "$app_src/Package.swift"
|
||||
download_file "app/Sources/LemanaVPN/LemanaVPNApp.swift" "$app_src/Sources/LemanaVPN/LemanaVPNApp.swift"
|
||||
download_file "app/Sources/LemanaVPN/VPNManager.swift" "$app_src/Sources/LemanaVPN/VPNManager.swift"
|
||||
|
||||
log "Building LemanaVPN.app"
|
||||
log_info "Building LemanaVPN.app"
|
||||
run swift build -c release --package-path "$app_src"
|
||||
|
||||
app_bin="$app_src/.build/release/LemanaVPN"
|
||||
@@ -531,14 +646,23 @@ install_menu_bar_app() {
|
||||
</dict>
|
||||
</plist>'
|
||||
run install -m 644 "$info_plist" "$APP_DIR/Contents/Info.plist"
|
||||
log_ok "Menu Bar app installed: $APP_DIR"
|
||||
}
|
||||
|
||||
install_launch_agent() {
|
||||
[ "$INSTALL_AUTOSTART" -eq 1 ] || return 0
|
||||
[ "$INSTALL_APP" -eq 1 ] || return 0
|
||||
if [ "$INSTALL_AUTOSTART" -ne 1 ]; then
|
||||
log_skip "Autostart disabled; LemanaVPN.app можно запускать вручную."
|
||||
return 0
|
||||
fi
|
||||
if [ "$INSTALL_APP" -ne 1 ]; then
|
||||
log_skip "Autostart skipped because Menu Bar app is disabled."
|
||||
return 0
|
||||
fi
|
||||
|
||||
tmp="$1"
|
||||
plist="$tmp/ru.dokops.LemanaVPN.plist"
|
||||
log_step "Настраиваю автозапуск Menu Bar app"
|
||||
log_detail "LaunchAgent запускает LemanaVPN.app при логине, чтобы VPN был доступен из status bar."
|
||||
|
||||
run mkdir -p "$HOME/Library/LaunchAgents"
|
||||
write_file "$plist" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
@@ -564,6 +688,7 @@ install_launch_agent() {
|
||||
else
|
||||
printf '+ launchctl load %s\n' "$LAUNCH_AGENT"
|
||||
fi
|
||||
log_ok "LaunchAgent installed: $LAUNCH_AGENT"
|
||||
}
|
||||
|
||||
restart_running_menu_bar_app() {
|
||||
@@ -575,18 +700,28 @@ restart_running_menu_bar_app() {
|
||||
fi
|
||||
|
||||
if pgrep -x LemanaVPN >/dev/null 2>&1; then
|
||||
log "Restarting running LemanaVPN.app"
|
||||
log_step "Перезапускаю уже запущенное LemanaVPN.app"
|
||||
log_detail "Это нужно, чтобы status bar приложение сразу увидело свежий CLI и новый код."
|
||||
killall LemanaVPN >/dev/null 2>&1 || true
|
||||
sleep 1
|
||||
open "$APP_DIR" >/dev/null 2>&1 || true
|
||||
log_ok "LemanaVPN.app restarted"
|
||||
else
|
||||
log_skip "LemanaVPN.app сейчас не запущен; перезапуск не нужен."
|
||||
fi
|
||||
}
|
||||
|
||||
install_shell_aliases() {
|
||||
[ "$INSTALL_ALIASES" -eq 1 ] || return 0
|
||||
if [ "$INSTALL_ALIASES" -ne 1 ]; then
|
||||
log_skip "Shell aliases disabled; команды можно вызывать напрямую из $INSTALL_BIN_DIR."
|
||||
return 0
|
||||
fi
|
||||
|
||||
zshrc="$HOME/.zshrc"
|
||||
tmp="$1"
|
||||
log_step "Обновляю shell aliases"
|
||||
log_detail "Алиасы vpn, vpn-debug и vpn-fix-dns добавляются идемпотентным блоком в ~/.zshrc."
|
||||
|
||||
[ -f "$zshrc" ] || run touch "$zshrc"
|
||||
|
||||
block="$tmp/zshrc-block"
|
||||
@@ -616,19 +751,29 @@ EOF
|
||||
} > "$tmp/zshrc.new"
|
||||
|
||||
mv "$tmp/zshrc.new" "$zshrc"
|
||||
log_ok "Shell aliases updated: $zshrc"
|
||||
}
|
||||
|
||||
maybe_login_bitwarden() {
|
||||
[ "$USE_BITWARDEN" -eq 1 ] || return 0
|
||||
command -v bw >/dev/null 2>&1 || return 0
|
||||
if [ "$USE_BITWARDEN" -ne 1 ]; then
|
||||
log_skip "Bitwarden module disabled; credentials будут браться из macOS Keychain."
|
||||
return 0
|
||||
fi
|
||||
if ! command -v bw >/dev/null 2>&1; then
|
||||
log_warn "Bitwarden CLI enabled but bw not found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_step "Проверяю Bitwarden CLI session"
|
||||
log_detail "На первом запуске vpn CLI сможет понять, нужно ли делать bw login/unlock."
|
||||
|
||||
status="$(bw status 2>/dev/null || true)"
|
||||
if printf '%s\n' "$status" | grep -q '"status":"unauthenticated"'; then
|
||||
log "Bitwarden CLI is not logged in. Run later: bw login"
|
||||
log_warn "Bitwarden CLI is not logged in. Run later: bw login"
|
||||
elif printf '%s\n' "$status" | grep -q '"status":"locked"'; then
|
||||
log "Bitwarden CLI is logged in but locked. First vpn run will ask for master password."
|
||||
log_info "Bitwarden CLI is logged in but locked. First vpn run will ask for master password."
|
||||
else
|
||||
log "Bitwarden CLI is available."
|
||||
log_ok "Bitwarden CLI is available."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -640,8 +785,9 @@ main() {
|
||||
|
||||
choose_modules
|
||||
|
||||
log "Installing Lemana VPN"
|
||||
log "Modules: bitwarden=$USE_BITWARDEN touchid=$USE_TOUCHID sudoers=$INSTALL_SUDOERS shell=$INSTALL_ALIASES app=$INSTALL_APP autostart=$INSTALL_AUTOSTART"
|
||||
log_step "Начинаю установку Lemana VPN"
|
||||
log_detail "Повторный запуск безопасен: файлы обновляются идемпотентно, существующие credentials не перезаписываются."
|
||||
log_info "Modules: bitwarden=$USE_BITWARDEN touchid=$USE_TOUCHID sudoers=$INSTALL_SUDOERS shell=$INSTALL_ALIASES app=$INSTALL_APP autostart=$INSTALL_AUTOSTART"
|
||||
|
||||
install_homebrew_packages
|
||||
install_openconnect_lite
|
||||
@@ -657,16 +803,18 @@ main() {
|
||||
maybe_login_bitwarden
|
||||
|
||||
if [ "$CONFIGURE_KEYCHAIN" -eq 1 ]; then
|
||||
log_step "Записываю credentials в macOS Keychain"
|
||||
log_detail "Будут запрошены LDAP-пароль и постоянный TOTP seed, не текущий 30-секундный код."
|
||||
run "$INSTALL_BIN_DIR/vpn-lemanapro.sh" --configure-keychain
|
||||
fi
|
||||
|
||||
log ""
|
||||
log "Done."
|
||||
log "Open a new shell or run: exec zsh"
|
||||
log "Connect: vpn"
|
||||
log "Status: vpn --status"
|
||||
log_ok "Done."
|
||||
log_info "Open a new shell or run: exec zsh"
|
||||
log_info "Connect: vpn"
|
||||
log_info "Status: vpn --status"
|
||||
if [ "$INSTALL_APP" -eq 1 ]; then
|
||||
log "App: open '$APP_DIR'"
|
||||
log_info "App: open '$APP_DIR'"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,19 @@ output="$(cd "$ROOT" && sh install.sh --dry-run --non-interactive --minimal)"
|
||||
printf '%s\n' "$output" | grep -q 'Detected state:'
|
||||
printf '%s\n' "$output" | grep -q 'Interactive prompts: off'
|
||||
printf '%s\n' "$output" | grep -q 'Modules: bitwarden=0 touchid=0 sudoers=1 shell=1 app=1 autostart=1'
|
||||
printf '%s\n' "$output" | grep -q 'Проверяю Homebrew-зависимости'
|
||||
printf '%s\n' "$output" | grep -q 'Swift build может занять минуту'
|
||||
printf '%s\n' "$output" | grep -q 'sudo install -d -m 755 -o root -g wheel /usr/local/sbin'
|
||||
printf '%s\n' "$output" | grep -q 'swift build -c release --package-path'
|
||||
printf '%s\n' "$output" | grep -q 'launchctl load'
|
||||
printf '%s\n' "$output" | grep -q 'restart LemanaVPN.app if running'
|
||||
|
||||
esc="$(printf '\033')"
|
||||
if printf '%s\n' "$output" | grep -q "$esc"; then
|
||||
echo "non-tty dry-run output contains ANSI color codes" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
status_json="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)"
|
||||
printf '%s\n' "$status_json" | grep -q '"modules":'
|
||||
printf '%s\n' "$status_json" | grep -q '"app":'
|
||||
|
||||
Reference in New Issue
Block a user