From e34b3feee7cf44a8fa083082ae7714c71c37d012 Mon Sep 17 00:00:00 2001 From: Cooper-X-Oak Date: Fri, 1 May 2026 23:22:03 +0800 Subject: [PATCH] fix updater download acceleration --- .github/workflows/release-tauri.yml | 4 ++ docs/auto-update-download-acceleration.md | 63 +++++++++++++++++++ .../app/scripts/write-updater-manifest.mjs | 13 +++- openless-all/app/src-tauri/tauri.conf.json | 1 + .../app/src/components/AutoUpdate.tsx | 4 +- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 docs/auto-update-download-acceleration.md diff --git a/.github/workflows/release-tauri.yml b/.github/workflows/release-tauri.yml index bcf5f42d..6b8a2ba9 100644 --- a/.github/workflows/release-tauri.yml +++ b/.github/workflows/release-tauri.yml @@ -222,6 +222,7 @@ jobs: OPENLESS_UPDATE_TARGET: ${{ matrix.updater-target }} OPENLESS_UPDATE_ARCH: ${{ matrix.updater-arch }} OPENLESS_UPDATE_REPO: appergb/openless + OPENLESS_UPDATE_MIRROR_BASE_URL: https://fastgit.cc/https://github.com run: node scripts/write-updater-manifest.mjs # ── 收集产物 ── @@ -266,6 +267,7 @@ jobs: openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz.sig openless-all/app/src-tauri/target/release/bundle/latest-darwin-aarch64.json + openless-all/app/src-tauri/target/release/bundle/latest-darwin-aarch64-mirror.json if-no-files-found: error - name: Upload Windows artifacts @@ -287,6 +289,7 @@ jobs: openless-all/app/src-tauri/target/release/bundle/nsis/*.exe.sig openless-all/app/src-tauri/target/release/bundle/msi/*.msi.sig openless-all/app/src-tauri/target/release/bundle/latest-windows-x86_64.json + openless-all/app/src-tauri/target/release/bundle/latest-windows-x86_64-mirror.json if-no-files-found: error - name: Upload Linux artifacts @@ -308,6 +311,7 @@ jobs: path: | openless-all/app/src-tauri/target/release/bundle/appimage/*.AppImage.sig openless-all/app/src-tauri/target/release/bundle/latest-linux-x86_64.json + openless-all/app/src-tauri/target/release/bundle/latest-linux-x86_64-mirror.json if-no-files-found: error # ── tag 推送时,同步上传到 GitHub Release ── diff --git a/docs/auto-update-download-acceleration.md b/docs/auto-update-download-acceleration.md new file mode 100644 index 00000000..b86fd54a --- /dev/null +++ b/docs/auto-update-download-acceleration.md @@ -0,0 +1,63 @@ +# Auto Update Download Acceleration + +## Problem + +OpenLess used a single Tauri updater endpoint on GitHub Releases: + +```text +https://github.com/appergb/openless/releases/latest/download/latest-{{target}}-{{arch}}.json +``` + +The manifest also pointed installer downloads back to GitHub Releases. On networks where GitHub release assets are slow, a small updater package can take minutes to download. + +Desktop apps do not reliably inherit a user's shell proxy environment. Instead of making updater correctness depend on whether a proxy is visible to the app process, the updater should use a GitHub release acceleration URL directly. + +## Runtime Behavior + +The app does not manually probe local proxy ports. It lets the OS/process network stack do whatever it normally does, while the updater endpoint itself points at `fastgit.cc` first. This keeps the rule simple: proxy or no proxy, updater traffic should prefer the fastgit transport. + +## Fastgit Acceleration + +Release builds now publish two updater manifests per target: + +```text +latest--.json +latest---mirror.json +``` + +The client checks the mirror manifest first, then GitHub. The mirror manifest points its installer URL at: + +```text +https://fastgit.cc/https://github.com//releases/latest/download/ +``` + +The updater signature still protects the downloaded package. The mirror only changes transport; it cannot replace the signed payload without verification failing. + +## Maintainer Notes + +Set `OPENLESS_UPDATE_MIRROR_BASE_URL` in CI to change the mirror host. Keep it formatted as a prefix for GitHub URLs, for example: + +```text +https://fastgit.cc/https://github.com +``` + +If a mirror becomes unreliable, replace that environment value and the mirror endpoint in `openless-all/app/src-tauri/tauri.conf.json`. + +## Evidence + +Measured from Windows on 2026-05-01. Direct GitHub release downloads were tested with local proxy disabled to reproduce the slow path. `fastgit.cc` was tested both through the normal local proxy environment and with local proxy disabled; results vary by route, so do not treat one machine's no-proxy number as a CDN SLA. + +```text +Direct GitHub installer asset, 4.78 MB, proxy disabled: +run 1: timed out after 90.75s, 1.73 MB received +run 2: timed out after 90.06s, 2.44 MB received + +fastgit.cc installer asset, 4.78 MB, normal local proxy environment: +with protocol prefix: 3.12s / 3.63s / 3.39s +without protocol prefix: 2.92s / 2.45s / 2.87s + +fastgit.cc target-user signal: +manual browser/download usage reported completing in under 1s without enabling a proxy. +``` + +This is enough to justify a `fastgit.cc` mirror path, but not enough to treat a public mirror as permanently trusted infrastructure. `fastgit.cc` explicitly documents support for GitHub release/archive acceleration and accepts GitHub links with or without the protocol prefix. Keep the mirror configurable and re-test before each release if download performance is a release blocker. diff --git a/openless-all/app/scripts/write-updater-manifest.mjs b/openless-all/app/scripts/write-updater-manifest.mjs index 4b4e8a40..eb85a906 100755 --- a/openless-all/app/scripts/write-updater-manifest.mjs +++ b/openless-all/app/scripts/write-updater-manifest.mjs @@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url'; const target = process.env.OPENLESS_UPDATE_TARGET; const arch = process.env.OPENLESS_UPDATE_ARCH; const repo = process.env.OPENLESS_UPDATE_REPO || 'appergb/openless'; +const mirrorBaseUrl = process.env.OPENLESS_UPDATE_MIRROR_BASE_URL || 'https://fastgit.cc/https://github.com'; if (!target || !arch) { throw new Error('OPENLESS_UPDATE_TARGET and OPENLESS_UPDATE_ARCH are required'); @@ -52,12 +53,20 @@ if (!existsSync(signaturePath)) { const assetName = basename(artifact); const manifestName = `latest-${target}-${arch}.json`; +const mirrorManifestName = `latest-${target}-${arch}-mirror.json`; +const githubAssetUrl = `https://github.com/${repo}/releases/latest/download/${assetName}`; +const mirrorAssetUrl = `${mirrorBaseUrl.replace(/\/$/, '')}/${repo}/releases/latest/download/${assetName}`; const manifest = { version: packageJson.version, pub_date: new Date().toISOString(), - url: `https://github.com/${repo}/releases/latest/download/${assetName}`, + url: githubAssetUrl, signature: readFileSync(signaturePath, 'utf8').trim(), }; +const mirrorManifest = { + ...manifest, + url: mirrorAssetUrl, +}; writeFileSync(join(bundleDir, manifestName), `${JSON.stringify(manifest, null, 2)}\n`); -console.log(`Wrote ${manifestName} for ${assetName}`); +writeFileSync(join(bundleDir, mirrorManifestName), `${JSON.stringify(mirrorManifest, null, 2)}\n`); +console.log(`Wrote ${manifestName} and ${mirrorManifestName} for ${assetName}`); diff --git a/openless-all/app/src-tauri/tauri.conf.json b/openless-all/app/src-tauri/tauri.conf.json index 8dd3aa78..c30bd852 100644 --- a/openless-all/app/src-tauri/tauri.conf.json +++ b/openless-all/app/src-tauri/tauri.conf.json @@ -88,6 +88,7 @@ "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFERUFBODAzNTY0QzMyM0YKUldRL01reFdBNmpxSGE1K0JadlpONXNWTzhJcGZCRGxjUVdIWExNNFJpeUNsSGZwazdlQThhemkK", "endpoints": [ + "https://fastgit.cc/https://github.com/appergb/openless/releases/latest/download/latest-{{target}}-{{arch}}-mirror.json", "https://github.com/appergb/openless/releases/latest/download/latest-{{target}}-{{arch}}.json" ] } diff --git a/openless-all/app/src/components/AutoUpdate.tsx b/openless-all/app/src/components/AutoUpdate.tsx index 92e11a3d..167103a1 100644 --- a/openless-all/app/src/components/AutoUpdate.tsx +++ b/openless-all/app/src/components/AutoUpdate.tsx @@ -7,6 +7,8 @@ import { useTranslation } from 'react-i18next'; import { isTauri, restartApp } from '../lib/ipc'; import { Btn } from '../pages/_atoms'; +const UPDATE_CHECK_TIMEOUT_MS = 15_000; + export type UpdateStatus = | 'idle' | 'checking' @@ -78,7 +80,7 @@ export function useAutoUpdate(): UseAutoUpdate { return; } const { check } = await import('@tauri-apps/plugin-updater'); - const next = await check(); + const next = await check({ timeout: UPDATE_CHECK_TIMEOUT_MS }); updateRef.current = next; setVersion(next?.version ?? ''); setStatus(next ? 'available' : 'none');