Configure desktop update feed
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user