@tool extends Node signal update_completed const API_RELEASES_URL := "https://api.github.com/repos/%s/%s/releases" const UPDATE_CONFIRMATION_MESSAGE := "This will update the contents of the plugin folder (addons/%s/).\nFiles outside of the plugin folder will not be affected.\n\nUpdate %s to v%s?" const PLUGIN_EXTRACT_PATH := "res://addons/%s/" const PLUGIN_TEMP_ZIP_PATH := "res://%s_%s_update.zip" @export var plugin_directory : String @export var plugin_github_url : String : set(value): plugin_github_url = value _update_urls() @export_group("Advanced") @export var auto_start : bool = false @export var default_version : String = "0.0.0" @export var replace_tag_name : String = "v" @export var _test_action : bool = false : set(value): if value and Engine.is_editor_hint(): get_newest_version() @onready var _api_client : APIClient = $APIClient @onready var _download_and_extract_node : DownloadAndExtract = $DownloadAndExtract @onready var _update_confirmation_dialog : ConfirmationDialog = $UpdateConfirmationDialog @onready var _installing_dialog : AcceptDialog = $InstallingDialog @onready var _error_dialog : AcceptDialog = $ErrorDialog @onready var _success_dialog : AcceptDialog = $SuccessDialog @onready var _release_label : RichTextLabel = %ReleaseLabel @onready var _update_label : Label = %UpdateLabel @onready var _release_notes_button : LinkButton = %ReleaseNotesButton @onready var _release_notes_panel : Panel = %ReleaseNotesPanel @onready var _stage_label : Label = %StageLabel @onready var _progress_bar : ProgressBar = %ProgressBar var _zipball_url : String var _newest_version : String var _plugin_name : String var _current_plugin_version : String func _load_plugin_details() -> void: if plugin_directory.is_empty(): return for enabled_plugin in ProjectSettings.get_setting("editor_plugins/enabled"): if enabled_plugin.contains(plugin_directory): var config := ConfigFile.new() var error = config.load(enabled_plugin) if error != OK: return _current_plugin_version = config.get_value("plugin", "version", default_version) _plugin_name = config.get_value("plugin", "name", "Plugin") func _update_urls() -> void: if plugin_github_url.is_empty(): return if _api_client == null: return var regex := RegEx.create_from_string("https:\\/\\/github\\.com\\/([\\w-]+)\\/([\\w-]+)\\/*") var regex_match := regex.search(plugin_github_url) if regex_match == null: return var username := regex_match.get_string(1) var repository := regex_match.get_string(2) _api_client.api_url = API_RELEASES_URL % [username, repository] func _show_error_dialog(error : String) -> void: _error_dialog.show() _error_dialog.dialog_text = "%s!" % error func _show_success_dialog() -> void: _success_dialog.show() _success_dialog.dialog_text = "%s updated to v%s." % [_plugin_name, _newest_version] func _on_api_client_request_failed(error : String) -> void: _show_error_dialog(error) func _on_api_client_response_received(response_body : Variant) -> void: if response_body is not Array: push_error("Response was not an array") return var latest_release : Dictionary = response_body.front() _newest_version = default_version if latest_release.has("tag_name"): var tag_name = latest_release["tag_name"] if replace_tag_name: tag_name = tag_name.replacen(replace_tag_name, "") _newest_version = tag_name if latest_release.has("zipball_url"): _zipball_url = latest_release["zipball_url"] _download_and_extract_node.zip_url = _zipball_url _download_and_extract_node.zip_file_path = PLUGIN_TEMP_ZIP_PATH % [plugin_directory, _newest_version] _update_label.text = UPDATE_CONFIRMATION_MESSAGE % [plugin_directory, _plugin_name, _newest_version] if latest_release.has("body"): _release_label.text = latest_release["body"] _update_confirmation_dialog.show() func _on_download_and_extract_zip_saved() -> void: OS.move_to_trash(ProjectSettings.globalize_path(PLUGIN_EXTRACT_PATH % plugin_directory)) func _on_download_and_extract_run_failed(error : String) -> void: _show_error_dialog(error) func _on_download_and_extract_run_completed() -> void: update_completed.emit() _show_success_dialog() func _on_error_dialog_canceled() -> void: queue_free() func _on_error_dialog_confirmed() -> void: queue_free() func _on_success_dialog_canceled() -> void: queue_free() func _on_success_dialog_confirmed() -> void: queue_free() func _on_update_confirmation_dialog_canceled() -> void: queue_free() func _on_update_confirmation_dialog_confirmed() -> void: _download_and_extract_node.run() _installing_dialog.show() func _on_release_notes_button_pressed() -> void: _release_notes_panel.show() _release_notes_button.hide() func get_newest_version() -> void: _api_client.request() func _ready() -> void: _load_plugin_details() _update_confirmation_dialog.hide() _installing_dialog.hide() _error_dialog.hide() _success_dialog.hide() if auto_start: get_newest_version() func _process(_delta : float) -> void: if _installing_dialog.visible: _progress_bar.value = _download_and_extract_node.get_progress() match _download_and_extract_node.stage: DownloadAndExtract.Stage.DOWNLOAD: _stage_label.text = "Downloading..." DownloadAndExtract.Stage.SAVE: _stage_label.text = "Saving..." DownloadAndExtract.Stage.EXTRACT: _stage_label.text = "Extracting..." DownloadAndExtract.Stage.DELETE: _stage_label.text = "Cleaning up..." DownloadAndExtract.Stage.NONE: _installing_dialog.hide()