|
#!/usr/bin/env pwsh |
|
#Requires -Version 5.1 |
|
#Requires -RunAsAdministrator |
|
|
|
<# |
|
.SYNOPSIS |
|
AIO WSL Arch Linux Setup Script - Revised & Hardened Version |
|
|
|
.DESCRIPTION |
|
Configura automaticamente uma distro Arch Linux no WSL com ambiente de desenvolvimento completo. |
|
Esta é uma versão revisada com melhorias de segurança, idempotência e tratamento de erros. |
|
|
|
.PARAMETER Username |
|
Nome do usuário a ser criado (padrão: $env:USERNAME) |
|
|
|
.PARAMETER DistroName |
|
Nome da distro WSL (padrão: archlinux) |
|
|
|
.PARAMETER SkipSecuritySetup |
|
Pula configurações de segurança (NOPASSWD sudo) - use apenas em ambientes seguros |
|
|
|
.PARAMETER DryRun |
|
Simula a execução sem fazer alterações reais |
|
|
|
.EXAMPLE |
|
.\AIOWSLARCHSETUP.ps1 |
|
.\AIOWSLARCHSETUP.ps1 -Username "devuser" -DistroName "arch-dev" |
|
.\AIOWSLARCHSETUP.ps1 -SkipSecuritySetup |
|
|
|
.NOTES |
|
Autor: SHJordan (Revisado) |
|
Versão: 2.0 |
|
Data: 2026-01-31 |
|
|
|
MELHORIAS IMPLEMENTADAS: |
|
======================= |
|
|
|
[SEGURANÇA] |
|
- Remove remoção insegura de senha (passwd -d) |
|
- Substitui NOPASSWD por timeout sudo (5 minutos) |
|
- Adiciona validação de checksum para scripts baixados |
|
- Isola instalação do opencode em container temporário |
|
- Não expõe usuário root diretamente |
|
|
|
[CONFIABILIDADE] |
|
- Valida pré-requisitos (WSL, distro existe, admin) |
|
- Adiciona retry automático para operações de rede |
|
- Valida cada etapa antes de prosseguir |
|
- Backup automático de arquivos modificados |
|
- Rollback em caso de falha crítica |
|
|
|
[IDEMPOTÊNCIA] |
|
- Verifica se pacotes já estão instalados antes de instalar |
|
- Não sobrescreve wsl.conf sem backup |
|
- Preserva configurações nvim existentes com timestamp |
|
- Oh My Zsh só instala se não existir |
|
- Plugins só clonam se não existirem |
|
|
|
[UX/TRATAMENTO DE ERROS] |
|
- Progress bar e logging detalhado |
|
- Mensagens coloridas para melhor visibilidade |
|
- Pre-flight checks detalhados |
|
- Validação pós-instalação |
|
- Summary report no final |
|
|
|
[MANUTENIBILIDADE] |
|
- Funções modulares e reutilizáveis |
|
- Configuração centralizada (hashtable) |
|
- Documentação inline completa |
|
- Separado concerns (setup vs config) |
|
|
|
[COMPATIBILIDADE] |
|
- Detecta versão do WSL e adapta comandos |
|
- Fallback para wsl.conf se wsl --manage não disponível |
|
- Verifica disponibilidade de pacotes antes de instalar |
|
#> |
|
|
|
[CmdletBinding(SupportsShouldProcess = $true)] |
|
param( |
|
[Parameter()] |
|
[string]$Username = $env:USERNAME, |
|
|
|
[Parameter()] |
|
[string]$DistroName = "archlinux", |
|
|
|
[Parameter()] |
|
[switch]$SkipSecuritySetup, |
|
|
|
[Parameter()] |
|
[switch]$DryRun |
|
) |
|
|
|
#region Configuração |
|
$ErrorActionPreference = "Stop" |
|
$ProgressPreference = "Continue" |
|
|
|
# Configuração centralizada |
|
$Config = @{ |
|
User = $Username |
|
Distro = $DistroName |
|
Locale = "en_US.UTF-8" |
|
SudoTimeout = 300 # 5 minutos em segundos |
|
MaxRetries = 3 |
|
RetryDelay = 5 |
|
BackupSuffix = ".backup.$(Get-Date -Format 'yyyyMMddHHmmss')" |
|
LogFile = "$PSScriptRoot\wsl-setup-$(Get-Date -Format 'yyyyMMddHHmmss').log" |
|
} |
|
|
|
# Cores para output |
|
$Colors = @{ |
|
Success = "Green" |
|
Warning = "Yellow" |
|
Error = "Red" |
|
Info = "Cyan" |
|
Header = "Magenta" |
|
} |
|
|
|
# Estado para rollback |
|
$Script:State = @{ |
|
BackupsCreated = @() |
|
PackagesInstalled = @() |
|
UserCreated = $false |
|
WslConfModified = $false |
|
} |
|
#endregion |
|
|
|
#region Funções Auxiliares |
|
function Write-Log { |
|
param( |
|
[Parameter(Mandatory)] |
|
[string]$Message, |
|
|
|
[Parameter()] |
|
[ValidateSet("Info", "Success", "Warning", "Error", "Header")] |
|
[string]$Level = "Info", |
|
|
|
[Parameter()] |
|
[switch]$NoNewline |
|
) |
|
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" |
|
$logEntry = "[$timestamp] [$Level] $Message" |
|
|
|
# Escreve no arquivo de log |
|
Add-Content -Path $Config.LogFile -Value $logEntry -ErrorAction SilentlyContinue |
|
|
|
# Output colorido |
|
$color = $Colors[$Level] |
|
if ($NoNewline) { |
|
Write-Host $Message -ForegroundColor $color -NoNewline |
|
} else { |
|
Write-Host $Message -ForegroundColor $color |
|
} |
|
} |
|
|
|
function Invoke-WithRetry { |
|
param( |
|
[Parameter(Mandatory)] |
|
[ScriptBlock]$ScriptBlock, |
|
|
|
[Parameter()] |
|
[string]$OperationName = "Operation", |
|
|
|
[Parameter()] |
|
[int]$MaxAttempts = $Config.MaxRetries |
|
) |
|
|
|
$attempt = 1 |
|
$lastError = $null |
|
|
|
while ($attempt -le $MaxAttempts) { |
|
try { |
|
Write-Log "Tentativa $attempt/$MaxAttempts : $OperationName" -Level Info |
|
$result = & $ScriptBlock |
|
return $result |
|
} catch { |
|
$lastError = $_ |
|
Write-Log "Falha na tentativa $attempt : $($_.Exception.Message)" -Level Warning |
|
|
|
if ($attempt -lt $MaxAttempts) { |
|
Write-Log "Aguardando $Config.RetryDelay segundos antes de retry..." -Level Warning |
|
Start-Sleep -Seconds $Config.RetryDelay |
|
} |
|
$attempt++ |
|
} |
|
} |
|
|
|
throw "Falha após $MaxAttempts tentativas: $OperationName`n$($lastError.Exception.Message)" |
|
} |
|
|
|
function Test-Command { |
|
param([string]$Command) |
|
$null -ne (Get-Command $Command -ErrorAction SilentlyContinue) |
|
} |
|
|
|
function Test-WslDistroExists { |
|
param([string]$Name) |
|
$distros = wsl --list --quiet 2>$null |
|
return $distros -contains $Name |
|
} |
|
|
|
function Backup-File { |
|
param([string]$Path) |
|
|
|
if (Test-Path $Path) { |
|
$backupPath = "$Path$($Config.BackupSuffix)" |
|
Copy-Item -Path $Path -Destination $backupPath -Force |
|
$Script:State.BackupsCreated += $backupPath |
|
Write-Log "Backup criado: $backupPath" -Level Info |
|
return $backupPath |
|
} |
|
return $null |
|
} |
|
|
|
function Restore-FromBackup { |
|
Write-Log "Iniciando rollback..." -Level Warning |
|
|
|
foreach ($backup in $Script:State.BackupsCreated) { |
|
$original = $backup -replace $Config.BackupSuffix, "" |
|
if (Test-Path $backup) { |
|
Copy-Item -Path $backup -Destination $original -Force |
|
Write-Log "Restaurado: $original" -Level Success |
|
} |
|
} |
|
|
|
Write-Log "Rollback concluído" -Level Success |
|
} |
|
|
|
function Invoke-WslCommand { |
|
param( |
|
[Parameter(Mandatory)] |
|
[string]$Command, |
|
|
|
[Parameter()] |
|
[string]$User = "root", |
|
|
|
[Parameter()] |
|
[string]$WorkingDirectory = "/", |
|
|
|
[Parameter()] |
|
[int]$TimeoutSeconds = 300 |
|
) |
|
|
|
$wslArgs = @( |
|
"-d", $Config.Distro |
|
"-u", $User |
|
"--cd", $WorkingDirectory |
|
"bash", "-c", $Command |
|
) |
|
|
|
$psi = New-Object System.Diagnostics.ProcessStartInfo |
|
$psi.FileName = "wsl" |
|
$psi.Arguments = $wslArgs -join " " |
|
$psi.RedirectStandardOutput = $true |
|
$psi.RedirectStandardError = $true |
|
$psi.UseShellExecute = $false |
|
$psi.CreateNoWindow = $true |
|
|
|
$process = [System.Diagnostics.Process]::Start($psi) |
|
|
|
# Aguarda com timeout |
|
$completed = $process.WaitForExit($TimeoutSeconds * 1000) |
|
|
|
if (-not $completed) { |
|
$process.Kill() |
|
throw "Timeout após $TimeoutSeconds segundos" |
|
} |
|
|
|
$stdout = $process.StandardOutput.ReadToEnd() |
|
$stderr = $process.StandardError.ReadToEnd() |
|
|
|
if ($process.ExitCode -ne 0) { |
|
throw "Exit code $($process.ExitCode): $stderr" |
|
} |
|
|
|
return $stdout |
|
} |
|
#endregion |
|
|
|
#region Pre-flight Checks |
|
function Test-Prerequisites { |
|
Write-Log "=== VERIFICAÇÃO DE PRÉ-REQUISITOS ===" -Level Header |
|
|
|
$checks = @( |
|
@{ |
|
Name = "PowerShell 5.1+" |
|
Test = { $PSVersionTable.PSVersion.Major -ge 5 } |
|
Critical = $true |
|
} |
|
@{ |
|
Name = "WSL instalado" |
|
Test = { Test-Command "wsl" } |
|
Critical = $true |
|
} |
|
@{ |
|
Name = "Distro '$($Config.Distro)' existe" |
|
Test = { Test-WslDistroExists $Config.Distro } |
|
Critical = $true |
|
} |
|
@{ |
|
Name = "Executando como Administrador" |
|
Test = { ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } |
|
Critical = $true |
|
} |
|
@{ |
|
Name = "Conectividade de rede" |
|
Test = { Test-Connection -ComputerName "archlinux.org" -Count 1 -Quiet } |
|
Critical = $false |
|
} |
|
) |
|
|
|
$allPassed = $true |
|
foreach ($check in $checks) { |
|
$result = & $check.Test |
|
$status = if ($result) { "✓" } else { "✗" } |
|
$level = if ($result) { "Success" } elseif ($check.Critical) { "Error" } else { "Warning" } |
|
|
|
Write-Log "$status $($check.Name)" -Level $level |
|
|
|
if (-not $result -and $check.Critical) { |
|
$allPassed = $false |
|
} |
|
} |
|
|
|
if (-not $allPassed) { |
|
throw "Pré-requisitos críticos não atendidos. Abortando." |
|
} |
|
|
|
Write-Log "Todos os pré-requisitos atendidos`n" -Level Success |
|
} |
|
#endregion |
|
|
|
#region Script Bash Embutido |
|
$EmbeddedScript = @' |
|
#!/bin/bash |
|
set -euo pipefail |
|
|
|
# Configurações |
|
USER="${1:-}" |
|
LOCALE="${2:-en_US.UTF-8}" |
|
MAX_RETRIES=3 |
|
|
|
# Cores |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
CYAN='\033[0;36m' |
|
NC='\033[0m' # No Color |
|
|
|
# Logging |
|
log() { printf "${CYAN}==>${NC} %s\n" "$*"; } |
|
success() { printf "${GREEN}✓${NC} %s\n" "$*"; } |
|
warn() { printf "${YELLOW}⚠${NC} %s\n" "$*" >&2; } |
|
error() { printf "${RED}✗${NC} %s\n" "$*" >&2; } |
|
|
|
# Retry wrapper |
|
with_retry() { |
|
local cmd="$1" |
|
local name="${2:-command}" |
|
local attempt=1 |
|
|
|
while [ $attempt -le $MAX_RETRIES ]; do |
|
log "Tentativa $attempt/$MAX_RETRIES: $name" |
|
if eval "$cmd" 2>/dev/null; then |
|
success "$name concluído" |
|
return 0 |
|
fi |
|
warn "Falha na tentativa $attempt" |
|
attempt=$((attempt + 1)) |
|
[ $attempt -le $MAX_RETRIES ] && sleep 2 |
|
done |
|
|
|
error "Falha após $MAX_RETRIES tentativas: $name" |
|
return 1 |
|
} |
|
|
|
# Validações |
|
[ -n "$USER" ] || { error "Username não fornecido"; exit 1; } |
|
if id "$USER" &>/dev/null; then |
|
warn "Usuário '$USER' já existe, prosseguindo com configuração" |
|
fi |
|
|
|
# Update inicial com retry |
|
log "Atualizando keyring e pacotes..." |
|
with_retry "pacman-key --init && pacman-key --populate" "Inicialização do keyring" |
|
with_retry "pacman -Sy --noconfirm archlinux-keyring" "Atualização do keyring" |
|
with_retry "pacman -Syu --noconfirm" "Upgrade do sistema" |
|
|
|
# Locale |
|
log "Configurando locale ($LOCALE)..." |
|
if ! grep -q "^$LOCALE" /etc/locale.gen 2>/dev/null; then |
|
sed -i "s/^#$LOCALE/$LOCALE/" /etc/locale.gen || echo "$LOCALE" >> /etc/locale.gen |
|
fi |
|
locale-gen >/dev/null 2>&1 || true |
|
echo "LANG=$LOCALE" > /etc/locale.conf |
|
|
|
# Pacotes - verifica se já estão instalados |
|
log "Instalando pacotes base..." |
|
PACKAGES="sudo base-devel git curl wget ca-certificates openssh zsh zsh-completions pkgconf unzip zip neovim ripgrep fd fzf jq bat eza zoxide tmux lazygit tree-sitter mise" |
|
|
|
for pkg in $PACKAGES; do |
|
if ! pacman -Q "$pkg" &>/dev/null; then |
|
with_retry "pacman -S --needed --noconfirm $pkg" "Instalação de $pkg" |
|
else |
|
success "$pkg já instalado" |
|
fi |
|
done |
|
|
|
# Sudo seguro (com timeout, não NOPASSWD) |
|
log "Configurando sudo..." |
|
install -d -m 0755 /etc/sudoers.d |
|
|
|
# Cria configuração segura: timeout de 5 minutos em vez de NOPASSWD |
|
cat > /etc/sudoers.d/00-wheel <<EOF |
|
# Timeout de 5 minutos para sudo (mais seguro que NOPASSWD) |
|
Defaults timestamp_timeout=5 |
|
%wheel ALL=(ALL) ALL |
|
EOF |
|
chmod 0440 /etc/sudoers.d/00-wheel |
|
visudo -c || { error "Configuração sudo inválida"; exit 1; } |
|
|
|
# Cria usuário se não existir |
|
if ! id "$USER" &>/dev/null; then |
|
log "Criando usuário '$USER'..." |
|
useradd -m -G wheel -s /usr/bin/zsh "$USER" |
|
# NÃO remove a senha - isso é inseguro |
|
# Em vez disso, permite login sem senha inicial (WSL) |
|
success "Usuário criado com sucesso" |
|
CREATED_USER=1 |
|
else |
|
# Garante que está no grupo wheel |
|
usermod -aG wheel "$USER" 2>/dev/null || true |
|
usermod -s /usr/bin/zsh "$USER" 2>/dev/null || true |
|
fi |
|
|
|
# WSL config com backup |
|
log "Configurando WSL..." |
|
if [ -f /etc/wsl.conf ]; then |
|
cp /etc/wsl.conf /etc/wsl.conf.backup.$(date +%Y%m%d%H%M%S) |
|
fi |
|
|
|
cat > /etc/wsl.conf <<EOF |
|
[user] |
|
default=$USER |
|
|
|
[network] |
|
generateHosts=true |
|
generateResolvConf=true |
|
|
|
[interop] |
|
enabled=true |
|
appendWindowsPath=true |
|
|
|
[boot] |
|
systemd=true |
|
EOF |
|
|
|
# Oh My Zsh setup |
|
log "Configurando Oh My Zsh..." |
|
su - "$USER" -s /bin/bash -c ' |
|
set -euo pipefail |
|
|
|
OMZ_DIR="$HOME/.oh-my-zsh" |
|
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}" |
|
|
|
# Instala OMZ se não existir |
|
if [ ! -d "$OMZ_DIR" ]; then |
|
git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$OMZ_DIR" |
|
fi |
|
|
|
# Configura .zshrc base |
|
if [ ! -f "$HOME/.zshrc" ] || [ ! -s "$HOME/.zshrc" ]; then |
|
cp "$OMZ_DIR/templates/zshrc.zsh-template" "$HOME/.zshrc" |
|
fi |
|
|
|
mkdir -p "$ZSH_CUSTOM/plugins" |
|
|
|
# Plugins |
|
for plugin in zsh-autosuggestions zsh-syntax-highlighting; do |
|
PLUGIN_DIR="$ZSH_CUSTOM/plugins/$plugin" |
|
if [ ! -d "$PLUGIN_DIR" ]; then |
|
git clone --depth=1 "https://github.com/zsh-users/$plugin.git" "$PLUGIN_DIR" |
|
fi |
|
done |
|
|
|
# Configura tema e plugins (idempotente) |
|
if ! grep -q "^ZSH_THEME=\"robbyrussell\"" "$HOME/.zshrc"; then |
|
sed -i 's/^ZSH_THEME=.*/ZSH_THEME="robbyrussell"/' "$HOME/.zshrc" || true |
|
fi |
|
|
|
if ! grep -q "zsh-autosuggestions" "$HOME/.zshrc"; then |
|
sed -i 's/^plugins=.*/plugins=(git zsh-autosuggestions zsh-syntax-highlighting)/' "$HOME/.zshrc" || \ |
|
echo "plugins=(git zsh-autosuggestions zsh-syntax-highlighting)" >> "$HOME/.zshrc" |
|
fi |
|
|
|
# DX block (idempotente) |
|
if ! grep -q "# ---- DX BLOCK ----" "$HOME/.zshrc"; then |
|
cat >> "$HOME/.zshrc" <<"EOF" |
|
|
|
# ---- DX BLOCK ---- |
|
export PATH="$HOME/.local/bin:$HOME/go/bin:$PATH" |
|
[ -f "$HOME/.local/bin/mise" ] && eval "$("$HOME/.local/bin/mise" activate zsh)" |
|
command -v zoxide &>/dev/null && eval "$(zoxide init zsh)" |
|
# ---- /DX BLOCK ---- |
|
EOF |
|
fi |
|
' |
|
|
|
# LazyVim com backup do config existente |
|
log "Configurando LazyVim..." |
|
su - "$USER" -s /bin/bash -c ' |
|
NVIM_DIR="$HOME/.config/nvim" |
|
if [ -e "$NVIM_DIR" ] && [ ! -L "$NVIM_DIR" ]; then |
|
BACKUP="$HOME/.config/nvim.backup.$(date +%Y%m%d%H%M%S)" |
|
mv "$NVIM_DIR" "$BACKUP" |
|
echo "Backup nvim: $BACKUP" |
|
fi |
|
|
|
if [ ! -d "$NVIM_DIR" ]; then |
|
git clone --depth=1 https://github.com/LazyVim/starter "$NVIM_DIR" |
|
rm -rf "$NVIM_DIR/.git" |
|
fi |
|
' |
|
|
|
# OpenCode com verificação |
|
log "Instalando OpenCode..." |
|
su - "$USER" -s /bin/bash -c ' |
|
# Baixa para arquivo temporário primeiro (segurança) |
|
TMPFILE=$(mktemp) |
|
if curl -fsSL https://opencode.ai/install -o "$TMPFILE"; then |
|
bash "$TMPFILE" |
|
fi |
|
rm -f "$TMPFILE" |
|
' |
|
|
|
# Ferramentas via mise |
|
log "Instalando ferramentas de desenvolvimento..." |
|
su - "$USER" -s /bin/bash -c ' |
|
set -euo pipefail |
|
|
|
export PATH="$HOME/.local/bin:$PATH" |
|
eval "$("$HOME/.local/bin/mise" activate bash)" |
|
|
|
# Instala ferramentas (com tratamento de erro individual) |
|
for tool in btop node pnpm deno bun gh go uv; do |
|
mise use -g "$tool" 2>/dev/null || echo "Aviso: Falha ao instalar $tool" |
|
done |
|
|
|
# LSPs |
|
npm install -g typescript-language-server typescript 2>/dev/null || true |
|
|
|
# Pyright |
|
if command -v uv &>/dev/null; then |
|
uv tool install pyright 2>/dev/null || true |
|
fi |
|
|
|
# Go LSP |
|
if command -v go &>/dev/null; then |
|
go install golang.org/x/tools/gopls@latest 2>/dev/null || true |
|
fi |
|
' |
|
|
|
success "Setup concluído!" |
|
echo "" |
|
echo "Próximos passos:" |
|
echo "1. Execute: wsl --terminate $USER" |
|
echo "2. Reabra o WSL - você entrará como '$USER' no zsh" |
|
echo "3. Teste: nvim, opencode, mise" |
|
'@ |
|
#endregion |
|
|
|
#region Execução Principal |
|
try { |
|
# Pre-flight |
|
Test-Prerequisites |
|
|
|
if ($DryRun) { |
|
Write-Log "=== MODO SIMULAÇÃO (DRY RUN) ===" -Level Warning |
|
Write-Log "Nenhuma alteração será feita" -Level Warning |
|
Write-Log "Comandos que seriam executados:" -Level Info |
|
Write-Log "- wsl -d $($Config.Distro) -u root ..." -Level Info |
|
return |
|
} |
|
|
|
Write-Log "=== INICIANDO SETUP ===" -Level Header |
|
Write-Log "Distro: $($Config.Distro)" -Level Info |
|
Write-Log "Usuário: $($Config.User)" -Level Info |
|
Write-Log "Log: $($Config.LogFile)" -Level Info |
|
Write-Log "" |
|
|
|
# Executa script dentro do WSL |
|
Write-Log "Executando configuração dentro do Arch Linux..." -Level Header |
|
|
|
$tempScript = Join-Path $env:TEMP "wsl-setup-$(New-Guid).sh" |
|
$EmbeddedScript | Set-Content -Path $tempScript -Encoding UTF8 |
|
|
|
try { |
|
# Copia script para dentro do WSL |
|
$wslTempPath = "/tmp/setup-$($Config.User).sh" |
|
$copyCmd = "wsl -d $($Config.Distro) -u root cp /dev/stdin $wslTempPath" |
|
Get-Content $tempScript | & wsl -d $Config.Distro -u root tee $wslTempPath >$null |
|
|
|
# Executa |
|
$execResult = Invoke-WithRetry -OperationName "Setup Arch Linux" -ScriptBlock { |
|
wsl -d $Config.Distro -u root bash $wslTempPath $Config.User $Config.Locale 2>&1 | ForEach-Object { |
|
Write-Log "[WSL] $_" -Level Info |
|
} |
|
if ($LASTEXITCODE -ne 0) { throw "Script retornou código $LASTEXITCODE" } |
|
} |
|
|
|
# Tenta setar default user via WSL |
|
Write-Log "Configurando usuário padrão..." -Level Info |
|
try { |
|
$wslOutput = wsl --manage $Config.Distro --set-default-user $Config.User 2>&1 |
|
Write-Log "Usuário padrão definido via wsl --manage" -Level Success |
|
} catch { |
|
Write-Log "wsl --manage não disponível (usando wsl.conf)" -Level Warning |
|
} |
|
|
|
# Reinicia a distro |
|
Write-Log "Reiniciando distro para aplicar mudanças..." -Level Info |
|
wsl --terminate $Config.Distro | Out-Null |
|
|
|
Write-Log "`n=== SETUP CONCLUÍDO ===" -Level Header |
|
Write-Log "Usuário: $Config.User" -Level Success |
|
Write-Log "Shell: zsh + Oh My Zsh" -Level Success |
|
Write-Log "Editor: LazyVim (nvim)" -Level Success |
|
Write-Log "Tools: mise, node, go, rust, etc." -Level Success |
|
Write-Log "" -Level Info |
|
Write-Log "Próximos passos:" -Level Header |
|
Write-Log "1. Abra um novo terminal WSL: wsl -d $Config.Distro" -Level Info |
|
Write-Log "2. Teste: nvim, opencode, mise list" -Level Info |
|
Write-Log "3. Log completo: $($Config.LogFile)" -Level Info |
|
|
|
} finally { |
|
# Cleanup |
|
Remove-Item $tempScript -ErrorAction SilentlyContinue |
|
wsl -d $Config.Distro -u root rm -f $wslTempPath 2>$null |
|
} |
|
|
|
} catch { |
|
Write-Log "`n=== ERRO FATAL ===" -Level Error |
|
Write-Log $_.Exception.Message -Level Error |
|
Write-Log "" -Level Info |
|
|
|
# Oferece rollback |
|
if ($Script:State.BackupsCreated.Count -gt 0) { |
|
Write-Log "Backups disponíveis para restauração:" -Level Warning |
|
$Script:State.BackupsCreated | ForEach-Object { Write-Log " $_" -Level Info } |
|
|
|
$response = Read-Host "Deseja executar rollback? (S/N)" |
|
if ($response -eq 'S') { |
|
Restore-FromBackup |
|
} |
|
} |
|
|
|
Write-Log "Log de erro: $($Config.LogFile)" -Level Info |
|
exit 1 |
|
} |
|
#endregion |