Улучши удаление и строку статуса VPN
This commit is contained in:
16
README.md
16
README.md
@@ -97,10 +97,16 @@ curl -fsSL https://example.org/dokril/lemana-vpn/raw/branch/main/install.sh \
|
||||
|
||||
```sh
|
||||
vpn --status
|
||||
Modules: core=ok, bitwarden=on, touchid=on, dns=on, app=on, autostart=on, patches=active, keychain=password:yes/totp_seed:yes
|
||||
Modules: ✅ core=ok, ✅ bitwarden=on, ✅ touchid=on, ✅ dns=on, ✅ app=on, ✅ autostart=on, ✅ patches=active, ✅ keychain=password:yes/totp_seed:yes
|
||||
VPN disconnected
|
||||
```
|
||||
|
||||
Emoji в human-выводе помогают быстро отличать норму, отключённый опциональный модуль и проблему:
|
||||
|
||||
- `✅` — модуль установлен или состояние готово;
|
||||
- `⏭️` — модуль осознанно отключён;
|
||||
- `⚠️` — модуль включён, но чего-то не хватает.
|
||||
|
||||
Значения:
|
||||
|
||||
| Поле | Значение |
|
||||
@@ -157,20 +163,22 @@ Detected state:
|
||||
|
||||
В неинтерактивной среде скрипт не задаёт вопросов и использует выбранные флаги/дефолты. Для CI или повторяемой установки лучше явно указывать `--non-interactive`.
|
||||
|
||||
## Логи установщика
|
||||
## Логи установщика и удаления
|
||||
|
||||
Установщик печатает пошаговый лог с emoji, цветом в интерактивном терминале и коротким пояснением, зачем нужен каждый шаг. Например, перед сборкой Swift-приложения он отдельно пишет, что `swift build` может занять время и что строки компилятора вида `[2/5] Write swift-version...` являются нормальным выводом.
|
||||
Установщик и uninstall script печатают пошаговый лог с emoji, цветом в интерактивном терминале и коротким пояснением, зачем нужен каждый шаг. Например, перед сборкой Swift-приложения установщик отдельно пишет, что `swift build` может занять время и что строки компилятора вида `[2/5] Write swift-version...` являются нормальным выводом. При удалении отдельно показывается откат runtime-патчей `openconnect-lite`, удаление sudoers/DNS wrapper, приложения, aliases и config.
|
||||
|
||||
Отключить цвет:
|
||||
|
||||
```sh
|
||||
NO_COLOR=1 sh install.sh
|
||||
NO_COLOR=1 sh uninstall.sh
|
||||
```
|
||||
|
||||
Отключить emoji:
|
||||
|
||||
```sh
|
||||
LEMANA_VPN_NO_EMOJI=1 sh install.sh
|
||||
LEMANA_VPN_NO_EMOJI=1 sh uninstall.sh
|
||||
```
|
||||
|
||||
## Модули
|
||||
@@ -254,6 +262,8 @@ sh install.sh --without-touchid
|
||||
|
||||
Приложение живёт в macOS status bar, запускает `~/bin/vpn-lemanapro.sh --json`, показывает состояние VPN, IP, оставшееся время сессии, health-check тоннеля и строку состояния модулей.
|
||||
|
||||
Строка состояния модулей в меню приложения использует те же маркеры, что CLI: `✅` для готового модуля, `⏭️` для отключённого опционального модуля и `⚠️` для проблемы. Иконка строки тоже меняется: `checkmark.circle` для полностью готового набора и `exclamationmark.triangle` для неполной установки.
|
||||
|
||||
Если в меню видно `modules unavailable: update CLI`, значит запущенное приложение обращается к старому `~/bin/vpn-lemanapro.sh`, который ещё не умеет отдавать модульный статус. Повтори установку через `curl`; установщик обновит CLI и перезапустит уже запущенное `LemanaVPN.app`.
|
||||
|
||||
Для сборки нужен Swift 5.9+ из Xcode Command Line Tools:
|
||||
|
||||
@@ -30,6 +30,12 @@ struct StatusBarLabel: View {
|
||||
private var iconName: String {
|
||||
switch vpnManager.state {
|
||||
case .disconnected:
|
||||
if vpnManager.moduleStatusSystemImage == "exclamationmark.triangle" {
|
||||
return "exclamationmark.shield"
|
||||
}
|
||||
if vpnManager.moduleStatusSystemImage == "questionmark.circle" {
|
||||
return "questionmark.circle"
|
||||
}
|
||||
return "shield.slash"
|
||||
case .unlocking, .connecting:
|
||||
return "shield.lefthalf.filled"
|
||||
@@ -63,7 +69,7 @@ struct VPNMenuView: View {
|
||||
}
|
||||
|
||||
Divider()
|
||||
Label(vpnManager.moduleSummary, systemImage: "puzzlepiece.extension")
|
||||
Label(vpnManager.moduleSummary, systemImage: vpnManager.moduleStatusSystemImage)
|
||||
.disabled(true)
|
||||
Button("Обновить статус модулей") {
|
||||
vpnManager.refreshStatus()
|
||||
@@ -174,4 +180,3 @@ struct VPNMenuView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,15 +58,35 @@ struct ModuleStatus: Decodable {
|
||||
var patches: Patches
|
||||
var app: AppModule?
|
||||
|
||||
var hasWarnings: Bool {
|
||||
let coreReady = core.openconnect && core.openconnect_lite && core.openconnect_lite_config
|
||||
let bitwardenReady = !bitwarden.enabled || bitwarden.installed
|
||||
let touchReady = !touchid.enabled || touchid.installed
|
||||
let keychainReady = keychain.password && keychain.totp_seed
|
||||
let appReady = app?.installed ?? true
|
||||
|
||||
return !coreReady
|
||||
|| !bitwardenReady
|
||||
|| !touchReady
|
||||
|| !keychainReady
|
||||
|| !dns_cleanup.installed
|
||||
|| !patches.active
|
||||
|| !appReady
|
||||
}
|
||||
|
||||
var systemImage: String {
|
||||
hasWarnings ? "exclamationmark.triangle" : "checkmark.circle"
|
||||
}
|
||||
|
||||
var summary: String {
|
||||
let coreState = core.openconnect && core.openconnect_lite && core.openconnect_lite_config ? "core ok" : "core missing"
|
||||
let bwState = bitwarden.enabled ? (bitwarden.installed ? "bw on" : "bw missing") : "bw off"
|
||||
let touchState = touchid.enabled ? (touchid.installed ? "touch on" : "touch missing") : "touch off"
|
||||
let dnsState = dns_cleanup.installed ? "dns on" : "dns missing"
|
||||
let appState = app.map { $0.installed ? "app on" : "app missing" } ?? "app unknown"
|
||||
let autostartState = app.map { $0.autostart ? "autostart on" : "autostart off" } ?? "autostart unknown"
|
||||
let patchState = patches.active ? "patches active" : "patches pending"
|
||||
let keychainState = "kc \(keychain.password ? "pass" : "-")/\(keychain.totp_seed ? "totp" : "-")"
|
||||
let coreState = core.openconnect && core.openconnect_lite && core.openconnect_lite_config ? "✅ core" : "⚠️ core"
|
||||
let bwState = bitwarden.enabled ? (bitwarden.installed ? "✅ bw" : "⚠️ bw") : "⏭️ bw"
|
||||
let touchState = touchid.enabled ? (touchid.installed ? "✅ touch" : "⚠️ touch") : "⏭️ touch"
|
||||
let dnsState = dns_cleanup.installed ? "✅ dns" : "⚠️ dns"
|
||||
let appState = app.map { $0.installed ? "✅ app" : "⚠️ app" } ?? "❔ app"
|
||||
let autostartState = app.map { $0.autostart ? "✅ autostart" : "⏭️ autostart" } ?? "❔ autostart"
|
||||
let patchState = patches.active ? "✅ patches" : "⚠️ patches"
|
||||
let keychainState = "\(keychain.password && keychain.totp_seed ? "✅" : "⚠️") kc \(keychain.password ? "pass" : "-")/\(keychain.totp_seed ? "totp" : "-")"
|
||||
return [coreState, bwState, touchState, dnsState, appState, autostartState, patchState, keychainState].joined(separator: " | ")
|
||||
}
|
||||
}
|
||||
@@ -97,6 +117,7 @@ class VPNManager: ObservableObject {
|
||||
@Published var lastError: String?
|
||||
@Published var tunnelHealthy: Bool = true
|
||||
@Published var moduleSummary: String = "modules loading..."
|
||||
@Published var moduleStatusSystemImage: String = "hourglass"
|
||||
|
||||
private var process: Process?
|
||||
private var outputPipe: Pipe?
|
||||
@@ -166,6 +187,7 @@ class VPNManager: ObservableObject {
|
||||
Task { @MainActor in
|
||||
guard !lastLine.isEmpty, let jsonData = lastLine.data(using: .utf8) else {
|
||||
self.moduleSummary = "modules unavailable"
|
||||
self.moduleStatusSystemImage = "questionmark.circle"
|
||||
self.log("[modules] status refresh returned no JSON output")
|
||||
return
|
||||
}
|
||||
@@ -175,6 +197,7 @@ class VPNManager: ObservableObject {
|
||||
response = try JSONDecoder().decode(VPNStatusResponse.self, from: jsonData)
|
||||
} catch {
|
||||
self.moduleSummary = "modules unavailable"
|
||||
self.moduleStatusSystemImage = "exclamationmark.triangle"
|
||||
let compact = text.replacingOccurrences(of: "\n", with: "\\n")
|
||||
let preview = compact.count > 500 ? String(compact.prefix(500)) + "..." : compact
|
||||
self.log("[modules] status decode failed: \(error.localizedDescription); output=\(preview)")
|
||||
@@ -183,11 +206,13 @@ class VPNManager: ObservableObject {
|
||||
|
||||
guard let modules = response.modules else {
|
||||
self.moduleSummary = "modules unavailable: update CLI"
|
||||
self.moduleStatusSystemImage = "exclamationmark.triangle"
|
||||
self.log("[modules] status has no modules field; reinstall CLI with install.sh")
|
||||
return
|
||||
}
|
||||
|
||||
self.moduleSummary = modules.summary
|
||||
self.moduleStatusSystemImage = modules.systemImage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +332,7 @@ class VPNManager: ObservableObject {
|
||||
|
||||
if let modules = event.modules {
|
||||
moduleSummary = modules.summary
|
||||
moduleStatusSystemImage = modules.systemImage
|
||||
}
|
||||
|
||||
log("[event] \(event.event)" + {
|
||||
|
||||
@@ -146,11 +146,11 @@ _module_status_json() {
|
||||
_module_human_part() {
|
||||
local name="$1" enabled="$2" installed="$3"
|
||||
if [[ "$enabled" == "0" ]]; then
|
||||
printf '%s=off' "$name"
|
||||
printf '⏭️ %s=off' "$name"
|
||||
elif [[ "$installed" == "true" ]]; then
|
||||
printf '%s=on' "$name"
|
||||
printf '✅ %s=on' "$name"
|
||||
else
|
||||
printf '%s=missing' "$name"
|
||||
printf '⚠️ %s=missing' "$name"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -172,15 +172,20 @@ _module_status_human() {
|
||||
app_installed="$(_module_bool test -x "$APP_DIR/Contents/MacOS/LemanaVPN")"
|
||||
app_autostart="$(_module_bool test -f "$LAUNCH_AGENT")"
|
||||
|
||||
printf 'Modules: %s, ' "$core"
|
||||
printf 'Modules: %s %s, ' "$([[ "$core" == "core=ok" ]] && printf '✅' || printf '⚠️')" "$core"
|
||||
_module_human_part "bitwarden" "$USE_BITWARDEN" "$bitwarden_installed"
|
||||
printf ', '
|
||||
_module_human_part "touchid" "$USE_TOUCHID" "$touchid_installed"
|
||||
printf ', dns=%s, app=%s, autostart=%s, patches=%s, keychain=password:%s/totp_seed:%s\n' \
|
||||
printf ', %s dns=%s, %s app=%s, %s autostart=%s, %s patches=%s, %s keychain=password:%s/totp_seed:%s\n' \
|
||||
"$([[ "$dns_cleanup_installed" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$dns_cleanup_installed" == "true" ]] && printf on || printf missing)" \
|
||||
"$([[ "$app_installed" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$app_installed" == "true" ]] && printf on || printf missing)" \
|
||||
"$([[ "$app_autostart" == "true" ]] && printf '✅' || printf '⏭️')" \
|
||||
"$([[ "$app_autostart" == "true" ]] && printf on || printf off)" \
|
||||
"$([[ "$patches_active" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$patches_active" == "true" ]] && printf active || printf pending)" \
|
||||
"$([[ "$keychain_password" == "true" && "$keychain_totp_seed" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$keychain_password" == "true" ]] && printf yes || printf no)" \
|
||||
"$([[ "$keychain_totp_seed" == "true" ]] && printf yes || printf no)"
|
||||
}
|
||||
|
||||
@@ -34,9 +34,31 @@ printf '%s\n' "$status_json" | grep -q '"modules":'
|
||||
printf '%s\n' "$status_json" | grep -q '"app":'
|
||||
|
||||
status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)"
|
||||
printf '%s\n' "$status_text" | grep -q 'Modules:'
|
||||
printf '%s\n' "$status_text" | grep -q 'core='
|
||||
printf '%s\n' "$status_text" | grep -q 'app='
|
||||
printf '%s\n' "$status_text" | grep -q 'autostart='
|
||||
|
||||
uninstall_home="$TMP_DIR/uninstall-home"
|
||||
mkdir -p "$uninstall_home"
|
||||
uninstall_output="$(
|
||||
HOME="$uninstall_home" \
|
||||
LEMANA_VPN_BIN_DIR="$uninstall_home/bin" \
|
||||
LEMANA_VPN_CONFIG_DIR="$uninstall_home/.config/lemana-vpn" \
|
||||
OPENCONNECT_LITE_CONFIG_DIR="$uninstall_home/.config/openconnect-lite" \
|
||||
sh "$ROOT/uninstall.sh" --dry-run --remove-keychain --remove-touchid-helper --remove-openconnect-lite
|
||||
)"
|
||||
|
||||
printf '%s\n' "$uninstall_output" | grep -q 'Начинаю удаление Lemana VPN'
|
||||
printf '%s\n' "$uninstall_output" | grep -q 'Проверяю runtime-патчи openconnect-lite'
|
||||
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю sudoers и DNS cleanup wrapper'
|
||||
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю VPN-записи из macOS Keychain'
|
||||
|
||||
if printf '%s\n' "$uninstall_output" | grep -q "$esc"; then
|
||||
echo "non-tty uninstall dry-run output contains ANSI color codes" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fake_pwd="$TMP_DIR/fake-pwd"
|
||||
mkdir -p "$fake_pwd/bin"
|
||||
printf 'stale local cli\n' > "$fake_pwd/bin/vpn-lemanapro.sh"
|
||||
|
||||
160
uninstall.sh
160
uninstall.sh
@@ -16,6 +16,42 @@ REMOVE_KEYCHAIN=0
|
||||
REMOVE_TOUCHID_HELPER=0
|
||||
REMOVE_OPENCONNECT_LITE=0
|
||||
|
||||
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:
|
||||
@@ -60,6 +96,41 @@ 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 '%s%s ERROR: %s%s\n' "$C_RED" "$E_ERROR" "$*" "$C_RESET" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run() {
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '+'
|
||||
@@ -84,23 +155,30 @@ restore_openconnect_lite_patch() {
|
||||
backup="$CONFIG_DIR/patch-backups/webengine_process.py.before-lemana-vpn"
|
||||
wep="$(find_webengine_process)"
|
||||
|
||||
log_step "Проверяю runtime-патчи openconnect-lite"
|
||||
log_detail "Если Lemana VPN менял webengine_process.py, перед удалением возвращаем исходник из backup."
|
||||
|
||||
if [ ! -f "$backup" ]; then
|
||||
log "No openconnect-lite patch backup found; patch rollback skipped."
|
||||
log_skip "No openconnect-lite patch backup found; patch rollback skipped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -z "$wep" ] || [ ! -f "$wep" ]; then
|
||||
log "openconnect-lite source not found; patch rollback skipped."
|
||||
log_warn "openconnect-lite source not found; patch rollback skipped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Restoring openconnect-lite source from patch backup"
|
||||
log_info "Restoring openconnect-lite source from patch backup"
|
||||
run cp "$backup" "$wep"
|
||||
log_ok "openconnect-lite patch rollback completed"
|
||||
}
|
||||
|
||||
remove_zshrc_block() {
|
||||
zshrc="$HOME/.zshrc"
|
||||
[ -f "$zshrc" ] || return 0
|
||||
if [ ! -f "$zshrc" ]; then
|
||||
log_skip "~/.zshrc not found; shell aliases skipped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
tmp="$(mktemp)"
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
@@ -115,39 +193,61 @@ remove_zshrc_block() {
|
||||
skip != 1 { print }
|
||||
' "$zshrc" > "$tmp"
|
||||
mv "$tmp" "$zshrc"
|
||||
log_ok "Shell aliases removed from $zshrc"
|
||||
}
|
||||
|
||||
remove_keychain_entries() {
|
||||
[ "$REMOVE_KEYCHAIN" -eq 1 ] || return 0
|
||||
if [ "$REMOVE_KEYCHAIN" -ne 1 ]; then
|
||||
log_skip "Keychain cleanup disabled; LDAP/TOTP and Bitwarden cached entries are kept."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Removing VPN-related Keychain entries"
|
||||
run security delete-generic-password -s openconnect-lite -a "$USERNAME" >/dev/null 2>&1 || true
|
||||
run security delete-generic-password -s openconnect-lite -a "totp/$USERNAME" >/dev/null 2>&1 || true
|
||||
run security delete-generic-password -s vpn-lemanapro -a bw-session >/dev/null 2>&1 || true
|
||||
run security delete-generic-password -s vpn-lemanapro -a bw-master >/dev/null 2>&1 || true
|
||||
log_step "Удаляю VPN-записи из macOS Keychain"
|
||||
log_detail "Удаляются только записи openconnect-lite для пользователя $USERNAME и кэш Bitwarden Lemana VPN."
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
run security delete-generic-password -s openconnect-lite -a "$USERNAME"
|
||||
run security delete-generic-password -s openconnect-lite -a "totp/$USERNAME"
|
||||
run security delete-generic-password -s vpn-lemanapro -a bw-session
|
||||
run security delete-generic-password -s vpn-lemanapro -a bw-master
|
||||
else
|
||||
security delete-generic-password -s openconnect-lite -a "$USERNAME" >/dev/null 2>&1 || true
|
||||
security delete-generic-password -s openconnect-lite -a "totp/$USERNAME" >/dev/null 2>&1 || true
|
||||
security delete-generic-password -s vpn-lemanapro -a bw-session >/dev/null 2>&1 || true
|
||||
security delete-generic-password -s vpn-lemanapro -a bw-master >/dev/null 2>&1 || true
|
||||
fi
|
||||
log_ok "Keychain cleanup completed"
|
||||
}
|
||||
|
||||
main() {
|
||||
[ "$(uname -s)" = "Darwin" ] || {
|
||||
echo "This uninstaller supports macOS only" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$(uname -s)" = "Darwin" ] || die "This uninstaller supports macOS only"
|
||||
|
||||
log_step "Начинаю удаление Lemana VPN"
|
||||
log_detail "По умолчанию удаляются скрипты, sudoers, DNS wrapper, config, aliases и приложение."
|
||||
log_detail "Shared-зависимости Homebrew не удаляются; openconnect-lite удаляется только с --remove-openconnect-lite."
|
||||
|
||||
restore_openconnect_lite_patch
|
||||
|
||||
log "Removing installed scripts"
|
||||
log_step "Удаляю CLI-скрипты"
|
||||
log_detail "Убираю vpn-lemanapro.sh и локальный uninstall helper из $INSTALL_BIN_DIR."
|
||||
run rm -f "$INSTALL_BIN_DIR/vpn-lemanapro.sh"
|
||||
run rm -f "$INSTALL_BIN_DIR/uninstall-lemana-vpn.sh"
|
||||
log_ok "CLI scripts removed"
|
||||
if [ "$REMOVE_TOUCHID_HELPER" -eq 1 ]; then
|
||||
log_info "Removing Touch ID helper: $INSTALL_BIN_DIR/keychain-fingerprint"
|
||||
run rm -f "$INSTALL_BIN_DIR/keychain-fingerprint"
|
||||
else
|
||||
log_skip "Touch ID helper kept; use --remove-touchid-helper to remove it."
|
||||
fi
|
||||
|
||||
log "Removing sudoers and DNS cleanup wrapper"
|
||||
log_step "Удаляю sudoers и DNS cleanup wrapper"
|
||||
log_detail "macOS может запросить sudo-пароль, потому что эти файлы принадлежат root."
|
||||
run sudo rm -f /etc/sudoers.d/lemana-vpn-openconnect /etc/sudoers.d/lemana-vpn-dns
|
||||
run sudo rm -f "$DNS_CLEANUP"
|
||||
log_ok "sudoers and DNS cleanup wrapper removed"
|
||||
|
||||
if [ "$KEEP_APP" -eq 0 ]; then
|
||||
log "Removing Menu Bar app"
|
||||
log_step "Удаляю Menu Bar app"
|
||||
log_detail "Сначала отключаю LaunchAgent, затем удаляю $APP_DIR."
|
||||
if [ "$DRY_RUN" -eq 0 ]; then
|
||||
launchctl unload "$LAUNCH_AGENT" >/dev/null 2>&1 || true
|
||||
else
|
||||
@@ -155,29 +255,45 @@ main() {
|
||||
fi
|
||||
run rm -f "$LAUNCH_AGENT"
|
||||
run rm -rf "$APP_DIR"
|
||||
log_ok "Menu Bar app removed"
|
||||
else
|
||||
log_skip "Menu Bar app kept because --keep-app is set."
|
||||
fi
|
||||
|
||||
log "Removing shell aliases"
|
||||
log_step "Удаляю shell aliases"
|
||||
log_detail "Из ~/.zshrc удаляется только блок между # >>> lemana-vpn и # <<< lemana-vpn."
|
||||
remove_zshrc_block
|
||||
|
||||
log "Removing openconnect-lite config"
|
||||
log_step "Удаляю openconnect-lite config"
|
||||
log_detail "Удаляется профиль SSO, который был создан установщиком Lemana VPN."
|
||||
run rm -f "$OC_CONFIG_DIR/config.toml"
|
||||
log_ok "openconnect-lite config removed"
|
||||
|
||||
if [ "$KEEP_CONFIG" -eq 0 ]; then
|
||||
log "Removing Lemana VPN config"
|
||||
log_step "Удаляю Lemana VPN config"
|
||||
log_detail "Удаляется $CONFIG_DIR, включая backup runtime-патчей после их отката."
|
||||
run rm -rf "$CONFIG_DIR"
|
||||
log_ok "Lemana VPN config removed"
|
||||
else
|
||||
log_skip "Lemana VPN config kept because --keep-config is set."
|
||||
fi
|
||||
|
||||
remove_keychain_entries
|
||||
|
||||
if [ "$REMOVE_OPENCONNECT_LITE" -eq 1 ]; then
|
||||
if command -v pipx >/dev/null 2>&1; then
|
||||
log "Removing openconnect-lite from pipx"
|
||||
log_step "Удаляю openconnect-lite из pipx"
|
||||
log_detail "Это опционально: пакет может использоваться не только Lemana VPN."
|
||||
run pipx uninstall openconnect-lite
|
||||
log_ok "openconnect-lite removed from pipx"
|
||||
else
|
||||
log_warn "pipx not found; openconnect-lite package removal skipped."
|
||||
fi
|
||||
else
|
||||
log_skip "openconnect-lite kept; use --remove-openconnect-lite to uninstall it from pipx."
|
||||
fi
|
||||
|
||||
log "Done."
|
||||
log_ok "Done."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user