<# .SYNOPSIS Publica o Carinha no Orange Pi: compila o painel, empacota o código, faz backup na placa, instala e reinicia o serviço. Configurações e bancos da placa nunca são sobrescritos. .EXAMPLE .\deploy.ps1 # compila, publica em root@192.168.1.35:/opt/carinha-pc e reinicia .\deploy.ps1 -SoPacote # só gera o pacote local (nada vai para a placa) .\deploy.ps1 -Placa 192.168.1.40 -Usuario carinha .\deploy.ps1 -Ssh orangepi # usa um Host do ~/.ssh/config (chave própria) #> param( [string]$Placa = '192.168.1.35', [string]$Usuario = 'root', [string]$Destino = '/opt/carinha-pc', [int]$Porta = 8100, [string]$Ssh = '', [switch]$SemCompilar, [switch]$SoPacote ) # Comandos nativos (npm, tar, scp, ssh) escrevem avisos no stderr; o PowerShell 5.1 trataria isso # como erro fatal. Por isso o sucesso de cada um é decidido pelo $LASTEXITCODE. $ErrorActionPreference = 'Continue' Set-Location $PSScriptRoot -ErrorAction Stop # Código que vai para a placa. Ficam de fora: configurações (*.json da placa), bancos (*.db), # chaves, vendor/ (modelos já instalados na placa) e o fonte do painel (só web/dist vai). $arquivos = @( 'kiosk-server.py', 'comum.py', 'eventos.py', 'controle_pc.py', 'sinalizacao.py', 'servos.py', 'braco.py', 'cerebro.py', 'conversa.py', 'gemini.py', 'treinamento.py', 'figurinhas.py', 'api_config.py', 'historico.py', 'carinha.html', 'camera.html', 'reacoes-render.js', 'rastreador-worker.js', 'rosto-motor.js', 'voz-robo.js', 'version.json', 'obter-rastreamento.py', 'android/app/assets/qualidade-auto.js' ) if (Test-Path 'android/carinha-rosto.apk') { $arquivos += 'android/carinha-rosto.apk' } # Telas antigas (até 0.7) que ainda podem estar na placa; vão para o backup e saem da pasta. $legado = 'dashboard.html dashboard.css dashboard.js overview.js painel.html painel.css painel-controle.js ' + 'painel-video.js painel-rastreamento.js painel-conversa.js conversa.html conversa.css emojis.html biblioteca.js ' + 'treinamento.html treinamento.js treinamento.css braco.html braco.css braco.js braco-calibracao.js ' + 'braco-movimentos.js movimentos-core.js servos.html servos.css servos.js api.html api-painel.js ' + 'rastreamento-core.js audio-robot.js' if (-not $SemCompilar) { Write-Host '== Compilando o painel (web/)' Push-Location web try { if (-not (Test-Path node_modules)) { npm ci; if ($LASTEXITCODE) { throw 'npm ci falhou' } } npm run check; if ($LASTEXITCODE) { throw 'svelte-check encontrou erros' } npm test; if ($LASTEXITCODE) { throw 'testes do painel falharam' } npm run build; if ($LASTEXITCODE) { throw 'build do painel falhou' } } finally { Pop-Location } } if (-not (Test-Path 'web/dist/index.html')) { throw 'web/dist não existe: rode sem -SemCompilar' } $versao = (Get-Content version.json -Raw -ErrorAction Stop | ConvertFrom-Json).versao $carimbo = Get-Date -Format 'yyyyMMdd-HHmmss' $pacote = Join-Path $env:TEMP "carinha-$versao-$carimbo.tar.gz" Write-Host "== Empacotando $versao em $pacote" $faltando = $arquivos | Where-Object { -not (Test-Path $_) } if ($faltando) { throw "Arquivos ausentes: $($faltando -join ', ')" } & tar -czf $pacote @arquivos web/dist if ($LASTEXITCODE) { throw 'tar falhou' } $hash = (Get-FileHash $pacote -Algorithm SHA256 -ErrorAction Stop).Hash.ToLower() Write-Host " SHA256 $hash" if ($SoPacote) { & tar -tzf $pacote | Measure-Object | ForEach-Object { Write-Host " $($_.Count) arquivos no pacote" } Write-Host 'Pacote pronto (nada enviado à placa).' return } $alvo = if ($Ssh) { $Ssh } else { "$Usuario@$Placa" } $remoto = "/tmp/carinha-$versao-$carimbo.tar.gz" Write-Host "== Enviando para $alvo" & scp -q $pacote "${alvo}:$remoto" if ($LASTEXITCODE) { throw 'scp falhou' } # Na placa: backup do código atual, remove telas antigas, extrai o novo e reinicia. $script = @" set -e cd '$Destino' backup='$Destino/backup-$carimbo' mkdir -p "`$backup" for f in $($arquivos -join ' ') $legado; do [ -e "`$f" ] && cp -a --parents "`$f" "`$backup/" || true; done [ -d web/dist ] && mkdir -p "`$backup/web" && cp -a web/dist "`$backup/web/" || true echo "$hash $remoto" | sha256sum -c --quiet rm -f $legado rm -rf web/dist tar -xzf '$remoto' -C '$Destino' rm -f '$remoto' systemctl restart carinha-pc echo "backup em `$backup" "@ Write-Host '== Instalando na placa' # O PowerShell 5.1 põe BOM no stdin do ssh; sem tirar, o bash lê "set -e" e não para no primeiro erro. $script -replace "`r", '' | & ssh $alvo "sed '1s/^\xEF\xBB\xBF//' | bash -s" if ($LASTEXITCODE) { throw 'instalação na placa falhou (o backup foi feito antes de qualquer remoção)' } Write-Host '== Conferindo' $url = "http://${Placa}:$Porta" for ($i = 0; $i -lt 10; $i++) { try { $saude = Invoke-RestMethod "$url/app/health" -TimeoutSec 3 if ($saude.versao -eq $versao) { Write-Host "OK: $url/ responde versão $versao"; return } } catch { } Start-Sleep -Seconds 1 } throw "O serviço não respondeu a versão $versao em $url. Veja: ssh $alvo journalctl -u carinha-pc -n 50"