Configure desktop update feed

This commit is contained in:
Курнат Андрей
2026-06-08 21:18:46 +03:00
parent e9aa9f8315
commit 955be5134d
5 changed files with 151 additions and 1 deletions
+1
View File
@@ -5,6 +5,7 @@ set( APPLICATION_VENDOR "QSfera" )
set( APPLICATION_ICON_NAME "qsfera" )
set( APPLICATION_REV_DOMAIN "eu.qsfera.desktop" )
set( APPLICATION_DEFAULT_SERVER_URL "https://qsfera.kusoft.xyz" )
set( APPLICATION_UPDATE_URL "https://qsfera.kusoft.xyz/desktop/updates/stable.xml" )
if(BETA_CHANNEL_BUILD)
set( APPLICATION_NAME "${APPLICATION_NAME} Beta")
+47
View File
@@ -0,0 +1,47 @@
# Windows Update Feed
QSfera Desktop uses the built-in Windows updater from `src/gui/updater`.
The updater reads `APPLICATION_UPDATE_URL`, appends query parameters such as
`version`, `platform=win32`, and `msi=true`, and expects an XML document with
root element `QSfera`.
Production feed URL:
```text
https://qsfera.kusoft.xyz/desktop/updates/stable.xml
```
The no-update feed is valid when no signed Windows MSI is available yet:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<QSfera>
<version></version>
<versionstring></versionstring>
<web>https://qsfera.kusoft.xyz/</web>
<downloadurl></downloadurl>
</QSfera>
```
To advertise a signed MSI update, publish the MSI over HTTPS and replace the
feed with a concrete version:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<QSfera>
<version>1.3.7</version>
<versionstring>1.3.7</versionstring>
<web>https://qsfera.kusoft.xyz/</web>
<downloadurl>https://qsfera.kusoft.xyz/desktop/downloads/QSfera_Desktop-1.3.7-x64.msi</downloadurl>
</QSfera>
```
Validate the live feed before a Windows production release:
```powershell
./scripts/validate-desktop-update-feed.ps1 -Url "https://qsfera.kusoft.xyz/desktop/updates/stable.xml"
./scripts/validate-production-release.ps1 -VerifyUpdateFeed
```
For a stable signed tag release, `validate-production-release.ps1 -RequireSigning`
also verifies this live feed.
@@ -0,0 +1,85 @@
param(
[Parameter(Mandatory = $true)]
[string]$Url
)
$ErrorActionPreference = "Stop"
function Fail($Message) {
Write-Error $Message
exit 1
}
$parsedUrl = $null
if (-not [System.Uri]::TryCreate($Url, [System.UriKind]::Absolute, [ref]$parsedUrl)) {
Fail "Desktop update feed URL is not absolute: $Url"
}
if ($parsedUrl.Scheme -ne "https") {
Fail "Desktop update feed URL must use HTTPS: $Url"
}
try {
$response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 30
} catch {
Fail "Desktop update feed request failed for ${Url}: $($_.Exception.Message)"
}
if ($response.StatusCode -ne 200) {
Fail "Desktop update feed must return HTTP 200. Actual status: $($response.StatusCode)"
}
try {
[xml]$xml = $response.Content
} catch {
Fail "Desktop update feed is not valid XML: $($_.Exception.Message)"
}
if ($xml.DocumentElement.Name -ne "QSfera") {
Fail "Desktop update feed root element must be <QSfera>."
}
$requiredElements = @("version", "versionstring", "web", "downloadurl")
foreach ($name in $requiredElements) {
if ($null -eq $xml.QSfera.$name) {
Fail "Desktop update feed is missing <$name>."
}
}
$version = [string]$xml.QSfera.version
$versionString = [string]$xml.QSfera.versionstring
$web = [string]$xml.QSfera.web
$downloadUrl = [string]$xml.QSfera.downloadurl
if (-not [string]::IsNullOrWhiteSpace($web)) {
$webUri = $null
if (-not [System.Uri]::TryCreate($web, [System.UriKind]::Absolute, [ref]$webUri) -or $webUri.Scheme -ne "https") {
Fail "Desktop update feed <web> must be an absolute HTTPS URL when present."
}
}
if ([string]::IsNullOrWhiteSpace($version)) {
if (-not [string]::IsNullOrWhiteSpace($downloadUrl)) {
Fail "Desktop no-update feed must leave <downloadurl> empty when <version> is empty."
}
} else {
$parsedVersion = $null
if (-not [System.Version]::TryParse($version, [ref]$parsedVersion)) {
Fail "Desktop update feed <version> must be parseable as a version number."
}
if ([string]::IsNullOrWhiteSpace($versionString)) {
Fail "Desktop update feed must set <versionstring> when <version> is present."
}
$downloadUri = $null
if (-not [System.Uri]::TryCreate($downloadUrl, [System.UriKind]::Absolute, [ref]$downloadUri) -or $downloadUri.Scheme -ne "https") {
Fail "Desktop update feed <downloadurl> must be an absolute HTTPS URL when an update is advertised."
}
if (-not $downloadUri.AbsolutePath.EndsWith(".msi", [System.StringComparison]::OrdinalIgnoreCase)) {
Fail "Desktop Windows updater expects an MSI download URL."
}
}
Write-Host "QSfera Desktop update feed validation passed: $Url"
@@ -1,5 +1,6 @@
param(
[switch]$RequireSigning,
[switch]$VerifyUpdateFeed,
[string]$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
)
@@ -35,6 +36,10 @@ if ($branding -notmatch 'APPLICATION_DEFAULT_SERVER_URL\s+"https://qsfera\.kusof
Fail "Desktop default server URL must be https://qsfera.kusoft.xyz."
}
if ($branding -notmatch 'APPLICATION_UPDATE_URL\s+"https://qsfera\.kusoft\.xyz/desktop/updates/stable\.xml"') {
Fail "Desktop update feed URL must be https://qsfera.kusoft.xyz/desktop/updates/stable.xml."
}
if ($cmake -notmatch 'option\(WITH_UPDATE_NOTIFICATION\s+"Whether to check for new releases"\s+ON\)') {
Fail "Desktop release builds must keep update notifications enabled by default."
}
@@ -55,6 +60,17 @@ if ($RequireSigning) {
if ([string]::IsNullOrWhiteSpace($env:CRAFT_CODESIGN_CERTIFICATE)) {
Fail "Production Windows tag releases require CRAFT_CODESIGN_CERTIFICATE."
}
$VerifyUpdateFeed = $true
}
if ($VerifyUpdateFeed) {
$feedValidator = Join-Path $PSScriptRoot "validate-desktop-update-feed.ps1"
if (-not (Test-Path -LiteralPath $feedValidator)) {
Fail "Desktop update feed validator was not found: $feedValidator"
}
& $feedValidator -Url "https://qsfera.kusoft.xyz/desktop/updates/stable.xml"
}
Write-Host "QSfera Desktop production release validation passed."
+2 -1
View File
@@ -36,6 +36,7 @@ Date: 2026-06-08.
- Server Docker builds now accept explicit production metadata for `QSFERA_VERSION`, `QSFERA_EDITION`, and `QSFERA_BUILD_DATE`, and the Raspberry Pi 5 compose path defaults to pinned image `qsfera-cloud-server:7.0.0-rpi5-stable`.
- Windows/Desktop update channel defaults now match the build channel: stable builds default to `stable`, beta builds default to `beta`.
- Windows/Desktop CI now validates production release invariants before Windows packaging and requires signing variables for stable tag releases.
- Windows/Desktop now has a configured HTTPS update feed at `https://qsfera.kusoft.xyz/desktop/updates/stable.xml`; the feed returned HTTP `200` on 2026-06-08 and contains the XML elements parsed by `Desktop/src/gui/updater/updateinfo.cpp`.
## Raspberry Pi 5 Check
@@ -65,7 +66,7 @@ Date: 2026-06-08.
- Fill production domains, ACME email, admin passwords, Keycloak passwords, and storage secrets from a secret manager. Do not commit real values.
- Run `./validate-production-env.sh .env` and then `docker compose config` for `Server/devtools/deployments/qsfera_full` with a filled production `.env`; Docker CLI is not available in this local environment, so compose rendering is not locally verified.
- Provide real Windows signing secrets in CI: `QSFERA_DESKTOP_SIGN_PACKAGE=True` and `QSFERA_DESKTOP_CODESIGN_CERTIFICATE` with the Craft-compatible signing certificate reference.
- Configure and verify the desktop updater endpoint via `APPLICATION_UPDATE_URL`; `Desktop/scripts/validate-production-release.ps1` confirms update notifications are enabled but cannot prove a live update feed without that URL.
- Publish the first signed Windows MSI and update `https://qsfera.kusoft.xyz/desktop/updates/stable.xml` from the current no-update XML to a concrete `<version>`, `<versionstring>`, and HTTPS `.msi` `<downloadurl>`.
## Remaining P1