fix(updater): publish stable release feed fallback
Some checks failed
Desktop Release / Bootstrap updater signing key (push) Has been cancelled
Desktop Release / Windows x64 NSIS (push) Has been cancelled
Desktop Release / Linux x64 DEB + AppImage (push) Has been cancelled
Desktop Release / Publish public desktop release (push) Has been cancelled

This commit is contained in:
Admin 2026-09-24 01:38:48 +00:00
parent 9e65bcafa3
commit d6e2a8483c
8 changed files with 132 additions and 18 deletions

View File

@ -245,6 +245,23 @@ jobs:
} }
}' > release-assets/latest.json }' > release-assets/latest.json
windows_sha="$(sha256sum release-assets/fieldlens-print-windows-x64-setup.exe | awk '{print $1}')"
linux_deb_sha="$(sha256sum release-assets/fieldlens-print-linux-x64.deb | awk '{print $1}')"
jq -n \
--arg version "$version" \
--arg windows_sha "$windows_sha" \
--arg linux_deb_sha "$linux_deb_sha" \
--arg windows_url "https://fieldlens.arrowstack.dev/downloads/fieldlens-print-windows-x64-setup.exe" \
--arg linux_deb_url "https://fieldlens.arrowstack.dev/downloads/fieldlens-print-linux-x64.deb" \
'{
version: $version,
notes: "FieldLens Print desktop update",
downloads: {
"windows-x86_64": {url:$windows_url, sha256:$windows_sha},
"linux-x86_64-deb": {url:$linux_deb_url, sha256:$linux_deb_sha}
}
}' > release-assets/release.json
- name: Update stable public downloads - name: Update stable public downloads
shell: bash shell: bash
run: | run: |
@ -257,6 +274,7 @@ jobs:
cp release-assets/fieldlens-print-linux-x64.AppImage downloads/ cp release-assets/fieldlens-print-linux-x64.AppImage downloads/
cp release-assets/fieldlens-print-linux-x64.AppImage.sig downloads/ cp release-assets/fieldlens-print-linux-x64.AppImage.sig downloads/
cp release-assets/latest.json downloads/latest.json cp release-assets/latest.json downloads/latest.json
cp release-assets/release.json downloads/release.json
deb_sha="$(sha256sum downloads/fieldlens-print-linux-x64.deb | awk '{print $1}')" deb_sha="$(sha256sum downloads/fieldlens-print-linux-x64.deb | awk '{print $1}')"
sed -E "s/EXPECTED_SHA256=\"[0-9a-f]{64}\"/EXPECTED_SHA256=\"$deb_sha\"/" downloads/install.sh > /tmp/install.sh sed -E "s/EXPECTED_SHA256=\"[0-9a-f]{64}\"/EXPECTED_SHA256=\"$deb_sha\"/" downloads/install.sh > /tmp/install.sh
@ -322,7 +340,8 @@ jobs:
release-assets/fieldlens-print-linux-x64.deb \ release-assets/fieldlens-print-linux-x64.deb \
release-assets/fieldlens-print-linux-x64.AppImage \ release-assets/fieldlens-print-linux-x64.AppImage \
release-assets/fieldlens-print-linux-x64.AppImage.sig \ release-assets/fieldlens-print-linux-x64.AppImage.sig \
release-assets/latest.json release-assets/latest.json \
release-assets/release.json
do do
name="$(basename "$file")" name="$(basename "$file")"
existing_id="$(jq -r --arg name "$name" '.[] | select(.name == $name) | .id' /tmp/assets.json | head -n 1)" existing_id="$(jq -r --arg name "$name" '.[] | select(.name == $name) | .id' /tmp/assets.json | head -n 1)"

View File

@ -32,7 +32,10 @@ RUN set -eux; \
deb="$(find src-tauri/target/release/bundle/deb -maxdepth 1 -type f -name '*.deb' -print -quit)"; \ deb="$(find src-tauri/target/release/bundle/deb -maxdepth 1 -type f -name '*.deb' -print -quit)"; \
test -n "$deb"; \ test -n "$deb"; \
cp "$deb" /desktop-artifacts/fieldlens-print-linux-x64.deb; \ cp "$deb" /desktop-artifacts/fieldlens-print-linux-x64.deb; \
sha256sum /desktop-artifacts/fieldlens-print-linux-x64.deb > /desktop-artifacts/SHA256SUMS deb_sha="$(sha256sum /desktop-artifacts/fieldlens-print-linux-x64.deb | awk '{print $1}')"; \
sha256sum /desktop-artifacts/fieldlens-print-linux-x64.deb > /desktop-artifacts/SHA256SUMS; \
version="$(node -p "require('./package.json').version")"; \
printf '{\n "version": "%s",\n "notes": "FieldLens Print desktop update",\n "downloads": {\n "linux-x86_64-deb": {\n "url": "https://fieldlens.arrowstack.dev/downloads/fieldlens-print-linux-x64.deb",\n "sha256": "%s"\n }\n }\n}\n' "$version" "$deb_sha" > /desktop-artifacts/release.json
FROM nginx:1.27-alpine FROM nginx:1.27-alpine
COPY --from=build /desktop-artifacts /usr/share/nginx/html COPY --from=build /desktop-artifacts /usr/share/nginx/html

View File

@ -10,8 +10,13 @@ A push to `main` builds:
- `fieldlens-print-windows-x64-setup.exe` - `fieldlens-print-windows-x64-setup.exe`
- `fieldlens-print-linux-x64.deb` - `fieldlens-print-linux-x64.deb`
- signed updater artifacts when the persistent Tauri signing key is available
- `downloads/latest.json` for the signed Tauri updater
- `downloads/release.json` as the stable installer/version feed
The workflow publishes them to the Gitea release matching the version in `package.json`. The workflow publishes them to the Gitea release matching the version in `package.json`.
The desktop app uses the signed Tauri updater where supported and falls back to
`release.json` for installer-based upgrades such as Debian packages.
## Local build ## Local build

14
downloads/release.json Normal file
View File

@ -0,0 +1,14 @@
{
"version": "0.1.4",
"notes": "FieldLens Print desktop update",
"downloads": {
"windows-x86_64": {
"url": "https://fieldlens.arrowstack.dev/downloads/fieldlens-print-windows-x64-setup.exe",
"sha256": "3ac9ddb57f44a0078f3e7b8739754901ff22ffd4f217a696910db4fbcd1343eb"
},
"linux-x86_64-deb": {
"url": "https://fieldlens.arrowstack.dev/downloads/fieldlens-print-linux-x64.deb",
"sha256": "PENDING_REBUILD"
}
}
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@fieldlens/print", "name": "@fieldlens/print",
"version": "0.1.3", "version": "0.1.4",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "fieldlens-print" name = "fieldlens-print"
version = "0.1.3" version = "0.1.4"
description = "FieldLens print-stream barcode capture companion" description = "FieldLens print-stream barcode capture companion"
authors = ["Arrowstack"] authors = ["Arrowstack"]
edition = "2021" edition = "2021"

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "FieldLens Print", "productName": "FieldLens Print",
"version": "0.1.3", "version": "0.1.4",
"identifier": "dev.arrowstack.fieldlens.print", "identifier": "dev.arrowstack.fieldlens.print",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",

View File

@ -64,14 +64,31 @@ type LoginStart = {
interval: number interval: number
} }
type ManualReleaseDownload = {
url: string
sha256?: string
}
type ManualRelease = {
version: string
notes?: string
downloads: Record<string, ManualReleaseDownload>
}
type ManualUpdate = {
version: string
url: string
}
const app = document.querySelector<HTMLDivElement>('#app')! const app = document.querySelector<HTMLDivElement>('#app')!
let config: AppConfig let config: AppConfig
let jobs: Job[] = [] let jobs: Job[] = []
let status: Status let status: Status
let loginTimer: number | undefined let loginTimer: number | undefined
let currentVersion = '0.1.3' let currentVersion = '0.1.4'
let bundleType: BundleType | null = null let bundleType: BundleType | null = null
let availableUpdate: Update | null = null let availableUpdate: Update | null = null
let availableManualUpdate: ManualUpdate | null = null
let updateBusy = false let updateBusy = false
let systemPrinters: SystemPrinter[] = [] let systemPrinters: SystemPrinter[] = []
let virtualPrinterStatus: VirtualPrinterStatus | null = null let virtualPrinterStatus: VirtualPrinterStatus | null = null
@ -404,18 +421,38 @@ async function checkForUpdates(manual = false) {
try { try {
checkButton.disabled = true checkButton.disabled = true
state.textContent = manual ? 'Checking for updates…' : 'Checking quietly for updates…' state.textContent = manual ? 'Checking for updates…' : 'Checking quietly for updates…'
availableUpdate = null
availableManualUpdate = null
if (bundleType !== BundleType.Deb) {
try {
const update = await check() const update = await check()
availableUpdate = update availableUpdate = update
if (!update) { if (update) {
installButton.hidden = true state.textContent = `Version ${update.version} is available.`
state.textContent = `FieldLens Print ${currentVersion} is up to date.` installButton.hidden = false
installButton.textContent = 'Download & restart'
return
}
} catch {
// The signed updater feed may not exist yet on a freshly bootstrapped
// install. Fall back to the stable installer feed below.
}
}
const manualUpdate = await checkManualRelease()
availableManualUpdate = manualUpdate
if (manualUpdate) {
state.textContent = `Version ${manualUpdate.version} is available.`
installButton.hidden = false
installButton.textContent = 'Open installer'
return return
} }
state.textContent = `Version ${update.version} is available.` installButton.hidden = true
installButton.hidden = false state.textContent = `FieldLens Print ${currentVersion} is up to date.`
installButton.textContent = bundleType === BundleType.Deb ? 'Open Linux installer' : 'Download & restart'
} catch (error) { } catch (error) {
if (manual) state.textContent = `Update check failed: ${String(error)}` if (manual) state.textContent = `Update check failed: ${String(error)}`
else state.textContent = 'Automatic update check unavailable. You can retry manually.' else state.textContent = 'Automatic update check unavailable. You can retry manually.'
@ -425,16 +462,16 @@ async function checkForUpdates(manual = false) {
} }
async function installAvailableUpdate() { async function installAvailableUpdate() {
if (!availableUpdate || updateBusy) return if ((!availableUpdate && !availableManualUpdate) || updateBusy) return
const state = el('update-state') const state = el('update-state')
const button = el<HTMLButtonElement>('install-update') const button = el<HTMLButtonElement>('install-update')
const progress = el<HTMLDivElement>('update-progress') const progress = el<HTMLDivElement>('update-progress')
const bar = progress.querySelector('span') as HTMLSpanElement const bar = progress.querySelector('span') as HTMLSpanElement
if (bundleType === BundleType.Deb) { if (availableManualUpdate) {
state.textContent = 'Debian packages update through the Linux installer.' state.textContent = 'Opening the current FieldLens Print installer…'
await openUrl('https://fieldlens.arrowstack.dev/downloads') await openUrl(availableManualUpdate.url)
return return
} }
@ -479,6 +516,42 @@ async function installAvailableUpdate() {
} }
} }
function compareVersions(left: string, right: string) {
const a = left.split('.').map((part) => Number.parseInt(part, 10) || 0)
const b = right.split('.').map((part) => Number.parseInt(part, 10) || 0)
const length = Math.max(a.length, b.length)
for (let index = 0; index < length; index += 1) {
const diff = (a[index] ?? 0) - (b[index] ?? 0)
if (diff !== 0) return diff
}
return 0
}
async function checkManualRelease(): Promise<ManualUpdate | null> {
const response = await fetch(
'https://git.arrowstack.dev/Admin/fieldlens-desktop/raw/branch/main/downloads/release.json',
{ cache: 'no-store' },
)
if (!response.ok) throw new Error(`release feed returned HTTP ${response.status}`)
const release = await response.json() as ManualRelease
if (!release.version || compareVersions(release.version, currentVersion) <= 0) return null
const key = bundleType === BundleType.Deb
? 'linux-x86_64-deb'
: navigator.userAgent.includes('Windows')
? 'windows-x86_64'
: null
if (!key) return null
const download = release.downloads?.[key]
if (!download?.url) return null
return { version: release.version, url: download.url }
}
el('check-update').addEventListener('click', () => { void checkForUpdates(true) }) el('check-update').addEventListener('click', () => { void checkForUpdates(true) })
el('install-update').addEventListener('click', () => { void installAvailableUpdate() }) el('install-update').addEventListener('click', () => { void installAvailableUpdate() })