param([string]$ApkDir = "") # Load .env.local $envFile = Join-Path $PSScriptRoot ".env.local" if (-not (Test-Path $envFile)) { Write-Error ".env.local not found"; exit 1 } Get-Content $envFile | ForEach-Object { $line = $_.Trim() if ($line -and -not $line.StartsWith('#') -and $line.Contains('=')) { $idx = $line.IndexOf('=') $key = $line.Substring(0, $idx).Trim() $val = $line.Substring($idx + 1).Trim() [System.Environment]::SetEnvironmentVariable($key, $val) } } $GITEA_TOKEN = $env:GITEA_TOKEN $GITEA_BASE_URL = $env:GITEA_BASE_URL $GITEA_OWNER = $env:GITEA_OWNER $GITEA_REPO = $env:GITEA_REPO if (-not $GITEA_TOKEN) { Write-Error "GITEA_TOKEN not set in .env.local"; exit 1 } # Find latest APK build folder if (-not $ApkDir) { $root = Join-Path $PSScriptRoot "build\apk_releases" if (-not (Test-Path $root)) { Write-Error "build\apk_releases not found"; exit 1 } $ApkDir = Get-ChildItem $root -Directory | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty FullName } if (-not (Test-Path $ApkDir)) { Write-Error "APK folder not found: $ApkDir"; exit 1 } $apkFiles = Get-ChildItem $ApkDir -Filter "*.apk" if ($apkFiles.Count -eq 0) { Write-Error "No APK files in: $ApkDir"; exit 1 } # Read version from pubspec.yaml $publine = Get-Content (Join-Path $PSScriptRoot "pubspec.yaml") | Where-Object { $_ -match "^version:" } | Select-Object -First 1 $version = if ($publine -match "version:\s*(\S+)") { $Matches[1].Split("+")[0] } else { "1.0.0" } $buildNum = if ($publine -match "version:\s*[^+]+\+(\S+)") { $Matches[1] } else { "0" } $dateStr = Get-Date -Format "yyyy-MM-dd" $tagName = "v$version" $relName = "Ponshu Room $version ($dateStr)" # APKの埋め込みバージョンと pubspec.yaml が一致するか確認 Write-Host " Checking APK version matches pubspec ($version+$buildNum)..." -ForegroundColor Gray $firstApk = $apkFiles | Select-Object -First 1 $apkInfo = & flutter.bat --version 2>$null # aapt2 でバージョン確認(利用可能な場合) $aapt2 = Get-ChildItem "$env:LOCALAPPDATA\Android\Sdk\build-tools" -Recurse -Filter "aapt2.exe" -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName if ($aapt2) { $apkDump = & $aapt2 dump badging $firstApk.FullName 2>$null | Select-String "versionName|versionCode" $apkVersion = if ($apkDump -match "versionName='([^']+)'") { $Matches[1] } else { $null } $apkBuild = if ($apkDump -match "versionCode='([^']+)'") { $Matches[1] } else { $null } if ($apkVersion -and $apkVersion -ne $version) { Write-Host "" Write-Host " [ERROR] APK version mismatch!" -ForegroundColor Red Write-Host " APK contains : $apkVersion (build $apkBuild)" -ForegroundColor Red Write-Host " pubspec.yaml : $version+$buildNum" -ForegroundColor Red Write-Host " -> Rebuild APKs after version bump, then retry." -ForegroundColor Yellow exit 1 } Write-Host " OK: APK version = $apkVersion (build $apkBuild)" -ForegroundColor Green } else { Write-Host " (aapt2 not found, skipping version check)" -ForegroundColor DarkGray } Write-Host "================================================" -ForegroundColor Cyan Write-Host " Ponshu Room - Gitea Auto Release" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host " Server : $GITEA_BASE_URL" -ForegroundColor Gray Write-Host " Repo : $GITEA_OWNER/$GITEA_REPO" -ForegroundColor Gray Write-Host " Tag : $tagName" -ForegroundColor Gray Write-Host " APKs : $($apkFiles.Count) files" -ForegroundColor Gray Write-Host "" $authHeader = @{ "Authorization" = "token $GITEA_TOKEN" } # Step 1: Delete existing release if found Write-Host "[1/3] Checking existing release..." -ForegroundColor Yellow try { $existing = Invoke-RestMethod ` -Uri "$GITEA_BASE_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/releases/tags/$tagName" ` -Headers $authHeader -Method Get -ErrorAction Stop Write-Host " Found existing (ID: $($existing.id)). Deleting..." -ForegroundColor Yellow Invoke-RestMethod ` -Uri "$GITEA_BASE_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/releases/$($existing.id)" ` -Headers $authHeader -Method Delete | Out-Null } catch { Write-Host " No existing release. Creating new." -ForegroundColor Gray } # Step 2: Create release Write-Host "[2/3] Creating release..." -ForegroundColor Yellow $releaseNotes = @( "## Ponshu Room $version ($dateStr)", "", "### APK Files", "- ponshu_room_consumer_maita.apk : Maita", "- ponshu_room_consumer_eiji.apk : Eiji", "", "### Install", "1. Download the APK for your name", "2. On Android: Settings > Security > Allow unknown sources", "3. Tap the downloaded APK to install", "", "Requires Android 8.0+ (API 26+)" ) -join "`n" $body = @{ tag_name = $tagName name = $relName body = $releaseNotes draft = $false prerelease = $false } | ConvertTo-Json -Depth 3 $release = Invoke-RestMethod ` -Uri "$GITEA_BASE_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/releases" ` -Headers $authHeader ` -Method Post ` -ContentType "application/json" ` -Body $body Write-Host " OK - Release ID: $($release.id)" -ForegroundColor Green # Step 3: Upload APKs with multipart/form-data Write-Host "[3/3] Uploading APK files..." -ForegroundColor Yellow $downloadUrls = @{} foreach ($apk in $apkFiles) { $mb = [math]::Round($apk.Length / 1MB, 1) Write-Host " Uploading: $($apk.Name) ($mb MB)..." -ForegroundColor Gray $boundary = [System.Guid]::NewGuid().ToString() $uploadUrl = "$GITEA_BASE_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/releases/$($release.id)/assets?name=$($apk.Name)" $fileBytes = [System.IO.File]::ReadAllBytes($apk.FullName) $enc = [System.Text.Encoding]::UTF8 $bodyParts = [System.Collections.Generic.List[byte]]::new() $header = "--$boundary`r`nContent-Disposition: form-data; name=`"attachment`"; filename=`"$($apk.Name)`"`r`nContent-Type: application/octet-stream`r`n`r`n" $bodyParts.AddRange($enc.GetBytes($header)) $bodyParts.AddRange($fileBytes) $footer = "`r`n--$boundary--`r`n" $bodyParts.AddRange($enc.GetBytes($footer)) $bodyArray = $bodyParts.ToArray() $wc = New-Object System.Net.WebClient $wc.Headers.Add("Authorization", "token $GITEA_TOKEN") $wc.Headers.Add("Content-Type", "multipart/form-data; boundary=$boundary") $result = $wc.UploadData($uploadUrl, "POST", $bodyArray) $wc.Dispose() $json = [System.Text.Encoding]::UTF8.GetString($result) | ConvertFrom-Json $downloadUrls[$apk.BaseName] = $json.browser_download_url Write-Host " OK: $($json.browser_download_url)" -ForegroundColor Green } # Step 4: Update releases.json for Vercel Write-Host "" Write-Host "[4/4] Updating releases.json..." -ForegroundColor Yellow $tailscaleBase = "$GITEA_BASE_URL".Replace("http://100.76.7.3:3000", "https://posimai-lab.tail72e846.ts.net") $maitaApk = $apkFiles | Where-Object { $_.Name -eq "ponshu_room_consumer_maita.apk" } $eijiApk = $apkFiles | Where-Object { $_.Name -eq "ponshu_room_consumer_eiji.apk" } $relJson = @{ version = $tagName name = $relName date = $dateStr apks = @{ maita = @{ lite = @{ filename = "ponshu_room_consumer_maita.apk"; url = "$tailscaleBase/mai/ponshu-room-lite/releases/download/$tagName/ponshu_room_consumer_maita.apk"; size_mb = [math]::Round($maitaApk.Length / 1MB, 1) } } eiji = @{ lite = @{ filename = "ponshu_room_consumer_eiji.apk"; url = "$tailscaleBase/mai/ponshu-room-lite/releases/download/$tagName/ponshu_room_consumer_eiji.apk"; size_mb = [math]::Round($eijiApk.Length / 1MB, 1) } } } } | ConvertTo-Json -Depth 5 $relJsonPath = Join-Path $PSScriptRoot "web\download\releases.json" [System.IO.File]::WriteAllText($relJsonPath, $relJson, [System.Text.Encoding]::UTF8) Write-Host " OK: releases.json updated" -ForegroundColor Green # Step 5: Vercel redeploy Write-Host "" Write-Host "[5/5] Deploying to Vercel..." -ForegroundColor Yellow $vercelDir = Join-Path $PSScriptRoot "web\download" Push-Location $vercelDir try { # Vercel CLI が stderr に "Vercel CLI x.x.x" を書くため、ErrorRecord を文字列に変換して収集する $prevEap = $ErrorActionPreference $ErrorActionPreference = 'Continue' $deployLines = vercel --prod --yes --no-color 2>&1 | ForEach-Object { $_.ToString() } $ErrorActionPreference = $prevEap $deployLines | ForEach-Object { Write-Host " $_" } # ANSI エスケープコード(カーソル移動等)を除去してから Production: 行を抽出 $cleanLines = $deployLines | ForEach-Object { $_ -replace '\x1B\[[0-9;]*[A-Za-z]', '' -replace '\x1B\[[\?][0-9;]*[A-Za-z]', '' } $prodLine = $cleanLines | Where-Object { $_ -match "(?i)Production:\s+https://" } | Select-Object -First 1 $prodUrl = $null if ($prodLine -match 'https://\S+') { $prodUrl = ($Matches[0] -replace '\s.*$', '').Trim() } if ($prodUrl) { Write-Host " Setting alias ponshu-room.vercel.app -> $prodUrl ..." -ForegroundColor Gray $prevEap2 = $ErrorActionPreference $ErrorActionPreference = 'Continue' vercel alias set $prodUrl ponshu-room.vercel.app 2>&1 | ForEach-Object { Write-Host " $_" } $ErrorActionPreference = $prevEap2 Write-Host " OK: Alias set" -ForegroundColor Green } else { Write-Host " [WARN] Could not extract deployment URL. Run manually:" -ForegroundColor Yellow Write-Host " vercel alias set ponshu-room.vercel.app" -ForegroundColor Yellow } } catch { Write-Host " Warning: Vercel deploy or alias failed: $_" -ForegroundColor Yellow } finally { Pop-Location } Write-Host "" Write-Host "================================================" -ForegroundColor Green Write-Host " All done!" -ForegroundColor Green Write-Host "================================================" -ForegroundColor Green Write-Host " Gitea : $GITEA_BASE_URL/$GITEA_OWNER/$GITEA_REPO/releases/tag/$tagName" Write-Host " Download : https://ponshu-room.vercel.app" Write-Host ""