diff --git a/docs/updater.md b/docs/updater.md index 5e3b70d6..9a09f585 100644 --- a/docs/updater.md +++ b/docs/updater.md @@ -126,9 +126,12 @@ bundled game, in that case. persistent launcher control. Android downloads the release APK, verifies its SHA-256 entry from `sha256sums.txt`, then invokes Android's Package Installer. The installer asks the user for consent and enforces package, - version-code, and signing-certificate compatibility. iOS links the - sideload repository for a re-sideload; Xbox, desktop, and PortMaster builds - link their correctly named full package. Switch keeps its native OTA flow. + version-code, and signing-certificate compatibility. A legacy APK without + the installer bridge links its full package for one manual bootstrap + update, including when its downloaded payload already reports the latest + engine version. iOS links the sideload repository for a re-sideload; Xbox, + desktop, and PortMaster builds link their correctly named full package. + Switch keeps its native OTA flow. ## Known limitations diff --git a/src/import/LauncherView.lua b/src/import/LauncherView.lua index 13c3eedd..2f20a15d 100644 --- a/src/import/LauncherView.lua +++ b/src/import/LauncherView.lua @@ -1320,7 +1320,7 @@ local function buildHeader(imp, m) return y + math.floor(10 * m.s) end --- The state of the self-updater, as a top-right control. +-- The state of the self-updater, shown in the launcher footer. -- Returns status, label, action, glow. function LauncherView._updateControl(imp) if not imp.Check then return nil end diff --git a/src/update/Check.lua b/src/update/Check.lua index 95f309cb..b918f2f3 100644 --- a/src/update/Check.lua +++ b/src/update/Check.lua @@ -58,6 +58,13 @@ function Check.fullAssetName(version, osName, arch, port) return fullAssetName(version, osName, arch, port) end +-- A legacy Android APK can run a newer downloaded payload while still lacking +-- the native bridge that installs future APK updates. It needs one manual +-- package update even when that payload already reports the latest engine. +function Check.androidNeedsInstallerBootstrap(osName, hasInstaller) + return osName == "Android" and not hasInstaller +end + local CMD = "update_check_cmd" local STATE = "update_check_state" @@ -266,12 +273,15 @@ function Check.fullUpdateAction() local st = Check.state() if st.status ~= "needs_full" and st.status ~= "full_ready" then return nil end local osName = love and love.system and love.system.getOS and love.system.getOS() or "" - if osName == "Android" and type(love.system.installApk) == "function" - and type(st.full) == "table" and type(st.full.url) == "string" then - if st.status == "full_ready" and type(st.full.path) == "string" then - return { label = "Install Android update", kind = "install" } + if osName == "Android" and type(st.full) == "table" + and type(st.full.url) == "string" then + if type(love.system.installApk) == "function" then + if st.status == "full_ready" and type(st.full.path) == "string" then + return { label = "Install Android update", kind = "install" } + end + return { label = "Download Android update", kind = "download" } end - return { label = "Download Android update", kind = "download" } + return { label = "Update app manually", url = st.full.url } end if osName == "iOS" then return { label = "Re-sideload app", url = diff --git a/src/update/check_worker.lua b/src/update/check_worker.lua index 81138572..896db5ab 100644 --- a/src/update/check_worker.lua +++ b/src/update/check_worker.lua @@ -243,6 +243,12 @@ local function doCheck(target) return end + if Check.androidNeedsInstallerBootstrap(osName, + type(love.system.installApk) == "function") then + postFullRequirement(rel, "native_installer_missing") + return + end + if compareVersions(rel.version, currentEngine) <= 0 then -- We are now running a native shell at least as new as GitHub's latest -- release, so a former minShell/payloadHost prompt no longer applies. diff --git a/tests/engine/update_check_tests.lua b/tests/engine/update_check_tests.lua index 96d8111f..99c2516b 100644 --- a/tests/engine/update_check_tests.lua +++ b/tests/engine/update_check_tests.lua @@ -48,6 +48,15 @@ eq(Check.fullAssetName("1.4.2", "iOS", "arm64"), eq(Check.fullAssetName("not-a-version", "Android", "arm64"), nil, "invalid full-package version rejected") +-- A legacy Android shell needs one manual package update even when its +-- downloaded payload already reports the latest engine version. +eq(Check.androidNeedsInstallerBootstrap("Android", false), true, + "legacy Android shell requires one bootstrap package") +eq(Check.androidNeedsInstallerBootstrap("Android", true), false, + "current Android shell keeps the selective updater") +eq(Check.androidNeedsInstallerBootstrap("Windows", false), false, + "non-Android update behavior remains unchanged") + local withNotes = Check.parseRelease(Json.encode({ tag_name = "v1.4.2", body = "## Issues closed\n\n- #1 cart padding",