Keep the butler binary up to date with itch's broth channel
All checks were successful
Build plugin / build (push) Successful in 1m10s
All checks were successful
Build plugin / build (push) Successful in 1m10s
butler was only downloaded when the binary was missing, so a device that installed it once kept that build forever (brutha was still on v15.30.0 from July while broth ships 15.32.0). bootstrap now compares the installed version against broth's LATEST and refreshes it when behind, keeping the installed binary when the channel can't be reached or the version can't be read, so a network hiccup never replaces a working butler.
This commit is contained in:
parent
c6fd78e76f
commit
bb4ddb0ea5
2 changed files with 63 additions and 17 deletions
|
|
@ -83,7 +83,8 @@ func _ready() -> void:
|
|||
bootstrap()
|
||||
|
||||
|
||||
## Bootstraps the butler binary if it isn't present, then spawns butlerd.
|
||||
## Bootstraps the butler binary (downloading it when missing or outdated), then
|
||||
## spawns butlerd.
|
||||
func bootstrap() -> void:
|
||||
var butler_dir_global := ProjectSettings.globalize_path(butler_dir)
|
||||
_migrate_butler(butler_dir_global)
|
||||
|
|
@ -96,6 +97,12 @@ func bootstrap() -> void:
|
|||
bootstrap_finished.emit()
|
||||
return
|
||||
logger.info("Successfully installed butler")
|
||||
elif await _butler_is_outdated(butler_bin):
|
||||
logger.info("Updating the butler binary to " + str(await _fetch_latest_butler_version()) + ".")
|
||||
var success := await _install_butler()
|
||||
if not success:
|
||||
# Not fatal: the installed butler still works, it is just behind.
|
||||
logger.warn("Unable to update butler, keeping the installed one.")
|
||||
|
||||
var dbpath := "/".join([butler_dir_global, "butler.db"])
|
||||
var args := [
|
||||
|
|
@ -139,9 +146,8 @@ func _migrate_butler(dest_dir: String) -> void:
|
|||
logger.info("Migrated butler from " + old_dir + " to " + dest_dir)
|
||||
|
||||
|
||||
## Downloads a butler binary for this platform from itch's broth distribution
|
||||
## channel (the same one the official itch.io app uses to self-update).
|
||||
func _install_butler() -> bool:
|
||||
## broth's slug for the platform we are running on, e.g. "linux-amd64".
|
||||
func _butler_platform_slug() -> String:
|
||||
var goos := "linux"
|
||||
var goarch := "amd64"
|
||||
if OS.get_name() == "Windows":
|
||||
|
|
@ -155,10 +161,13 @@ func _install_butler() -> bool:
|
|||
var arch: String = Engine.get_architecture_name()
|
||||
if "arm64" in arch or "aarch64" in arch:
|
||||
goarch = "arm64"
|
||||
return goos + "-" + goarch
|
||||
|
||||
var platform_slug := goos + "-" + goarch
|
||||
var latest_url := "/".join([broth_base, platform_slug, "LATEST"])
|
||||
|
||||
## Asks itch's broth channel which butler version is current. Returns "" when
|
||||
## the channel can't be reached, so callers can tell "unknown" from "current".
|
||||
func _fetch_latest_butler_version() -> String:
|
||||
var latest_url := "/".join([broth_base, _butler_platform_slug(), "LATEST"])
|
||||
var http := HTTPRequest.new()
|
||||
add_child.call_deferred(http)
|
||||
await http.ready
|
||||
|
|
@ -166,28 +175,65 @@ func _install_butler() -> bool:
|
|||
logger.error("Error fetching latest butler version: " + latest_url)
|
||||
remove_child(http)
|
||||
http.queue_free()
|
||||
return false
|
||||
return ""
|
||||
var args: Array = await http.request_completed
|
||||
var result: int = args[0]
|
||||
var response_code: int = args[1]
|
||||
var body: PackedByteArray = args[3]
|
||||
remove_child(http)
|
||||
http.queue_free()
|
||||
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
|
||||
logger.error("Unable to determine latest butler version for " + platform_slug)
|
||||
remove_child(http)
|
||||
http.queue_free()
|
||||
return false
|
||||
var version := body.get_string_from_utf8().strip_edges()
|
||||
logger.error("Unable to determine the latest butler version")
|
||||
return ""
|
||||
return body.get_string_from_utf8().strip_edges()
|
||||
|
||||
|
||||
## Reads the version of an installed butler binary, e.g. "15.30.0" out of
|
||||
## "v15.30.0, built on Jul 27 2026". Returns "" when the binary can't be run.
|
||||
func _butler_installed_version(butler_bin: String) -> String:
|
||||
var out := []
|
||||
if OS.execute(butler_bin, ["version"], out) != OK or out.is_empty():
|
||||
return ""
|
||||
var reported: String = (out[0] as String).strip_edges().get_slice(",", 0)
|
||||
return reported.trim_prefix("v").strip_edges()
|
||||
|
||||
|
||||
## True when the installed butler is behind broth's current version. The
|
||||
## binary used to be downloaded only when missing, so it silently rotted for
|
||||
## months and missed fixes. Unknown version (channel unreachable, binary not
|
||||
## runnable) counts as up to date: never replace a working butler on a guess.
|
||||
func _butler_is_outdated(butler_bin: String) -> bool:
|
||||
var latest := await _fetch_latest_butler_version()
|
||||
if latest == "":
|
||||
return false
|
||||
var installed := _butler_installed_version(butler_bin)
|
||||
if installed == "" or installed == latest:
|
||||
return false
|
||||
logger.info("butler " + installed + " is behind " + latest + ". Updating it.")
|
||||
return true
|
||||
|
||||
|
||||
## Downloads a butler binary for this platform from itch's broth distribution
|
||||
## channel (the same one the official itch.io app uses to self-update).
|
||||
func _install_butler() -> bool:
|
||||
var platform_slug := _butler_platform_slug()
|
||||
var version := await _fetch_latest_butler_version()
|
||||
if version == "":
|
||||
return false
|
||||
|
||||
var http := HTTPRequest.new()
|
||||
add_child.call_deferred(http)
|
||||
await http.ready
|
||||
var archive_url := "/".join([broth_base, platform_slug, version, "archive", "default"])
|
||||
if http.request(archive_url) != OK:
|
||||
logger.error("Error downloading butler: " + archive_url)
|
||||
remove_child(http)
|
||||
http.queue_free()
|
||||
return false
|
||||
args = await http.request_completed
|
||||
result = args[0]
|
||||
response_code = args[1]
|
||||
body = args[3]
|
||||
var args: Array = await http.request_completed
|
||||
var result: int = args[0]
|
||||
var response_code: int = args[1]
|
||||
var body: PackedByteArray = args[3]
|
||||
remove_child(http)
|
||||
http.queue_free()
|
||||
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue