177 lines
7.7 KiB
PowerShell
177 lines
7.7 KiB
PowerShell
# make-adb-bundle.ps1 — собирает offline-бандл (Windows-хост, без WSL).
|
||
#
|
||
# powershell -ExecutionPolicy Bypass -File make-adb-bundle.ps1 -Version 2.0
|
||
#
|
||
# По умолчанию берёт софт из .\software (бинарник ButtonTask + webconfig\),
|
||
# пакеты из .\debs. Все shell-скрипты/юниты нормализуются в LF (CRLF-фикс).
|
||
param(
|
||
[string]$SoftwareDir = '.\software',
|
||
[Parameter(Mandatory = $true)][string]$Version,
|
||
[string]$BinPath,
|
||
[string]$DebsDir,
|
||
[string]$MasterPassword
|
||
)
|
||
$ErrorActionPreference = 'Stop'
|
||
$Here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
|
||
function Log($m) { Write-Host "[bundle] $m" -ForegroundColor Green }
|
||
function Warn($m) { Write-Host "[warn] $m" -ForegroundColor Yellow }
|
||
function Die($m) { Write-Host "[error] $m" -ForegroundColor Red; exit 1 }
|
||
|
||
function Write-Bytes($path, [string]$text) {
|
||
[System.IO.File]::WriteAllText($path, $text, (New-Object System.Text.UTF8Encoding($false)))
|
||
}
|
||
function ConvertTo-Lf($file) {
|
||
$t = [System.IO.File]::ReadAllText($file)
|
||
$t = ($t -replace "`r`n", "`n") -replace "`r", "`n"
|
||
Write-Bytes $file $t
|
||
}
|
||
|
||
if (-not (Test-Path $SoftwareDir)) { Die "нет каталога: $SoftwareDir" }
|
||
$SoftwareDir = (Resolve-Path $SoftwareDir).Path
|
||
if (-not $BinPath) { $BinPath = Join-Path $SoftwareDir 'ButtonTask' }
|
||
if (-not $DebsDir) { $DebsDir = Join-Path $Here 'debs' }
|
||
$web = Join-Path $SoftwareDir 'webconfig'
|
||
|
||
if (-not (Test-Path $BinPath)) { Die "нет бинарника: $BinPath" }
|
||
if (-not (Test-Path $web)) { Die "нет webconfig: $web" }
|
||
if (-not (Get-Command tar.exe -ErrorAction SilentlyContinue)) {
|
||
Die "не найден tar.exe (есть в Windows 10 1803+)"
|
||
}
|
||
|
||
# юниты systemd — все *.service из webconfig
|
||
$svc = @(Get-ChildItem $web -Filter '*.service' -File)
|
||
if ($svc.Count -eq 0) { Die "в webconfig нет ни одного *.service" }
|
||
|
||
# хелперы (должны совпадать с device-install.sh)
|
||
$scriptsSrc = Join-Path $web 'scripts'
|
||
$needScripts = @(
|
||
'bt-netconfig', 'bt-netconfig-boot', 'bt-update', 'bt-service',
|
||
'bt-settime', 'bt-reboot', 'bt-access', 'bt-brightness', 'bt-set-master',
|
||
'buttontask.sudoers'
|
||
)
|
||
foreach ($s in $needScripts) {
|
||
if (-not (Test-Path (Join-Path $scriptsSrc $s))) { Die "нет webconfig\scripts\$s" }
|
||
}
|
||
|
||
$stage = Join-Path $Here "stage-$Version"
|
||
if (Test-Path $stage) { Remove-Item -Recurse -Force $stage }
|
||
foreach ($d in 'app', 'debs', 'scripts', 'config', 'icons', 'systemd') {
|
||
New-Item -ItemType Directory -Force -Path (Join-Path $stage $d) | Out-Null
|
||
}
|
||
|
||
Log "копирую бинарник"
|
||
Copy-Item $BinPath (Join-Path $stage 'app\ButtonTask')
|
||
|
||
Log "копирую webconfig"
|
||
Copy-Item $web (Join-Path $stage 'webconfig') -Recurse
|
||
|
||
Log "копирую хелперы и sudoers"
|
||
foreach ($s in $needScripts) { Copy-Item (Join-Path $scriptsSrc $s) (Join-Path $stage "scripts\$s") }
|
||
|
||
Log "копирую юниты: $($svc.Name -join ', ')"
|
||
foreach ($u in $svc) { Copy-Item $u.FullName (Join-Path $stage "systemd\$($u.Name)") }
|
||
$ovr = Join-Path $web 'buttontask.service.d\override.conf.example'
|
||
if (Test-Path $ovr) { Copy-Item $ovr (Join-Path $stage 'systemd\override.conf.example') }
|
||
else { Warn "override.conf.example не найден — авто-настройка тача будет пропущена" }
|
||
|
||
# config.json — ищем; если нигде нет, кладём дефолтный
|
||
$cfg = @(
|
||
(Join-Path $SoftwareDir 'config\config.json'),
|
||
(Join-Path $web 'config\config.json'),
|
||
(Join-Path $Here 'config\config.json')
|
||
) | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||
if ($cfg) {
|
||
Copy-Item $cfg (Join-Path $stage 'config\config.json')
|
||
Log "config.json: $cfg"
|
||
}
|
||
else {
|
||
Warn "config.json нигде не найден — кладу дефолтный"
|
||
$default = @'
|
||
{
|
||
"version": 2,
|
||
"layout": { "mode": "grid", "columns": 2, "spacing": 10, "showLabels": true },
|
||
"background": { "type": "gradient", "color1": "#00007b", "color2": "#7b007b", "animated": true, "imagePath": "" },
|
||
"feedback": { "okColor": "#00cc44", "errorColor": "#cc2200", "okWidth": 6, "errorWidth": 6, "glowRadius": 20 },
|
||
"settings": { "darkMode": true, "password": "admin_26", "kioskMode": false, "iconsDir": "/opt/buttontask/icons", "webPort": 8080, "brightness": 60 },
|
||
"buttons": [
|
||
{
|
||
"id": "cleaning-call",
|
||
"label": "Вызов клининга",
|
||
"iconPath": "cleaner1Crop.png",
|
||
"action": { "type": "http_get", "url": "http://192.168.30.147:80/api/click/btn?bid=01*04*01", "headers": {}, "body": "" },
|
||
"latchReset": { "enabled": true, "resetUrl": "http://192.168.30.147:80/api/click/btn?bid=01*04*00", "successMatch": "OK" },
|
||
"feedback": { "successText": "Вызов отправлен", "errorText": "Ошибка", "pendingText": "...", "fadeMs": 5000 },
|
||
"trigger": { "mode": "hold", "holdMs": 800 },
|
||
"color": "#7b007b"
|
||
}
|
||
]
|
||
}
|
||
'@
|
||
Write-Bytes (Join-Path $stage 'config\config.json') ($default -replace "`r`n", "`n")
|
||
}
|
||
|
||
$netCfg = Join-Path $SoftwareDir 'config\network.json'
|
||
if (Test-Path $netCfg) {
|
||
Copy-Item $netCfg (Join-Path $stage 'config\network.json')
|
||
Log "network.json: $netCfg"
|
||
}
|
||
else {
|
||
Warn 'network.json не найден в .\software\config\ — сеть останется на DHCP'
|
||
}
|
||
|
||
$iconExts = '.png', '.jpg', '.jpeg', '.bmp', '.svg', '.gif'
|
||
$iconSources = @()
|
||
$iconSources += Get-ChildItem $SoftwareDir -File -ErrorAction SilentlyContinue |
|
||
Where-Object { $iconExts -contains $_.Extension.ToLowerInvariant() }
|
||
$iconsDir = Join-Path $SoftwareDir 'icons'
|
||
if (Test-Path $iconsDir) {
|
||
$iconSources += Get-ChildItem $iconsDir -File -ErrorAction SilentlyContinue |
|
||
Where-Object { $iconExts -contains $_.Extension.ToLowerInvariant() }
|
||
}
|
||
$iconSources = @($iconSources | Sort-Object FullName -Unique)
|
||
foreach ($ic in $iconSources) {
|
||
Copy-Item $ic.FullName (Join-Path $stage "icons\$($ic.Name)")
|
||
}
|
||
if ($iconSources.Count -gt 0) {
|
||
Log "иконок в бандле: $($iconSources.Count)"
|
||
}
|
||
else {
|
||
Warn "иконки в .\software или .\software\icons не найдены"
|
||
}
|
||
|
||
if ($MasterPassword) {
|
||
Write-Bytes (Join-Path $stage 'config\.master') $MasterPassword
|
||
Log 'мастер-пароль записан в бандл (config/.master)'
|
||
}
|
||
else {
|
||
Warn 'мастер-пароль не задан (-MasterPassword) — на устройстве задайте вручную: bt-set-master'
|
||
}
|
||
|
||
# .deb
|
||
if ((Test-Path $DebsDir) -and (Get-ChildItem $DebsDir -Filter *.deb -ErrorAction SilentlyContinue)) {
|
||
Copy-Item (Join-Path $DebsDir '*.deb') (Join-Path $stage 'debs')
|
||
Log "пакетов в бандле: $((Get-ChildItem (Join-Path $stage 'debs') -Filter *.deb).Count)"
|
||
}
|
||
else { Warn "каталог debs пуст ($DebsDir) — сначала прогоните collect-debs.ps1" }
|
||
|
||
Log "кладу device-install.sh и VERSION"
|
||
Copy-Item (Join-Path $Here 'device-install.sh') (Join-Path $stage 'device-install.sh')
|
||
Write-Bytes (Join-Path $stage 'VERSION') ($Version + "`n")
|
||
|
||
Log "нормализую переводы строк в LF (CRLF-фикс)"
|
||
$fix = @()
|
||
$fix += Get-ChildItem (Join-Path $stage 'scripts') -File
|
||
$fix += Get-ChildItem (Join-Path $stage 'systemd') -File
|
||
$fix += Get-Item (Join-Path $stage 'device-install.sh')
|
||
$fix += @(Get-ChildItem (Join-Path $stage 'webconfig') -Recurse -File -Include 'bt-*', '*.sh', '*.service', '*.sudoers', 'override.conf*')
|
||
$fix | Sort-Object FullName -Unique | ForEach-Object { ConvertTo-Lf $_.FullName }
|
||
|
||
Log "пакую архив"
|
||
$tgz = Join-Path $Here "buttontask-deploy-$Version.tgz"
|
||
if (Test-Path $tgz) { Remove-Item $tgz }
|
||
& tar.exe -C $stage -czf $tgz .
|
||
if ($LASTEXITCODE -ne 0) { Die "tar завершился с ошибкой" }
|
||
Remove-Item -Recurse -Force $stage
|
||
Log "готово: $tgz"
|