fix(android): bootstrap legacy APK updater

This commit is contained in:
AverageConsumer
2026-08-21 00:55:32 +02:00
parent 087a275189
commit 8c65e76145
5 changed files with 37 additions and 9 deletions
+6 -3
View File
@@ -126,9 +126,12 @@ bundled game, in that case.
persistent launcher control. Android downloads the release APK, verifies persistent launcher control. Android downloads the release APK, verifies
its SHA-256 entry from `sha256sums.txt`, then invokes Android's Package its SHA-256 entry from `sha256sums.txt`, then invokes Android's Package
Installer. The installer asks the user for consent and enforces package, Installer. The installer asks the user for consent and enforces package,
version-code, and signing-certificate compatibility. iOS links the version-code, and signing-certificate compatibility. A legacy APK without
sideload repository for a re-sideload; Xbox, desktop, and PortMaster builds the installer bridge links its full package for one manual bootstrap
link their correctly named full package. Switch keeps its native OTA flow. 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 ## Known limitations
+1 -1
View File
@@ -1320,7 +1320,7 @@ local function buildHeader(imp, m)
return y + math.floor(10 * m.s) return y + math.floor(10 * m.s)
end 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. -- Returns status, label, action, glow.
function LauncherView._updateControl(imp) function LauncherView._updateControl(imp)
if not imp.Check then return nil end if not imp.Check then return nil end
+15 -5
View File
@@ -58,6 +58,13 @@ function Check.fullAssetName(version, osName, arch, port)
return fullAssetName(version, osName, arch, port) return fullAssetName(version, osName, arch, port)
end 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 CMD = "update_check_cmd"
local STATE = "update_check_state" local STATE = "update_check_state"
@@ -266,12 +273,15 @@ function Check.fullUpdateAction()
local st = Check.state() local st = Check.state()
if st.status ~= "needs_full" and st.status ~= "full_ready" then return nil end 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 "" local osName = love and love.system and love.system.getOS and love.system.getOS() or ""
if osName == "Android" and type(love.system.installApk) == "function" if osName == "Android" and type(st.full) == "table"
and type(st.full) == "table" and type(st.full.url) == "string" then and type(st.full.url) == "string" then
if st.status == "full_ready" and type(st.full.path) == "string" then if type(love.system.installApk) == "function" then
return { label = "Install Android update", kind = "install" } 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 end
return { label = "Download Android update", kind = "download" } return { label = "Update app manually", url = st.full.url }
end end
if osName == "iOS" then if osName == "iOS" then
return { label = "Re-sideload app", url = return { label = "Re-sideload app", url =
+6
View File
@@ -243,6 +243,12 @@ local function doCheck(target)
return return
end 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 if compareVersions(rel.version, currentEngine) <= 0 then
-- We are now running a native shell at least as new as GitHub's latest -- 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. -- release, so a former minShell/payloadHost prompt no longer applies.
+9
View File
@@ -48,6 +48,15 @@ eq(Check.fullAssetName("1.4.2", "iOS", "arm64"),
eq(Check.fullAssetName("not-a-version", "Android", "arm64"), nil, eq(Check.fullAssetName("not-a-version", "Android", "arm64"), nil,
"invalid full-package version rejected") "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({ local withNotes = Check.parseRelease(Json.encode({
tag_name = "v1.4.2", tag_name = "v1.4.2",
body = "## Issues closed\n\n- #1 cart padding", body = "## Issues closed\n\n- #1 cart padding",