Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ece48fe53d | |||
| 744ead383d |
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
- Релизы собираются **локально** (CI отключён).
|
- Релизы собираются **локально** (CI отключён).
|
||||||
- Артефакты кладутся в `D:\TTRPG-Release\` (или копируются из `release\` после сборки).
|
- Артефакты кладутся в `D:\TTRPG-Release\` (или копируются из `release\` после сборки).
|
||||||
- Публикация на VPS: `powershell -ExecutionPolicy Bypass -File scripts\publish-to-updates.ps1`
|
- Подготовка релиза: **`D:\TTRPG-Release\prepare-release.cmd`** (версия, git, сборка Win/Linux, копирование в папку).
|
||||||
|
- Публикация на VPS: **`D:\TTRPG-Release\publish.cmd`** (проверка + `scp`). Копия в репо: `scripts/ttrpg-release/`
|
||||||
- **Имена файлов без версии** — версия только в `latest*.yml`.
|
- **Имена файлов без версии** — версия только в `latest*.yml`.
|
||||||
|
|
||||||
## Feed URL
|
## Feed URL
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "TTRPGPlayer",
|
"name": "TTRPGPlayer",
|
||||||
"version": "1.0.15",
|
"version": "1.0.17",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "TTRPGPlayer",
|
"name": "TTRPGPlayer",
|
||||||
"version": "1.0.15",
|
"version": "1.0.17",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+5
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TTRPGPlayer",
|
"name": "TTRPGPlayer",
|
||||||
"version": "1.0.16",
|
"version": "1.0.17",
|
||||||
"description": "TTRPG Player — редактор и проигрыватель НРИ",
|
"description": "TTRPG Player — редактор и проигрыватель НРИ",
|
||||||
"main": "dist/main/index.cjs",
|
"main": "dist/main/index.cjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -20,7 +20,10 @@
|
|||||||
"pack:mac": "npm run build && electron-builder --mac",
|
"pack:mac": "npm run build && electron-builder --mac",
|
||||||
"pack:win": "npm run build && node scripts/release-win-prep.mjs && electron-builder --win",
|
"pack:win": "npm run build && node scripts/release-win-prep.mjs && electron-builder --win",
|
||||||
"pack:linux": "node scripts/release-linux-pack.mjs",
|
"pack:linux": "node scripts/release-linux-pack.mjs",
|
||||||
"publish:updates": "powershell -ExecutionPolicy Bypass -File scripts/publish-to-updates.ps1"
|
"release": "powershell -ExecutionPolicy Bypass -File scripts/ttrpg-release/release.ps1",
|
||||||
|
"release:all": "powershell -ExecutionPolicy Bypass -File scripts/ttrpg-release/release-all.ps1",
|
||||||
|
"prepare:release": "powershell -ExecutionPolicy Bypass -File scripts/ttrpg-release/prepare-release.ps1",
|
||||||
|
"publish:updates": "powershell -ExecutionPolicy Bypass -File scripts/ttrpg-release/publish.ps1"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
# Заливка релиза на https://updates.mailib.ru/ (nginx root: /var/www/updates_mailib_ru)
|
|
||||||
# Использование:
|
|
||||||
# 1) Положите артефакты в $LocalDir (или соберите pack:win и скопируйте из release\)
|
|
||||||
# 2) powershell -ExecutionPolicy Bypass -File scripts\publish-to-updates.ps1
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
$Key = "$env:USERPROFILE\.ssh\ttrpg_updates_root"
|
|
||||||
$LocalDir = 'D:\TTRPG-Release'
|
|
||||||
$SshTarget = 'root@185.173.94.234'
|
|
||||||
$RemoteDir = '/var/www/updates_mailib_ru'
|
|
||||||
|
|
||||||
if (-not (Test-Path $Key)) {
|
|
||||||
Write-Error "SSH key not found: $Key"
|
|
||||||
}
|
|
||||||
if (-not (Test-Path $LocalDir)) {
|
|
||||||
Write-Error "Local release folder not found: $LocalDir"
|
|
||||||
}
|
|
||||||
|
|
||||||
$patterns = @(
|
|
||||||
'latest.yml',
|
|
||||||
'latest-linux*.yml',
|
|
||||||
'latest-mac.yml',
|
|
||||||
'TTRPGPlayer-Setup.exe',
|
|
||||||
'TTRPGPlayer-Setup.exe.blockmap',
|
|
||||||
'TTRPGPlayer-x64.AppImage',
|
|
||||||
'TTRPGPlayer-x86_64.AppImage',
|
|
||||||
'TTRPGPlayer-arm64.AppImage',
|
|
||||||
'TTRPGPlayer-x64.dmg',
|
|
||||||
'TTRPGPlayer-arm64.dmg'
|
|
||||||
)
|
|
||||||
|
|
||||||
$files = @()
|
|
||||||
foreach ($pat in $patterns) {
|
|
||||||
$files += Get-ChildItem -Path $LocalDir -Filter $pat -File -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
$files = $files | Sort-Object -Property Name -Unique
|
|
||||||
|
|
||||||
if (-not $files) {
|
|
||||||
Write-Error "No release files matched in $LocalDir"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Uploading to ${SshTarget}:${RemoteDir}"
|
|
||||||
foreach ($f in $files) {
|
|
||||||
Write-Host " -> $($f.Name)"
|
|
||||||
& scp -i $Key $f.FullName "${SshTarget}:${RemoteDir}/"
|
|
||||||
}
|
|
||||||
|
|
||||||
& ssh -i $Key $SshTarget "chown -R www-data:www-data $RemoteDir && ls -la $RemoteDir"
|
|
||||||
|
|
||||||
Write-Host ''
|
|
||||||
Write-Host 'Verify:'
|
|
||||||
Write-Host ' curl https://updates.mailib.ru/latest.yml'
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Copy publish.ps1, publish.cmd, publish-config.json to D:\TTRPG-Release\
|
||||||
|
when updating the release publisher tool.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0prepare-release.ps1" %*
|
||||||
|
if errorlevel 1 pause
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
# Prepare TTRPG release: bump version, git push, build Win/Linux, copy to release folder.
|
||||||
|
# Run: prepare-release.cmd
|
||||||
|
# Options:
|
||||||
|
# -Version 1.0.17 explicit version (skip auto bump)
|
||||||
|
# -Patch bump patch (default)
|
||||||
|
# -Minor bump minor
|
||||||
|
# -SkipGit skip commit/push
|
||||||
|
# -SkipLinux skip Linux build (WSL)
|
||||||
|
# -NoBump do not change package.json version
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Version = '',
|
||||||
|
[switch]$Patch,
|
||||||
|
[switch]$Minor,
|
||||||
|
[switch]$SkipGit,
|
||||||
|
[switch]$SkipLinux,
|
||||||
|
[switch]$NoBump
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ToolDir = $PSScriptRoot
|
||||||
|
$ConfigPath = Join-Path $ToolDir 'release-config.json'
|
||||||
|
|
||||||
|
function Write-Step([int]$n, [string]$text) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host "----- Step $n : $text -----" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Ok([string]$text) {
|
||||||
|
Write-Host " [OK] $text" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Fail([string]$text) {
|
||||||
|
Write-Host " [!!] $text" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Npm {
|
||||||
|
param(
|
||||||
|
[string]$Label,
|
||||||
|
[string[]]$NpmArgs,
|
||||||
|
[string]$WorkingDirectory
|
||||||
|
)
|
||||||
|
Write-Host " > $Label"
|
||||||
|
Push-Location $WorkingDirectory
|
||||||
|
try {
|
||||||
|
& npm @NpmArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "$Label failed (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Convert-ToWslPath([string]$winPath) {
|
||||||
|
$full = [System.IO.Path]::GetFullPath($winPath)
|
||||||
|
$p = $full -replace '\\', '/'
|
||||||
|
if ($p -match '^([A-Za-z]):(.*)$') {
|
||||||
|
$drive = $Matches[1].ToLower()
|
||||||
|
return "/mnt/$drive$($Matches[2])"
|
||||||
|
}
|
||||||
|
return $p
|
||||||
|
}
|
||||||
|
|
||||||
|
function Read-PackageVersion([string]$packageJsonPath) {
|
||||||
|
$json = Get-Content -LiteralPath $packageJsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||||
|
return [string]$json.version
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-NpmVersion {
|
||||||
|
param(
|
||||||
|
[string]$ProjectRoot,
|
||||||
|
[string]$Spec
|
||||||
|
)
|
||||||
|
Invoke-Npm "npm version $Spec" @('version', $Spec, '--no-git-tag-version', '--allow-same-version') $ProjectRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-GiteaPushUrl([string]$mcpConfigPath) {
|
||||||
|
if (-not (Test-Path -LiteralPath $mcpConfigPath)) {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
$raw = Get-Content -LiteralPath $mcpConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||||
|
$token = $raw.mcpServers.'gitea-mailib'.env.GITEA_ACCESS_TOKEN
|
||||||
|
if (-not $token) {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
return "https://ifontosh:${token}@git.mailib.ru/ifontosh/DndGamePlayer.git"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Git {
|
||||||
|
param(
|
||||||
|
[string]$ProjectRoot,
|
||||||
|
[string[]]$GitArgs
|
||||||
|
)
|
||||||
|
& git -C $ProjectRoot @GitArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "git $($GitArgs -join ' ') failed (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Push-Git([string]$ProjectRoot, [string]$Remote, [string]$McpConfigPath) {
|
||||||
|
$branch = (git -C $ProjectRoot rev-parse --abbrev-ref HEAD).Trim()
|
||||||
|
Write-Host " > git push $Remote $branch"
|
||||||
|
& git -C $ProjectRoot push $Remote $branch 2>&1 | ForEach-Object { Write-Host $_ }
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$pushUrl = Get-GiteaPushUrl $McpConfigPath
|
||||||
|
if (-not $pushUrl) {
|
||||||
|
throw "git push failed and no Gitea token in mcp config"
|
||||||
|
}
|
||||||
|
Write-Host " > git push via Gitea token URL"
|
||||||
|
& git -C $ProjectRoot push $pushUrl "HEAD:${branch}" 2>&1 | ForEach-Object { Write-Host $_ }
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "git push failed (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-ReleaseArtifacts {
|
||||||
|
param(
|
||||||
|
[string]$BuildReleaseDir,
|
||||||
|
[string]$TargetDir
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $TargetDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $TargetDir | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
||||||
|
|
||||||
|
$fixed = @(
|
||||||
|
'latest.yml',
|
||||||
|
'TTRPGPlayer-Setup.exe',
|
||||||
|
'TTRPGPlayer-Setup.exe.blockmap'
|
||||||
|
)
|
||||||
|
foreach ($n in $fixed) {
|
||||||
|
[void]$names.Add($n)
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($yml in Get-ChildItem -LiteralPath $BuildReleaseDir -Filter 'latest-linux*.yml' -File -ErrorAction SilentlyContinue) {
|
||||||
|
[void]$names.Add($yml.Name)
|
||||||
|
$content = Get-Content -LiteralPath $yml.FullName -Raw -Encoding UTF8
|
||||||
|
foreach ($m in [regex]::Matches($content, '(?m)^(?:\s*-\s*)?(?:url|path):\s*(\S+)\s*$')) {
|
||||||
|
[void]$names.Add($m.Groups[1].Value.Trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($img in Get-ChildItem -LiteralPath $BuildReleaseDir -Filter 'TTRPGPlayer-*.AppImage' -File -ErrorAction SilentlyContinue) {
|
||||||
|
[void]$names.Add($img.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
$copied = 0
|
||||||
|
foreach ($name in ($names | Sort-Object)) {
|
||||||
|
$src = Join-Path $BuildReleaseDir $name
|
||||||
|
if (-not (Test-Path -LiteralPath $src)) {
|
||||||
|
if ($name -match 'x64' -and $name -notmatch 'x86_64') {
|
||||||
|
$alt = $name -replace 'x64', 'x86_64'
|
||||||
|
$src = Join-Path $BuildReleaseDir $alt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $src)) {
|
||||||
|
Write-Host " [--] skip (not built): $name" -ForegroundColor Yellow
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$dest = Join-Path $TargetDir ([System.IO.Path]::GetFileName($src))
|
||||||
|
Copy-Item -LiteralPath $src -Destination $dest -Force
|
||||||
|
Write-Ok "copied $([System.IO.Path]::GetFileName($src))"
|
||||||
|
$copied += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($copied -eq 0) {
|
||||||
|
throw 'No release artifacts copied - check build output in project release folder'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $ConfigPath)) {
|
||||||
|
throw "Missing release-config.json: $ConfigPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = Get-Content -LiteralPath $ConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||||
|
$projectRoot = [System.IO.Path]::GetFullPath([string]$config.projectRoot)
|
||||||
|
$releaseDir = [System.IO.Path]::GetFullPath([string]$config.releaseDir)
|
||||||
|
$gitRemote = [string]$config.gitRemote
|
||||||
|
$mcpConfig = [string]$config.giteaMcpConfig
|
||||||
|
$wslDistro = [string]$config.wslDistro
|
||||||
|
|
||||||
|
$packageJson = Join-Path $projectRoot 'package.json'
|
||||||
|
$buildReleaseDir = Join-Path $projectRoot 'release'
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $packageJson)) {
|
||||||
|
throw "package.json not found: $packageJson"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host '=== TTRPG Prepare Release ===' -ForegroundColor Cyan
|
||||||
|
Write-Host "Project: $projectRoot"
|
||||||
|
Write-Host "Release folder: $releaseDir"
|
||||||
|
|
||||||
|
# Step 1 - version
|
||||||
|
Write-Step 1 'Bump version'
|
||||||
|
$currentVersion = Read-PackageVersion $packageJson
|
||||||
|
$newVersion = $currentVersion
|
||||||
|
|
||||||
|
if ($NoBump) {
|
||||||
|
Write-Ok "version unchanged: $currentVersion"
|
||||||
|
$newVersion = $currentVersion
|
||||||
|
} elseif ($Version) {
|
||||||
|
Invoke-NpmVersion $projectRoot $Version.Trim()
|
||||||
|
$newVersion = Read-PackageVersion $packageJson
|
||||||
|
Write-Ok "version set to $newVersion (was $currentVersion)"
|
||||||
|
} elseif ($Minor) {
|
||||||
|
Invoke-NpmVersion $projectRoot 'minor'
|
||||||
|
$newVersion = Read-PackageVersion $packageJson
|
||||||
|
Write-Ok "version $currentVersion -> $newVersion (minor)"
|
||||||
|
} else {
|
||||||
|
Invoke-NpmVersion $projectRoot 'patch'
|
||||||
|
$newVersion = Read-PackageVersion $packageJson
|
||||||
|
Write-Ok "version $currentVersion -> $newVersion (patch)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 2 - git
|
||||||
|
if ($SkipGit) {
|
||||||
|
Write-Step 2 'Git commit and push (skipped)'
|
||||||
|
} else {
|
||||||
|
Write-Step 2 'Git commit and push'
|
||||||
|
Invoke-Git $projectRoot @('add', 'package.json')
|
||||||
|
if (Test-Path -LiteralPath (Join-Path $projectRoot 'package-lock.json')) {
|
||||||
|
Invoke-Git $projectRoot @('add', 'package-lock.json')
|
||||||
|
}
|
||||||
|
$commitMsg = "chore: release v$newVersion"
|
||||||
|
$status = (git -C $projectRoot status --porcelain).Trim()
|
||||||
|
if ($status) {
|
||||||
|
Invoke-Git $projectRoot @('commit', '-m', $commitMsg)
|
||||||
|
Write-Ok "committed: $commitMsg"
|
||||||
|
} else {
|
||||||
|
Write-Ok 'nothing to commit (version may already be committed)'
|
||||||
|
}
|
||||||
|
Push-Git $projectRoot $gitRemote $mcpConfig
|
||||||
|
Write-Ok 'pushed to remote'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 3 - Windows build
|
||||||
|
Write-Step 3 'Build Windows (npm ci + pack:win)'
|
||||||
|
Invoke-Npm 'npm ci' @('ci') $projectRoot
|
||||||
|
Invoke-Npm 'npm run pack:win' @('run', 'pack:win') $projectRoot
|
||||||
|
Write-Ok 'Windows build finished'
|
||||||
|
|
||||||
|
# Step 4 - Linux build
|
||||||
|
if ($SkipLinux) {
|
||||||
|
Write-Step 4 'Build Linux (skipped)'
|
||||||
|
} else {
|
||||||
|
Write-Step 4 'Build Linux via WSL (npm ci + pack:linux)'
|
||||||
|
$wslProject = Convert-ToWslPath $projectRoot
|
||||||
|
$wslCmd = "cd '$wslProject' && npm ci && npm run pack:linux"
|
||||||
|
$wslArgs = @()
|
||||||
|
if ($wslDistro) {
|
||||||
|
$wslArgs += '-d', $wslDistro
|
||||||
|
}
|
||||||
|
$wslArgs += '-e', 'bash', '-lc', $wslCmd
|
||||||
|
Write-Host " > wsl $($wslArgs -join ' ')"
|
||||||
|
& wsl @wslArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "WSL Linux build failed (exit $LASTEXITCODE). Install WSL or use -SkipLinux"
|
||||||
|
}
|
||||||
|
Write-Ok 'Linux build finished'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 5 - copy
|
||||||
|
Write-Step 5 'Copy artifacts to release folder'
|
||||||
|
if (-not (Test-Path -LiteralPath $buildReleaseDir)) {
|
||||||
|
throw "Build output missing: $buildReleaseDir"
|
||||||
|
}
|
||||||
|
Copy-ReleaseArtifacts $buildReleaseDir $releaseDir
|
||||||
|
Write-Ok "release folder updated: $releaseDir"
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host '=== Prepare release done ===' -ForegroundColor Green
|
||||||
|
Write-Host "Version: $newVersion"
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Next steps:' -ForegroundColor Yellow
|
||||||
|
Write-Host ' 1) Copy Mac files (latest-mac.yml, *.dmg) from Mac into release folder if needed'
|
||||||
|
Write-Host ' 2) Run publish.cmd to validate and upload to updates.mailib.ru'
|
||||||
|
Write-Host ''
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"sshKey": "%USERPROFILE%\\.ssh\\ttrpg_updates_root",
|
||||||
|
"sshTarget": "root@185.173.94.234",
|
||||||
|
"remoteDir": "/var/www/updates_mailib_ru",
|
||||||
|
"feedUrl": "https://updates.mailib.ru/"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0publish.ps1" %*
|
||||||
|
if errorlevel 1 pause
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
# Copy of D:\TTRPG-Release\publish.ps1 - keep in sync when updating the release folder tool.
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$CheckOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ReleaseDir = $PSScriptRoot
|
||||||
|
$ConfigPath = Join-Path $ReleaseDir 'publish-config.json'
|
||||||
|
|
||||||
|
function Expand-ConfigPath([string]$value) {
|
||||||
|
if ($value -match '%([^%]+)%') {
|
||||||
|
$envName = $Matches[1]
|
||||||
|
$envVal = [Environment]::GetEnvironmentVariable($envName)
|
||||||
|
if ($null -ne $envVal) {
|
||||||
|
return $value.Replace("%$envName%", $envVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $value
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Title([string]$text) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host "=== $text ===" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Ok([string]$text) {
|
||||||
|
Write-Host " [OK] $text" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Fail([string]$text) {
|
||||||
|
Write-Host " [!!] $text" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Warn([string]$text) {
|
||||||
|
Write-Host " [--] $text" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-YmlReferencedFiles([string]$ymlPath) {
|
||||||
|
$names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$content = Get-Content -LiteralPath $ymlPath -Raw -Encoding UTF8
|
||||||
|
foreach ($m in [regex]::Matches($content, '(?m)^(?:\s*-\s*)?(?:url|path):\s*(\S+)\s*$')) {
|
||||||
|
[void]$names.Add($m.Groups[1].Value.Trim())
|
||||||
|
}
|
||||||
|
return @($names)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Resolve-ReleaseFile([string]$name) {
|
||||||
|
$direct = Join-Path $ReleaseDir $name
|
||||||
|
if (Test-Path -LiteralPath $direct) {
|
||||||
|
return Get-Item -LiteralPath $direct
|
||||||
|
}
|
||||||
|
if ($name -match 'x64' -and $name -notmatch 'x86_64') {
|
||||||
|
$alt = $name -replace 'x64', 'x86_64'
|
||||||
|
$altPath = Join-Path $ReleaseDir $alt
|
||||||
|
if (Test-Path -LiteralPath $altPath) {
|
||||||
|
return Get-Item -LiteralPath $altPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($name -match 'x86_64') {
|
||||||
|
$alt = $name -replace 'x86_64', 'x64'
|
||||||
|
$altPath = Join-Path $ReleaseDir $alt
|
||||||
|
if (Test-Path -LiteralPath $altPath) {
|
||||||
|
return Get-Item -LiteralPath $altPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-FileToUploadSet {
|
||||||
|
param(
|
||||||
|
[System.Collections.Generic.HashSet[string]]$set,
|
||||||
|
[System.IO.FileInfo]$file
|
||||||
|
)
|
||||||
|
[void]$set.Add($file.FullName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'TTRPG Release Publisher'
|
||||||
|
Write-Host "Release folder: $ReleaseDir"
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $ConfigPath)) {
|
||||||
|
throw "Missing publish-config.json: $ConfigPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = Get-Content -LiteralPath $ConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||||
|
$sshKey = Expand-ConfigPath $config.sshKey
|
||||||
|
$sshTarget = [string]$config.sshTarget
|
||||||
|
$remoteDir = [string]$config.remoteDir
|
||||||
|
$feedUrl = [string]$config.feedUrl
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $sshKey)) {
|
||||||
|
throw "SSH key not found: $sshKey"
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = [System.Collections.Generic.List[string]]::new()
|
||||||
|
$warnings = [System.Collections.Generic.List[string]]::new()
|
||||||
|
$uploadFiles = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
||||||
|
|
||||||
|
Write-Title 'Windows (required)'
|
||||||
|
$winYml = Join-Path $ReleaseDir 'latest.yml'
|
||||||
|
if (-not (Test-Path -LiteralPath $winYml)) {
|
||||||
|
$errors.Add('Missing latest.yml')
|
||||||
|
} else {
|
||||||
|
Write-Ok 'latest.yml'
|
||||||
|
[void]$uploadFiles.Add($winYml)
|
||||||
|
foreach ($name in (Get-YmlReferencedFiles $winYml)) {
|
||||||
|
$file = Resolve-ReleaseFile $name
|
||||||
|
if ($null -eq $file) {
|
||||||
|
$errors.Add("Windows: missing file $name (from latest.yml)")
|
||||||
|
} else {
|
||||||
|
Write-Ok $file.Name
|
||||||
|
Add-FileToUploadSet $uploadFiles $file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$blockmap = Resolve-ReleaseFile 'TTRPGPlayer-Setup.exe.blockmap'
|
||||||
|
if ($null -eq $blockmap) {
|
||||||
|
$warnings.Add('Missing TTRPGPlayer-Setup.exe.blockmap (recommended)')
|
||||||
|
} else {
|
||||||
|
Write-Ok $blockmap.Name
|
||||||
|
Add-FileToUploadSet $uploadFiles $blockmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'Linux (if latest-linux*.yml present)'
|
||||||
|
$linuxYmls = Get-ChildItem -LiteralPath $ReleaseDir -Filter 'latest-linux*.yml' -File -ErrorAction SilentlyContinue
|
||||||
|
if ($linuxYmls.Count -eq 0) {
|
||||||
|
Write-Warn 'No latest-linux*.yml - skipping Linux'
|
||||||
|
} else {
|
||||||
|
foreach ($yml in $linuxYmls) {
|
||||||
|
Write-Ok $yml.Name
|
||||||
|
[void]$uploadFiles.Add($yml.FullName)
|
||||||
|
foreach ($name in (Get-YmlReferencedFiles $yml.FullName)) {
|
||||||
|
$file = Resolve-ReleaseFile $name
|
||||||
|
if ($null -eq $file) {
|
||||||
|
$errors.Add("Linux ($($yml.Name)): missing file $name")
|
||||||
|
} else {
|
||||||
|
if ($file.Name -ne $name) {
|
||||||
|
Write-Warn "$($yml.Name): yml expects $name, disk has $($file.Name) - will upload $($file.Name)"
|
||||||
|
} else {
|
||||||
|
Write-Ok "$($yml.Name) -> $name"
|
||||||
|
}
|
||||||
|
Add-FileToUploadSet $uploadFiles $file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'macOS (if latest-mac.yml present)'
|
||||||
|
$macYml = Join-Path $ReleaseDir 'latest-mac.yml'
|
||||||
|
if (-not (Test-Path -LiteralPath $macYml)) {
|
||||||
|
Write-Warn 'No latest-mac.yml - skipping macOS'
|
||||||
|
} else {
|
||||||
|
Write-Ok 'latest-mac.yml'
|
||||||
|
[void]$uploadFiles.Add($macYml)
|
||||||
|
foreach ($name in (Get-YmlReferencedFiles $macYml)) {
|
||||||
|
$file = Resolve-ReleaseFile $name
|
||||||
|
if ($null -eq $file) {
|
||||||
|
$errors.Add("macOS: missing file $name (from latest-mac.yml)")
|
||||||
|
} else {
|
||||||
|
Write-Ok $name
|
||||||
|
Add-FileToUploadSet $uploadFiles $file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'Summary'
|
||||||
|
foreach ($w in $warnings) {
|
||||||
|
Write-Warn $w
|
||||||
|
}
|
||||||
|
if ($errors.Count -gt 0) {
|
||||||
|
foreach ($e in $errors) {
|
||||||
|
Write-Fail $e
|
||||||
|
}
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Upload cancelled. Fix the release folder and run again.' -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host "Files to upload: $($uploadFiles.Count)" -ForegroundColor Green
|
||||||
|
foreach ($path in ($uploadFiles | Sort-Object)) {
|
||||||
|
Write-Host " - $([System.IO.Path]::GetFileName($path))"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($CheckOnly) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'CheckOnly: upload skipped.' -ForegroundColor Yellow
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'Upload'
|
||||||
|
Write-Host "Target: ${sshTarget}:${remoteDir}"
|
||||||
|
Write-Host "Feed: $feedUrl"
|
||||||
|
|
||||||
|
foreach ($path in ($uploadFiles | Sort-Object)) {
|
||||||
|
$name = [System.IO.Path]::GetFileName($path)
|
||||||
|
Write-Host " -> $name"
|
||||||
|
& scp -i $sshKey -q $path "${sshTarget}:${remoteDir}/"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "scp failed for $name (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Setting www-data ownership on server...'
|
||||||
|
& ssh -i $sshKey $sshTarget "chown -R www-data:www-data '$remoteDir'"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "ssh chown failed (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Title 'Done'
|
||||||
|
Write-Host 'Verify:'
|
||||||
|
Write-Host " ${feedUrl}latest.yml"
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0release-all.ps1" %*
|
||||||
|
if errorlevel 1 pause
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Copy of D:\TTRPG-Release\release-all.ps1
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Version = '',
|
||||||
|
[switch]$Patch,
|
||||||
|
[switch]$Minor,
|
||||||
|
[switch]$SkipGit,
|
||||||
|
[switch]$SkipLinux,
|
||||||
|
[switch]$NoBump,
|
||||||
|
[switch]$CheckOnlyPublish
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ToolDir = $PSScriptRoot
|
||||||
|
$PrepareScript = Join-Path $ToolDir 'prepare-release.ps1'
|
||||||
|
$PublishScript = Join-Path $ToolDir 'publish.ps1'
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $PrepareScript)) {
|
||||||
|
throw "Missing prepare-release.ps1: $PrepareScript"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $PublishScript)) {
|
||||||
|
throw "Missing publish.ps1: $PublishScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
$prepareArgs = @()
|
||||||
|
if ($Version) { $prepareArgs += '-Version', $Version }
|
||||||
|
if ($Patch) { $prepareArgs += '-Patch' }
|
||||||
|
if ($Minor) { $prepareArgs += '-Minor' }
|
||||||
|
if ($SkipGit) { $prepareArgs += '-SkipGit' }
|
||||||
|
if ($SkipLinux) { $prepareArgs += '-SkipLinux' }
|
||||||
|
if ($NoBump) { $prepareArgs += '-NoBump' }
|
||||||
|
|
||||||
|
$publishArgs = @()
|
||||||
|
if ($CheckOnlyPublish) { $publishArgs += '-CheckOnly' }
|
||||||
|
|
||||||
|
Write-Host '=== TTRPG Full Release ===' -ForegroundColor Cyan
|
||||||
|
Write-Host ''
|
||||||
|
|
||||||
|
Write-Host '>>> Phase 1/2: Prepare release' -ForegroundColor Yellow
|
||||||
|
& powershell -NoProfile -ExecutionPolicy Bypass -File $PrepareScript @prepareArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Prepare release failed (exit $LASTEXITCODE). Publish not started."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host '>>> Phase 2/2: Publish to server' -ForegroundColor Yellow
|
||||||
|
& powershell -NoProfile -ExecutionPolicy Bypass -File $PublishScript @publishArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Publish failed (exit $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host '=== Full release completed ===' -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"projectRoot": "D:\\Work\\my_projects\\dnd_project\\dnd_player",
|
||||||
|
"releaseDir": "D:\\TTRPG-Release",
|
||||||
|
"gitRemote": "origin",
|
||||||
|
"giteaMcpConfig": "C:\\Users\\Administrator\\.cursor\\mcp.json",
|
||||||
|
"wslDistro": ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0release.ps1" %*
|
||||||
|
if errorlevel 1 pause
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Full release: prepare (version, git, build, copy) then publish (validate + upload).
|
||||||
|
# Run: release.cmd
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Version = '',
|
||||||
|
[switch]$Minor,
|
||||||
|
[switch]$SkipGit,
|
||||||
|
[switch]$SkipLinux,
|
||||||
|
[switch]$NoBump,
|
||||||
|
[switch]$CheckOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ToolDir = $PSScriptRoot
|
||||||
|
$PrepareScript = Join-Path $ToolDir 'prepare-release.ps1'
|
||||||
|
$PublishScript = Join-Path $ToolDir 'publish.ps1'
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $PrepareScript)) {
|
||||||
|
throw "Missing prepare-release.ps1 in $ToolDir"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $PublishScript)) {
|
||||||
|
throw "Missing publish.ps1 in $ToolDir"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host '=== TTRPG Full Release ===' -ForegroundColor Cyan
|
||||||
|
Write-Host 'Step A: prepare-release'
|
||||||
|
Write-Host 'Step B: publish (after prepare succeeds)'
|
||||||
|
Write-Host ''
|
||||||
|
|
||||||
|
$prepareArgs = @('-File', $PrepareScript)
|
||||||
|
if ($Version) { $prepareArgs += '-Version', $Version }
|
||||||
|
if ($Minor) { $prepareArgs += '-Minor' }
|
||||||
|
if ($SkipGit) { $prepareArgs += '-SkipGit' }
|
||||||
|
if ($SkipLinux) { $prepareArgs += '-SkipLinux' }
|
||||||
|
if ($NoBump) { $prepareArgs += '-NoBump' }
|
||||||
|
|
||||||
|
& powershell -NoProfile -ExecutionPolicy Bypass @prepareArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Prepare failed. Publish was not started.' -ForegroundColor Red
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host 'Prepare finished. Starting publish...' -ForegroundColor Green
|
||||||
|
Write-Host ''
|
||||||
|
|
||||||
|
$publishArgs = @('-File', $PublishScript)
|
||||||
|
if ($CheckOnly) { $publishArgs += '-CheckOnly' }
|
||||||
|
|
||||||
|
& powershell -NoProfile -ExecutionPolicy Bypass @publishArgs
|
||||||
|
exit $LASTEXITCODE
|
||||||
Reference in New Issue
Block a user