사이트 인터랙티브 워크스테이션 확장 + 감사 폴리시 재적용 + 원클릭 릴리스 도구 — 병행 세션 확장분(히어로 3탭 워크스테이션 시뮬레이터, 비교 섹션(기존 방식 vs MEDIABROWSER), 가이드 단축키 섹션(kbd), 스포트라이트 카드, build/release.ps1 원클릭 릴리스(-Publish/-DeploySite), build-remote.ps1 PowerShell 원격 빌드, upload-release-asset.ps1 로컬 실행 지원(.env 자동 로드·-Publish 승격·-Repo), fetch-releases.mjs 인증·폴백)을 수용하고, 덮여 사라진 감사 폴리시를 새 베이스에 재적용: 타이틀 em대시 제거+OG 메타(index/guide), 아이브로우 6→1(장식 점 제거), 히어로 2줄 헤드라인·20단어 서브·CTA 라벨 단일화·신뢰 스트립 제거, 파이프라인 3분리 카드→단일 머신드 레일 재구성, 과장 카피 완화(100%·1초·초고속·완벽), 시뮬레이터 예시 캡션, skip-link·focus-visible·hero__grid min-width:0(모바일 오버플로) 재적용 — 데스크톱/모바일 검증(scrollWidth 375==375, 아이브로우 1, 레일·캡션·탭 3종 확인), deploy-site.ps1 로 재배포 완료

This commit is contained in:
Yun Chan 2026-08-18 10:35:53 +09:00
parent 9c9d9cb024
commit f13914534a
11 changed files with 976 additions and 246 deletions

View file

@ -1,35 +1,100 @@
# Forgejo 릴리스(draft)에 산출물을 업로드한다. 릴리스가 없으면 만들고, 동명 자산은 교체한다(재실행 멱등).
# 사용: ./build/ci/upload-release-asset.ps1 -Tag v1.0.0 -Files out/a.exe, out/b.exe
# 필요 환경: RELEASE_TOKEN + (Actions 기본 제공) GITHUB_SERVER_URL, GITHUB_REPOSITORY
# RELEASE_REPO 를 주면 해당 저장소로 업로드(공개 배포 저장소용), 없으면 현재 저장소
# RELEASE_PRERELEASE=true 면 prerelease 로 생성(리허설용)
# 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
[Parameter(Mandatory)][string[]]$Files,
[switch]$Publish,
[string]$Notes = "",
[string]$Repo = ""
)
$ErrorActionPreference = 'Stop'
if (-not $env:RELEASE_TOKEN) { throw "RELEASE_TOKEN 환경변수가 없습니다" }
$repo = if ($env:RELEASE_REPO) { $env:RELEASE_REPO } else { $env:GITHUB_REPOSITORY }
$api = "$($env:GITHUB_SERVER_URL.TrimEnd('/'))/api/v1/repos/$repo"
$hdr = @{ Authorization = "token $env:RELEASE_TOKEN" }
# .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> 단건 조회에 안 잡힐 수 있어 목록에서 tag_name 으로 찾는다
function Find-Release {
(Invoke-RestMethod -Uri "$api/releases?limit=50" -Headers $hdr) |
Where-Object tag_name -eq $Tag | Select-Object -First 1
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) {
$body = @{ tag_name = $Tag; name = $Tag; draft = $true
prerelease = ($env:RELEASE_PRERELEASE -eq 'true') } | ConvertTo-Json
try { $rel = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $hdr -ContentType 'application/json' -Body $body }
catch { $rel = Find-Release } # 병렬 잡 동시 생성 경합 대비
$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" }
Write-Host "릴리스 #$($rel.id) ($Tag)"
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 {
@ -37,5 +102,5 @@ foreach ($f in $Files) {
}
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))
Write-Host ("업로드 완료: {0} ({1:N1} MB)" -f $item.Name, ($item.Length / 1MB)) -ForegroundColor Green
}