Synchronize local branch after publishing
Some checks are pending
Validate Decky / web-client (push) Waiting to run

This commit is contained in:
Elijah 2026-07-20 09:35:51 -07:00
parent a0b46497c1
commit e7b2326521
3 changed files with 51 additions and 15 deletions

View file

@ -40,10 +40,11 @@ runner only validates the web client via `.forgejo/workflows/validate.yml`.
`artifacts/release/vX.Y.Z/` and stay out of Git. `artifacts/release/vX.Y.Z/` and stay out of Git.
- Publish by double-clicking `scripts/Publish-DeckyRelease.cmd` or by invoking - Publish by double-clicking `scripts/Publish-DeckyRelease.cmd` or by invoking
`scripts/Publish-ForgejoRelease.ps1`. The publisher creates the tag/release, `scripts/Publish-ForgejoRelease.ps1`. The publisher creates the tag/release,
uploads the installer/signature, and commits `releases/latest.json` to `main`. uploads the installer/signature, commits `releases/latest.json` to `main`, and
- Publishing creates a new remote metadata commit. Fetch/rebase or pull before automatically fast-forwards the local checkout to that metadata commit.
the next push; a non-fast-forward rejection usually means this commit is - The publisher requires a clean `main` that exactly matches `origin/main`, so
missing locally. LF-to-CRLF messages on Windows are warnings, not failures. commit and push source first. LF-to-CRLF messages on Windows are warnings,
not failures.
- Forgejo: `https://git.elijahkuntz.com/Elijah/Decky`. A publishing PAT needs - Forgejo: `https://git.elijahkuntz.com/Elijah/Decky`. A publishing PAT needs
`write:repository`; never commit or print it. `write:repository`; never commit or print it.
- The permanent Tauri updater key is outside the repository at - The permanent Tauri updater key is outside the repository at

View file

@ -73,7 +73,6 @@ current PowerShell process and run:
$env:FORGEJO_PAT = 'paste-token-for-this-session' $env:FORGEJO_PAT = 'paste-token-for-this-session'
powershell -ExecutionPolicy Bypass -File .\scripts\Publish-ForgejoRelease.ps1 -Version 0.3.0 -Notes "Board and stack management" powershell -ExecutionPolicy Bypass -File .\scripts\Publish-ForgejoRelease.ps1 -Version 0.3.0 -Notes "Board and stack management"
Remove-Item Env:FORGEJO_PAT Remove-Item Env:FORGEJO_PAT
git pull --ff-only
``` ```
For an interactive, double-clickable option, run: For an interactive, double-clickable option, run:
@ -86,11 +85,12 @@ It bypasses PowerShell's script policy only for the publisher process, prompts
for the version and a hidden personal access token, and leaves the window open for the version and a hidden personal access token, and leaves the window open
so any error can be read. The token is not saved. so any error can be read. The token is not saved.
The publisher creates the `v0.3.0` Forgejo release/tag at `main`, uploads the The publisher first verifies that the working tree is clean and local `main`
installer, signature, and metadata, then commits the tracked exactly matches `origin/main`. It then creates the `v0.3.0` Forgejo release/tag,
`releases/latest.json` through the Forgejo API. The final pull synchronizes that uploads the installer, signature, and metadata, and commits the tracked
small metadata commit locally. Existing Decky installations then discover the `releases/latest.json` through the Forgejo API. Finally, it automatically
release through Settings. fast-forwards local `main` to that metadata commit. Existing Decky installations
then discover the release through Settings.
## Forgejo Actions ## Forgejo Actions

View file

@ -12,6 +12,35 @@ param(
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$releaseDirectory = Join-Path $repositoryRoot "artifacts\release\v$Version"
$latestJsonPath = Join-Path $releaseDirectory 'latest.json'
if (-not (Test-Path -LiteralPath $latestJsonPath)) { throw "Run Prepare-Release.ps1 first: $latestJsonPath is missing." }
function Invoke-GitCommand {
param([Parameter(Mandatory)][string[]]$ArgumentList)
$output = @(& git -C $repositoryRoot @ArgumentList 2>&1)
if ($LASTEXITCODE) {
throw "git $($ArgumentList -join ' ') failed: $($output -join [Environment]::NewLine)"
}
return $output
}
$branch = ((Invoke-GitCommand @('rev-parse', '--abbrev-ref', 'HEAD')) | Select-Object -Last 1).Trim()
if ($branch -ne 'main') { throw "Releases must be published from main; the current branch is '$branch'." }
$workingChanges = @(Invoke-GitCommand @('status', '--porcelain'))
if ($workingChanges.Count) {
throw "Commit or discard all source changes before publishing:`n$($workingChanges -join [Environment]::NewLine)"
}
Invoke-GitCommand @('fetch', 'origin', 'main') | Out-Null
$localHead = ((Invoke-GitCommand @('rev-parse', 'HEAD')) | Select-Object -Last 1).Trim()
$remoteHead = ((Invoke-GitCommand @('rev-parse', 'origin/main')) | Select-Object -Last 1).Trim()
if ($localHead -ne $remoteHead) {
throw 'Local main does not exactly match origin/main. Pull/rebase or push the source before publishing.'
}
if (-not $Token) { if (-not $Token) {
Write-Host 'A Forgejo personal access token is required to create the release.' -ForegroundColor Cyan Write-Host 'A Forgejo personal access token is required to create the release.' -ForegroundColor Cyan
Write-Host 'The token is used only by this process and is not saved by Decky.' -ForegroundColor DarkGray Write-Host 'The token is used only by this process and is not saved by Decky.' -ForegroundColor DarkGray
@ -20,11 +49,6 @@ if (-not $Token) {
} }
if (-not $Token) { throw 'No Forgejo personal access token was entered.' } if (-not $Token) { throw 'No Forgejo personal access token was entered.' }
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$releaseDirectory = Join-Path $repositoryRoot "artifacts\release\v$Version"
$latestJsonPath = Join-Path $releaseDirectory 'latest.json'
if (-not (Test-Path -LiteralPath $latestJsonPath)) { throw "Run Prepare-Release.ps1 first: $latestJsonPath is missing." }
$headers = @{ Authorization = "token $Token"; Accept = 'application/json' } $headers = @{ Authorization = "token $Token"; Accept = 'application/json' }
$apiRoot = "$($ForgejoBaseUrl.TrimEnd('/'))/api/v1/repos/$Repository" $apiRoot = "$($ForgejoBaseUrl.TrimEnd('/'))/api/v1/repos/$Repository"
$tag = "v$Version" $tag = "v$Version"
@ -82,3 +106,14 @@ $contentBody = @{
if ($sha) { $contentBody.sha = $sha } if ($sha) { $contentBody.sha = $sha }
Invoke-RestMethod -Method Put -Uri "$apiRoot/contents/releases/latest.json" -Headers $headers -ContentType 'application/json' -Body ($contentBody | ConvertTo-Json) | Out-Null Invoke-RestMethod -Method Put -Uri "$apiRoot/contents/releases/latest.json" -Headers $headers -ContentType 'application/json' -Body ($contentBody | ConvertTo-Json) | Out-Null
Write-Host "Published Decky $Version and updated releases/latest.json." Write-Host "Published Decky $Version and updated releases/latest.json."
Invoke-GitCommand @('fetch', 'origin', 'main') | Out-Null
$publishedHead = ((Invoke-GitCommand @('rev-parse', 'origin/main')) | Select-Object -Last 1).Trim()
if ($publishedHead -ne $localHead) {
$mergeBase = ((Invoke-GitCommand @('merge-base', $localHead, $publishedHead)) | Select-Object -Last 1).Trim()
if ($mergeBase -ne $localHead) {
throw 'The release was published, but origin/main also changed independently. Fetch and reconcile it manually.'
}
Invoke-GitCommand @('merge', '--ff-only', 'origin/main') | Out-Null
}
Write-Host 'Local main is synchronized with the Forgejo release metadata commit.'