Files

294 lines
10 KiB
PowerShell

# ESP32-S3 Firmware Release Script
param(
[string]$Tag = "",
[switch]$SkipBuild,
[switch]$NoPush
)
$ErrorActionPreference = "Stop"
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$EimExe = "C:\Users\micha\AppData\Local\Microsoft\WinGet\Packages\Espressif.EIM-CLI_Microsoft.Winget.Source_8wekyb3d8bbwe\eim.exe"
$BinName = "esp32s3_audio.bin"
$BuildBin = Join-Path $ProjectRoot "build\$BinName"
$ReleasesDir = Join-Path $ProjectRoot "releases"
$CatalogFile = Join-Path $ReleasesDir "firmware-catalog.json"
# -- Helpers (Using pure ASCII to prevent encoding parser issues) --
function Write-Step([string]$msg) {
Write-Host "`n>> $msg" -ForegroundColor Cyan
}
function Write-Ok([string]$msg) {
Write-Host " [OK] $msg" -ForegroundColor Green
}
function Write-Err([string]$msg) {
Write-Host " [ERROR] $msg" -ForegroundColor Red
exit 1
}
# -- Step 1: Determine version --
Write-Step "Resolving version..."
if ($Tag -eq "") {
# Try exact tag on current commit first
$exactTag = & {
$oldEA = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
$t = git tag --points-at HEAD 2>$null
$ErrorActionPreference = $oldEA
$t
} | Select-Object -First 1
if ($exactTag) {
$Tag = $exactTag.Trim()
Write-Ok "Using exact commit tag: $Tag"
} else {
# Fall back to git describe (nearest tag + distance)
$described = & {
$oldEA = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
$d = git describe --tags --long 2>$null
$ErrorActionPreference = $oldEA
$d
}
if ($described) {
# If on a tag, described looks like "v1.0.0-0-gabcdef" -> strip -0-g...
if ($described -match '^(.+)-0-g[0-9a-f]+$') {
$Tag = $Matches[1]
Write-Ok "On tagged commit: $Tag"
} else {
# Not on a clean tag - build a dev version
$Tag = $described -replace '-(\d+)-g([0-9a-f]+)$', '+$1.$2'
Write-Ok "Dev build version: $Tag"
}
} else {
# No tags at all - generate from commit count
$commitCount = & {
$oldEA = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
$c = git rev-list HEAD --count
$ErrorActionPreference = $oldEA
$c
}
if ($commitCount) { $commitCount = $commitCount.Trim() } else { $commitCount = "0" }
$shortHash = & {
$oldEA = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
$h = git rev-parse --short HEAD
$ErrorActionPreference = $oldEA
$h
}
if ($shortHash) { $shortHash = $shortHash.Trim() } else { $shortHash = "unknown" }
$Tag = "v0.0.$commitCount-$shortHash"
Write-Ok "Auto-generated version (no tags): $Tag"
}
}
}
Write-Ok "Release version: $Tag"
# -- Step 2: Build and Package DEV-1 and PROTO-1 --
# Detect original model from device-config file to restore later
$deviceConfigFile = Join-Path $ProjectRoot "device-config"
$originalModel = "DEV-1"
if (Test-Path $deviceConfigFile) {
$configLine = Get-Content $deviceConfigFile | Where-Object { $_ -match '^MODEL=' }
if ($configLine) { $originalModel = ($configLine -split '=')[1].Trim() }
}
$SafeTag = $Tag -replace '[+/]', '-'
if (-not (Test-Path $ReleasesDir)) {
New-Item -ItemType Directory -Path $ReleasesDir | Out-Null
}
$DevBinSize = 0
$ProtoBinSize = 0
if (-not $SkipBuild) {
# 1. Build and copy DEV-1
Write-Step "Building firmware for DEV-1 ($Tag)..."
"MODEL=DEV-1" | Set-Content $deviceConfigFile
Push-Location $ProjectRoot
if (Test-Path "build") {
Remove-Item -Path "build" -Force -Recurse -ErrorAction SilentlyContinue
}
& $EimExe run "idf.py build"
if ($LASTEXITCODE -ne 0) { Pop-Location; Write-Err "Build failed for DEV-1 (exit code $LASTEXITCODE)" }
Pop-Location
if (-not (Test-Path $BuildBin)) { Write-Err "Binary not found: $BuildBin" }
$DevBinSize = (Get-Item $BuildBin).Length
$DestDevBin = Join-Path $ReleasesDir "firmware-$SafeTag-dev1.bin"
Copy-Item $BuildBin $DestDevBin -Force
Write-Ok "DEV-1 release packaged -> releases/firmware-$SafeTag-dev1.bin"
# 2. Build and copy PROTO-1
Write-Step "Building firmware for PROTO-1 ($Tag)..."
"MODEL=PROTO-1" | Set-Content $deviceConfigFile
Push-Location $ProjectRoot
if (Test-Path "build") {
Remove-Item -Path "build" -Force -Recurse -ErrorAction SilentlyContinue
}
& $EimExe run "idf.py build"
if ($LASTEXITCODE -ne 0) { Pop-Location; Write-Err "Build failed for PROTO-1 (exit code $LASTEXITCODE)" }
Pop-Location
if (-not (Test-Path $BuildBin)) { Write-Err "Binary not found: $BuildBin" }
$ProtoBinSize = (Get-Item $BuildBin).Length
$DestProtoBin = Join-Path $ReleasesDir "firmware-$SafeTag-proto1.bin"
Copy-Item $BuildBin $DestProtoBin -Force
Write-Ok "PROTO-1 release packaged -> releases/firmware-$SafeTag-proto1.bin"
# Restore original configuration
"MODEL=$originalModel" | Set-Content $deviceConfigFile
Push-Location $ProjectRoot
if (Test-Path "build") {
Remove-Item -Path "build" -Force -Recurse -ErrorAction SilentlyContinue
}
Pop-Location
} else {
Write-Host " (skipping build - using existing output in build/)" -ForegroundColor Yellow
if (-not (Test-Path $BuildBin)) {
Write-Err "Binary not found in build/: $BuildBin"
}
$DevBinSize = (Get-Item $BuildBin).Length
$ProtoBinSize = $DevBinSize
Copy-Item $BuildBin (Join-Path $ReleasesDir "firmware-$SafeTag-dev1.bin") -Force
Copy-Item $BuildBin (Join-Path $ReleasesDir "firmware-$SafeTag-proto1.bin") -Force
Write-Ok "Copied build/esp32s3_audio.bin to both dev1 and proto1 release outputs."
}
# -- Step 5: Build changelog --
Write-Step "Generating changelog..."
$prevTag = & {
$oldEA = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
$t = git describe --tags --abbrev=0 HEAD~1 2>$null
$ErrorActionPreference = $oldEA
$t
}
if ($prevTag) {
$logRange = "$($prevTag.Trim())..HEAD"
} else {
$logRange = "HEAD"
}
$gitLog = git log $logRange --pretty=format:"%s" 2>$null
$changelog = @()
if ($gitLog) {
$changelog = ($gitLog -split "`n") | Where-Object { $_ -match '\S' } | ForEach-Object { $_.Trim() }
}
if ($changelog.Count -eq 0) {
$changelog = @("No changes logged")
}
Write-Ok "$($changelog.Count) changelog entries"
# -- Step 6: Read / update firmware-catalog.json --
Write-Step "Updating firmware-catalog.json..."
if (Test-Path $CatalogFile) {
$catalog = Get-Content $CatalogFile -Raw | ConvertFrom-Json
$releases = [System.Collections.Generic.List[object]]@()
if ($catalog.releases) {
foreach ($item in $catalog.releases) { $releases.Add($item) }
}
} else {
$releases = [System.Collections.Generic.List[object]]@()
$catalog = [PSCustomObject]@{ latest = ""; releases = @() }
}
# Remove existing entries for this version if re-releasing
$filteredReleases = [System.Collections.Generic.List[object]]::new()
foreach ($r in $releases) {
if ($r.version -ne $Tag) {
$filteredReleases.Add($r)
}
}
$releases = $filteredReleases
# Prepend new entries (DEV-1 first, then PROTO-1)
$newDevEntry = [PSCustomObject]@{
version = $Tag
filename = "firmware-$SafeTag-dev1.bin"
date = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
size = $DevBinSize
model = "DEV-1"
commitHash = (git rev-parse HEAD).Trim()
changelog = $changelog
}
$newProtoEntry = [PSCustomObject]@{
version = $Tag
filename = "firmware-$SafeTag-proto1.bin"
date = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
size = $ProtoBinSize
model = "PROTO-1"
commitHash = (git rev-parse HEAD).Trim()
changelog = $changelog
}
$releases.Insert(0, $newDevEntry)
$releases.Insert(1, $newProtoEntry)
# Update catalog
$catalog.latest = $Tag
$catalog.releases = $releases.ToArray()
$catalog | ConvertTo-Json -Depth 10 | Set-Content $CatalogFile -Encoding UTF8
Write-Ok "Catalog written: $CatalogFile"
Write-Ok "Latest = $Tag | Total releases = $($releases.Count)"
# -- Step 7: Git tag (only for clean semver tags) --
Write-Step "Tagging..."
$isCleanTag = $Tag -match '^v\d+\.\d+\.\d+$'
if ($isCleanTag) {
$existingTag = git tag -l $Tag
if (-not $existingTag) {
git tag -a $Tag -m "Release $Tag"
Write-Ok "Created annotated tag: $Tag"
} else {
Write-Host " Tag $Tag already exists - skipping" -ForegroundColor Yellow
}
} else {
Write-Host " Dev version ($Tag) - skipping tag creation" -ForegroundColor Yellow
}
# -- Step 8: Commit catalog update --
Write-Step "Committing catalog..."
git add (Join-Path $ReleasesDir "firmware-catalog.json")
$hasChanges = git diff --quiet HEAD -- (Join-Path $ReleasesDir "firmware-catalog.json")
if ($LASTEXITCODE -ne 0) {
git commit -m "release: update firmware-catalog.json for $Tag"
Write-Ok "Committed catalog update"
} else {
Write-Host " No catalog changes to commit" -ForegroundColor Yellow
}
# -- Step 9: Push --
if (-not $NoPush) {
Write-Step "Pushing to origin..."
git push origin master
if ($isCleanTag) { git push origin $Tag }
Write-Ok "Pushed"
} else {
Write-Host " (skipping push - -NoPush flag set)" -ForegroundColor Yellow
}
# -- Done --
Write-Host ""
Write-Host "=======================================================" -ForegroundColor Magenta
Write-Host " Release complete: $Tag" -ForegroundColor Green
Write-Host " DEV-1 Bin : releases/firmware-$SafeTag-dev1.bin ($([math]::Round($DevBinSize/1024,1)) KB)" -ForegroundColor White
Write-Host " PROTO-1 Bin: releases/firmware-$SafeTag-proto1.bin ($([math]::Round($ProtoBinSize/1024,1)) KB)" -ForegroundColor White
Write-Host " Catalog: releases/firmware-catalog.json" -ForegroundColor White
Write-Host "=======================================================" -ForegroundColor Magenta
Write-Host ""