# Forgejo 릴리스에 산출물을 업로드한다. 릴리스가 없으면 만들고, 동명 자산은 교체한다(재실행 멱등). # 사용: ./build/ci/upload-release-asset.ps1 -Tag v1.0.0 -Files out/a.exe, out/b.exe [-Publish] param( [Parameter(Mandatory)][string]$Tag, [Parameter(Mandatory)][string[]]$Files, [switch]$Publish, [string]$Notes = "", [string]$Repo = "" ) $ErrorActionPreference = 'Stop' # .env 자동 로드 (로컬 실행 시) $root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) $envFile = Join-Path $root ".env" if (Test-Path $envFile) { foreach ($line in Get-Content $envFile) { if ($line -match '^\s*([A-Za-z0-9_]+)\s*=\s*(.+)$') { $k = $Matches[1]; $v = $Matches[2].Trim() if (-not (Get-Item "env:$k" -ErrorAction SilentlyContinue)) { Set-Item "env:$k" $v } } } } $token = if ($env:RELEASE_TOKEN) { $env:RELEASE_TOKEN } else { $env:FORGEJO_TOKEN } $hdr = @{} if ($token) { $hdr["Authorization"] = "token $token" } elseif ($env:FORGEJO_USER -and $env:FORGEJO_PASSWORD) { $bytes = [System.Text.Encoding]::UTF8.GetBytes("$($env:FORGEJO_USER):$($env:FORGEJO_PASSWORD)") $b64 = [Convert]::ToBase64String($bytes) $hdr["Authorization"] = "Basic $b64" } else { throw ".env 에 FORGEJO_TOKEN 또는 FORGEJO_USER/PASSWORD 가 없습니다." } $serverUrl = if ($env:GITHUB_SERVER_URL) { $env:GITHUB_SERVER_URL } elseif ($env:FORGEJO_URL) { $env:FORGEJO_URL } else { "https://git.chanpaca.net" } $serverUrl = $serverUrl.TrimEnd('/') $targetRepo = if ($Repo) { $Repo } elseif ($env:RELEASE_REPO) { $env:RELEASE_REPO } elseif ($env:GITHUB_REPOSITORY) { $env:GITHUB_REPOSITORY } else { "Video-Downloader/releases" } $api = "$serverUrl/api/v1/repos/$targetRepo" $isDraft = -not $Publish $isPrerelease = ($env:RELEASE_PRERELEASE -eq 'true') # draft 릴리스는 tags/ 단건 조회에 안 잡힐 수 있어 목록에서 tag_name 으로 찾는다 function Find-Release { try { (Invoke-RestMethod -Uri "$api/releases?limit=50" -Headers $hdr) | Where-Object tag_name -eq $Tag | Select-Object -First 1 } catch { return $null } } $rel = Find-Release if (-not $rel) { $bodyObj = @{ tag_name = $Tag name = $Tag draft = $isDraft prerelease = $isPrerelease } if ($Notes) { $bodyObj["body"] = $Notes } $body = $bodyObj | ConvertTo-Json try { $rel = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $hdr -ContentType 'application/json' -Body $body } catch { $rel = Find-Release # 병렬 잡 동시 생성 경합 대비 } } else { # 기존 릴리스가 있고 Publish 옵션이 주어졌는데 현재 draft 상태인 경우 Publish로 갱신 if ($Publish -and $rel.draft) { $patchBody = @{ draft = $false } | ConvertTo-Json $rel = Invoke-RestMethod -Method Patch -Uri "$api/releases/$($rel.id)" -Headers $hdr -ContentType 'application/json' -Body $patchBody Write-Host "릴리스를 정식 공개(Published) 상태로 전환했습니다." -ForegroundColor Green } } if (-not $rel) { throw "릴리스 확보 실패: $Tag (저장소: $targetRepo)" } $statusStr = if ($rel.draft) { "Draft" } else { "Published" } Write-Host "릴리스 #$($rel.id) ($Tag, 상태: $statusStr, 저장소: $targetRepo)" -ForegroundColor Cyan foreach ($f in $Files) { if (-not (Test-Path $f)) { Write-Warning "파일이 존재하지 않아 건너뜁니다: $f" continue } $item = Get-Item $f (Invoke-RestMethod -Uri "$api/releases/$($rel.id)/assets" -Headers $hdr) | Where-Object name -eq $item.Name | ForEach-Object { Invoke-RestMethod -Method Delete -Uri "$api/releases/$($rel.id)/assets/$($_.id)" -Headers $hdr | Out-Null } Invoke-RestMethod -Method Post -Uri "$api/releases/$($rel.id)/assets?name=$($item.Name)" ` -Headers $hdr -Form @{ attachment = $item } | Out-Null Write-Host ("업로드 완료: {0} ({1:N1} MB)" -f $item.Name, ($item.Length / 1MB)) -ForegroundColor Green }