83 lines
3.0 KiB
GDScript
83 lines
3.0 KiB
GDScript
@tool
|
|
extends Node
|
|
class_name CghUpdater
|
|
## Checks the public update endpoint; if a newer semver exists, downloads the
|
|
## addon zip and extracts it over res://addons/cgheven, then asks for an editor
|
|
## restart (Godot has no silent plugin hot-reload).
|
|
|
|
signal update_available(version: String, notes: String, url: String)
|
|
signal up_to_date()
|
|
signal update_ready_to_restart()
|
|
signal update_failed(message: String)
|
|
|
|
var _check_http: HTTPRequest
|
|
var _dl: CghDownloader
|
|
|
|
func _ready() -> void:
|
|
_check_http = HTTPRequest.new(); add_child(_check_http)
|
|
_check_http.request_completed.connect(_on_check)
|
|
_dl = CghDownloader.new(); add_child(_dl)
|
|
_dl.finished.connect(_on_zip_downloaded)
|
|
_dl.failed.connect(func(m): update_failed.emit(m))
|
|
|
|
func check() -> void:
|
|
# Backend contract: POST /api/addon-updates/check with body {addon_slug,
|
|
# current_version}; public, no auth (matches autoupdate-public-popup). Response:
|
|
# {update_available, latest_version, changelog, download_url}.
|
|
var body := JSON.stringify({
|
|
"addon_slug": CghConfig.ADDON_SLUG,
|
|
"current_version": CghConfig.ADDON_VERSION,
|
|
})
|
|
_check_http.request(CghConfig.check_update_url(),
|
|
PackedStringArray(["Content-Type: application/json"]),
|
|
HTTPClient.METHOD_POST, body)
|
|
|
|
func _on_check(_r: int, code: int, _h: PackedStringArray, b: PackedByteArray) -> void:
|
|
if code < 200 or code >= 300:
|
|
update_failed.emit("Update check failed (HTTP %d)" % code)
|
|
return
|
|
var d = JSON.parse_string(b.get_string_from_utf8())
|
|
if typeof(d) != TYPE_DICTIONARY:
|
|
update_failed.emit("Bad update response")
|
|
return
|
|
if d.get("update_available", false):
|
|
var ver := str(d.get("latest_version", "?"))
|
|
var notes_v = d.get("changelog", "")
|
|
var notes := str(notes_v) if notes_v != null else ""
|
|
var url_v = d.get("download_url", "")
|
|
var url := str(url_v) if url_v != null else ""
|
|
if url == "":
|
|
# Update exists but no ZIP uploaded to the release yet — don't prompt a
|
|
# download that can't complete; surface only on a manual check.
|
|
update_failed.emit("Update %s available, but no download is published yet." % ver)
|
|
return
|
|
update_available.emit(ver, notes, url)
|
|
else:
|
|
up_to_date.emit()
|
|
|
|
func download_and_stage(url: String) -> void:
|
|
_dl.download(url, "cgheven_update.zip")
|
|
|
|
func _on_zip_downloaded(zip_path: String) -> void:
|
|
var reader := ZIPReader.new()
|
|
if reader.open(zip_path) != OK:
|
|
update_failed.emit("Could not open update zip")
|
|
return
|
|
var base := "res://addons/cgheven/"
|
|
for entry in reader.get_files():
|
|
if entry.ends_with("/"):
|
|
continue
|
|
# entries are expected under addons/cgheven/... ; strip leading path to map onto base
|
|
var rel := entry
|
|
var idx := rel.find("addons/cgheven/")
|
|
if idx != -1:
|
|
rel = rel.substr(idx + "addons/cgheven/".length())
|
|
var out_path := base + rel
|
|
DirAccess.make_dir_recursive_absolute(out_path.get_base_dir())
|
|
var f := FileAccess.open(out_path, FileAccess.WRITE)
|
|
if f:
|
|
f.store_buffer(reader.read_file(entry))
|
|
f.close()
|
|
reader.close()
|
|
update_ready_to_restart.emit() # caller shows "Restart editor?" dialog -> EditorInterface.restart_editor()
|