diff --git a/src/components/editors/remote/remote_editors.gd b/src/components/editors/remote/remote_editors.gd index 27607868..4d623c0e 100644 --- a/src/components/editors/remote/remote_editors.gd +++ b/src/components/editors/remote/remote_editors.gd @@ -1,47 +1,33 @@ extends Control signal installed(name, abs_path) -signal _loadings_number_changed(value) - -const exml = preload("res://src/extensions/xml.gd") -const uuid = preload("res://addons/uuid.gd") +const MIRROR_GITHUB_ID = 0 +const MIRROR_TUX_FAMILY_ID = 1 +const MIRROR_DEFAULT = MIRROR_GITHUB_ID -const url = "https://downloads.tuxfamily.org/godotengine/" -const github_url = "https://github.com/godotengine/godot/releases/download/" -const platforms = { - "X11": { - "suffixes": ["_x11.64.zip", "_linux.64.zip", "_linux.x86_64.zip", "_linux.x86_32.zip"], - }, - "OSX": { - "suffixes": ["_osx.universal.zip", "_macos.universal.zip", "_osx.fat.zip", "_osx32.zip", "_osx64.zip"], - }, - "Windows": { - "suffixes": ["_win64.exe.zip", "_win32.exe.zip", "_win64.zip", "_win32.zip"], - } -} +const uuid = preload("res://addons/uuid.gd") @export var _editor_download_scene : PackedScene @export var _editor_install_scene : PackedScene @export var _remote_editor_direct_link_scene : PackedScene -@onready var tree: Tree = %Tree @onready var _open_downloads_button: Button = %OpenDownloadsButton @onready var _direct_link_button: Button = %DirectLinkButton -@onready var _check_box_container: HFlowContainer = %CheckBoxContainer @onready var _refresh_button: Button = %RefreshButton +@onready var _remote_editors_tree = %RemoteEditorsTree +@onready var _tree_mirror_button: = %TreeMirrorButton as OptionButton -var _current_platform -var _root_loaded = false -var _row_filters: Array[RowFilter] = [NotRelatedFilter.new()] -var _current_loadings_number = 0: - set(value): - _current_loadings_number = value - _loadings_number_changed.emit(value) -var _remote_editors_checkbox_checked = Cache.smart_section( - Cache.section_of(self) + ".checkbox_checked", true -) var _editor_downloads +var _tree_mirrors = {} +var _active_mirror_cache = Cache.smart_value( + self, "active_mirror", true +).map_return_value(func(v): + if not v in _tree_mirrors: + return MIRROR_DEFAULT + else: + return v +) func init(editor_downloads): @@ -49,9 +35,24 @@ func init(editor_downloads): func _ready(): - _detect_platform() - _setup_tree() - _setup_checkboxes() + _tree_mirrors[MIRROR_GITHUB_ID] = RemoteEditorsTreeDataSourceGithub.Self.new( + RemoteEditorsTreeDataSource.RemoteAssetsCallable.new(download_zip) + ) + _tree_mirrors[MIRROR_TUX_FAMILY_ID] = RemoteEditorsTreeDataSourceTuxFamily.Self.new( + RemoteEditorsTreeDataSource.RemoteAssetsCallable.new(download_zip) + ) + + _tree_mirror_button.add_item("Github", MIRROR_GITHUB_ID) + _tree_mirror_button.add_item("TuxFamily", MIRROR_TUX_FAMILY_ID) + _tree_mirror_button.selected = _tree_mirror_button.get_item_index( + _active_mirror_cache.ret(MIRROR_DEFAULT) + ) + _tree_mirror_button.item_selected.connect(func(item_idx): + var item_id = _tree_mirror_button.get_item_id(item_idx) + if item_id in _tree_mirrors: + _remote_editors_tree.set_data_source(_tree_mirrors[item_id]) + _active_mirror_cache.put(item_id) + ) _open_downloads_button.pressed.connect(func(): OS.shell_show_in_file_manager(ProjectSettings.globalize_path(Config.DOWNLOADS_PATH.ret())) @@ -70,170 +71,12 @@ func _ready(): ) _refresh_button.icon = get_theme_icon("Reload", "EditorIcons") - _refresh_button.pressed.connect(func(): - for c in tree.get_root().get_children(): - c.free() - _load_data(tree.get_root(), true, true) - ) - - _loadings_number_changed.connect(func(value): - _refresh_button.disabled = value != 0 - ) - - - -func _setup_checkboxes(): - %CheckBoxPanelContainer.add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Tree")) - - var checkbox = func(text, filter, button_pressed=false): - var box = CheckBox.new() - box.text = text - box.button_pressed = button_pressed - if button_pressed: - _row_filters.append(filter) - box.toggled.connect(func(pressed): - if pressed: - _row_filters.append(filter) - else: - var idx = _row_filters.find(filter) - _row_filters.remove_at(idx) - _remote_editors_checkbox_checked.set_value(text, pressed) - _update_whole_tree_visibility(tree.get_root()) - ) - return box - - var inverted_checkbox = func(text, filter, button_pressed=false): - var box = CheckBox.new() - box.text = text - box.button_pressed = button_pressed - if not button_pressed: - _row_filters.append(filter) - box.toggled.connect(func(pressed): - if pressed: - var idx = _row_filters.find(filter) - if idx >= 0: - _row_filters.remove_at(idx) - else: - _row_filters.append(filter) - _remote_editors_checkbox_checked.set_value(text, pressed) - _update_whole_tree_visibility(tree.get_root()) - ) - return box - - var contains_any = func(words): - return func(row: TuxfamilyRow): - return words.any(func(x): return row.name.to_lower().contains(x.to_lower())) - - var _not = func(original): - return func(row): return not original.call(row) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("mono"), - RowFilter.new(contains_any.call(["mono"])), - _remote_editors_checkbox_checked.get_value("mono", true) - ) - ) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("unstable"), - RowFilter.new(contains_any.call(["rc", "beta", "alpha", "dev", "fixup"])), - _remote_editors_checkbox_checked.get_value("unstable", false) - ) - ) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("any platform"), - RowFilter.new(func(row): - return row.is_file and row.is_for_different_platform(_current_platform["suffixes"])), - _remote_editors_checkbox_checked.get_value("any platform", false) - ) - ) - - if not OS.has_feature("macos"): - var bit - var opposite - if OS.has_feature("32"): - bit = "32" - opposite = "64" - elif OS.has_feature("64"): - bit = "64" - opposite = "32" - if bit: - _check_box_container.add_child( - checkbox.call( - "%s-bit" % bit, - RowFilter.new(contains_any.call([opposite])), - _remote_editors_checkbox_checked.get_value("same-bit", true) - ) - ) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("4.x"), - RowFilter.new(func(row: TuxfamilyRow): - return row.is_possible_version_folder and row.name.begins_with("4")), - _remote_editors_checkbox_checked.get_value("4.x", true) - ) - ) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("3.x"), - RowFilter.new(func(row: TuxfamilyRow): - return row.is_possible_version_folder and row.name.begins_with("3")), - _remote_editors_checkbox_checked.get_value("3.x", true) - ) - ) - - _check_box_container.add_child( - inverted_checkbox.call( - tr("x.x"), - RowFilter.new(func(row: TuxfamilyRow): - return row.is_possible_version_folder and not (row.name.begins_with("4") or row.name.begins_with("3"))), - _remote_editors_checkbox_checked.get_value("x.x", false) - ) - ) - - - -func _detect_platform(): - if OS.has_feature("windows"): - _current_platform = platforms["Windows"] - elif OS.has_feature("macos"): - _current_platform = platforms["OSX"] - elif OS.has_feature("linux"): - _current_platform = platforms["X11"] - - -func _setup_tree(): - var tree_root: TreeItem = tree.create_item() - tree_root.set_meta("url_part", url) - - tree.item_collapsed.connect( - func(x: TreeItem): - var expanded = not x.collapsed - var not_loaded_yet = not x.has_meta("loaded") - if expanded and not_loaded_yet: - _load_data(x) - ) - - - # FIX - tree.button_clicked.connect(func(item, col, id, mouse): - if not item.has_meta("file_name"): return - var file_name = item.get_meta("file_name") - var url = _restore_url(item, Config.USE_GITHUB.ret()) - if Config.USE_GITHUB.ret(): - download_zip(url, file_name, _restore_url(item, false)) - else: - download_zip(url, file_name) - ) + _remote_editors_tree.post_ready(_refresh_button) + var cached_mirror_id = _active_mirror_cache.ret(MIRROR_DEFAULT) + _remote_editors_tree.set_data_source(_tree_mirrors[cached_mirror_id]) -func download_zip(url, file_name, tux_fallback = ""): +func download_zip(url, file_name): var editor_download = _editor_download_scene.instantiate() _editor_downloads.add_download_item(editor_download) editor_download.start( @@ -244,15 +87,6 @@ func download_zip(url, file_name, tux_fallback = ""): "Failed to download editor: %s" % response_code ) ) - if not tux_fallback.is_empty(): - editor_download.download_failed.connect(func(_response_code): - Output.push( - "Attempt to download with tux_fallback: %s" % tux_fallback - ) - editor_download.start(tux_fallback, Config.DOWNLOADS_PATH.ret() + "/", file_name) - pass, - CONNECT_ONE_SHOT + CONNECT_DEFERRED - ) editor_download.downloaded.connect(func(abs_path): install_zip( abs_path, @@ -293,197 +127,3 @@ func _unzip_downloaded(downloaded_abs_path, root_unzip_folder_name): zip_content_dir += "/" zip.unzip(downloaded_abs_path, zip_content_dir) return zip_content_dir - - -func _load_data(root: TreeItem, reverse=false, is_tree_root=false): - _current_loadings_number += 1 - root.set_meta("loaded", true) - - var resp = await _http_get(_restore_url(root)) - _current_loadings_number -= 1 - var body = XML.parse_buffer(resp[3]) - - var tbody = exml.smart(body.root).find_smart_child_recursive( - exml.Filters.by_name("tbody") - ) - if not tbody: return - var nodes = [] - for node in tbody.iter_children_recursive(): - nodes.append(node) - if reverse: - nodes.reverse() - for node in nodes: - if node.name == "tr": - var row = TuxfamilyRow.new(node, is_tree_root) - var tree_item = tree.create_item(root) - tree_item.set_text(0, row.name) - tree_item.visible = _should_be_visible(row) - if row.is_dir: -# tree_item.set_icon(0, get_theme_icon("folder", "FileDialog")) - tree_item.set_icon(0, get_theme_icon("Folder", "EditorIcons")) - tree_item.set_icon_modulate(0, get_theme_color("folder_icon_color", "FileDialog")) - var placeholder = tree.create_item(tree_item) - placeholder.set_text(0, tr("loading...")) - # TODO animate - placeholder.set_icon(0, get_theme_icon("Progress1", "EditorIcons")) - tree_item.set_meta("loading_placeholder", placeholder) - elif row.is_zip: - tree_item.set_icon(0, get_theme_icon("Godot", "EditorIcons")) - # FIX save ref to button_click_handler etc. - var btn_texture: Texture2D = get_theme_icon("AssetLib", "EditorIcons") - tree_item.add_button(0, btn_texture) - tree_item.collapsed = true - tree_item.set_meta("url_part", row.href) - tree_item.set_meta("file_name", row.name) - tree_item.set_meta("row", row) - if root.has_meta("loading_placeholder"): - root.get_meta("loading_placeholder").free() - - -func _should_be_visible(row: TuxfamilyRow): - if row.is_parent_ref: - return false - - if row.is_file and not row.is_zip: - return false - - for filter in _row_filters: - if filter.test(row): - return false - - return true - - -func _update_whole_tree_visibility(from: TreeItem): - if not is_instance_valid(from): - return - if from.has_meta("row"): - from.visible = _should_be_visible(from.get_meta("row")) - for child in from.get_children(): - _update_whole_tree_visibility(child) - - -func _http_get(url, headers=[]): - var default_headers = [Config.AGENT_HEADER] - default_headers.append_array(headers) - - var req = HTTPRequest.new() - add_child(req) - req.request(url, default_headers, HTTPClient.METHOD_GET) - var response = await req.request_completed - req.queue_free() - return response - - -func _restore_url(item: TreeItem, use_github: bool = false): - var path_steps = [] - var path_src = item - while path_src != null: - path_steps.append(path_src.get_meta("url_part")) - path_src = path_src.get_parent() - path_steps.reverse() - - var result_url = "".join(path_steps) # tux url - if use_github: - var github_url = _tux_zip_url_to_github(result_url) - if github_url: - return github_url - return result_url - - -## Converts a given TuxFamiy URL for a Godot ZIP to a Github URL.[br] -## -## This will only work for all Godot stable Godot releases beginning from -## 3.1.1 (version 2.1.6, which will work, is the exception). This is due -## to the fact that older releases either aren't available or only provide -## the source code.[br] -## -## Returns [code]""[/code] if [param tux_url] can't be converted to a -## valid Github URL. -func _tux_zip_url_to_github(tux_url: String) -> String: - var version = tux_url.trim_prefix(url).split("/", false, 1)[0] - if (not (version >= "3.1.1" or version == "2.1.6") - or not ".zip" in tux_url or not "-stable_" in tux_url): - return "" - - var result_url = tux_url.replace(url, github_url) - result_url = result_url.replace("/mono/", "/") - result_url = result_url.replace("/" + version + "/", "/" + version + "-stable/") - return result_url - - -func _on_visibility_changed() -> void: - if visible and not _root_loaded: - _load_data(tree.get_root(), true, true) - _root_loaded = true - - -class TuxfamilyRow extends RefCounted: - const exml = preload("res://src/extensions/xml.gd") - - var _src: exml.XMLNodeSmart - var _is_possible_version_folder = false - - var is_parent_ref: - get: return self.href == "../" - - var href: - get: return _src.find_smart_child_recursive(exml.Filters.by_name("a")).attr("href") - - var is_dir: - get: return self.type == "Directory" - - var type: - get: return _src.find_child_recursive(exml.Filters.by_attr("class", "t")).content - - var name: String: - get: return _src.find_child_recursive(exml.Filters.by_name("a")).content - - var is_zip: - get: return self.type == "application/zip" - - var is_file: - get: return not is_dir - - var is_possible_version_folder: - get: return _is_possible_version_folder - - func _init(src, is_possible_version_folder=false): - if src is exml.XMLNodeSmart: - _src = src - else: - _src = exml.smart(src) - _is_possible_version_folder = is_possible_version_folder - - func is_for_different_platform(platform_suffixes): - var cached_name = name - return not platform_suffixes.any(func(suffix): return cached_name.ends_with(suffix)) - - -class RowFilter: - var _delegate - - func _init(delegate): - _delegate = delegate - - func test(row: TuxfamilyRow) -> bool: - return _delegate.call(row) - - -class SimpleContainsFilter extends RowFilter: - func _init(what: String): - super._init( - func(row: TuxfamilyRow): - return row.name.to_lower().contains(what) - ) - - -class NotRelatedFilter extends RowFilter: - func _init(): - super._init( - func(row: TuxfamilyRow): - return ["media", "patreon", "testing", "toolchains"].any( - func(x): return row.name == x - ) - ) - diff --git a/src/components/editors/remote/remote_editors.tscn b/src/components/editors/remote/remote_editors.tscn index e38a3146..2b9ebce4 100644 --- a/src/components/editors/remote/remote_editors.tscn +++ b/src/components/editors/remote/remote_editors.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=6 format=3 uid="uid://dxnoecc3ruw3t"] +[gd_scene load_steps=7 format=3 uid="uid://dxnoecc3ruw3t"] [ext_resource type="Script" path="res://src/components/editors/remote/remote_editors.gd" id="1_80m0a"] [ext_resource type="PackedScene" uid="uid://cb6u1mub27xo" path="res://src/components/asset_download/asset_download.tscn" id="2_obpx8"] [ext_resource type="PackedScene" uid="uid://ca7g1asyno8ik" path="res://src/components/editors/remote/remote_editor_install/remote_editor_install.tscn" id="3_wvg3b"] [ext_resource type="PackedScene" uid="uid://dhvesrdvhm6lv" path="res://src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.tscn" id="4_nu1uq"] [ext_resource type="PackedScene" uid="uid://cuuiumge42ghh" path="res://src/components/actions_sidebar/actions_sidebar.tscn" id="4_s82j0"] +[ext_resource type="PackedScene" uid="uid://jq7lowjivf6a" path="res://src/components/editors/remote/remote_editors_tree/remote_editors_tree.tscn" id="5_yj8hk"] [node name="RemoteEditors" type="Control"] layout_mode = 3 @@ -30,25 +31,10 @@ grow_vertical = 2 layout_mode = 2 size_flags_vertical = 3 -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Tree" type="Tree" parent="VBoxContainer/HBoxContainer/VBoxContainer"] +[node name="RemoteEditorsTree" parent="VBoxContainer/HBoxContainer" instance=ExtResource("5_yj8hk")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -size_flags_vertical = 3 -hide_root = true - -[node name="CheckBoxPanelContainer" type="PanelContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 - -[node name="CheckBoxContainer" type="HFlowContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer/CheckBoxPanelContainer"] -unique_name_in_owner = true -layout_mode = 2 -alignment = 1 [node name="ActionsSidebar" parent="VBoxContainer/HBoxContainer" instance=ExtResource("4_s82j0")] layout_mode = 2 @@ -71,6 +57,8 @@ text = "Refresh" [node name="HSeparator" parent="VBoxContainer/HBoxContainer/ActionsSidebar" index="1"] visible = false -[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] +[node name="TreeMirrorButton" type="OptionButton" parent="VBoxContainer/HBoxContainer/ActionsSidebar"] +unique_name_in_owner = true +layout_mode = 2 [editable path="VBoxContainer/HBoxContainer/ActionsSidebar"] diff --git a/src/components/editors/remote/remote_editors_tree/data_source.gd b/src/components/editors/remote/remote_editors_tree/data_source.gd new file mode 100644 index 00000000..46bd289c --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/data_source.gd @@ -0,0 +1,91 @@ +class_name RemoteEditorsTreeDataSource + + +class I: + func setup(tree: Tree): + pass + + func cleanup(tree: Tree): + pass + + func get_platform_suffixes(): + pass + + func to_remote_item(item: TreeItem) -> Item: + return null + + +# TODO looks bad +class RemoteAssets: + func download(url, file_name): + pass + + +class RemoteAssetsCallable extends RemoteAssets: + var _callable: Callable + + func _init(callable: Callable): + _callable = callable + + func download(url, file_name): + _callable.call(url, file_name) + + +class FilterTarget: + func is_possible_version_folder() -> bool: + return false + + func is_file() -> bool: + return false + + func is_for_different_platform(platform_suffixes) -> bool: + return false + + func get_name() -> String: + return '' + + +class RemoteTree: + var _tree: Tree + var _theme_source: Control + + var theme_source: Control: + get: return _theme_source + + func _init(tree, theme_source: Control): + _tree = tree + _theme_source = theme_source + + func create_item(parent: TreeItem) -> TreeItem: + var result = _tree.create_item(parent) + return result + + func free_loading_placeholder(tree_item: TreeItem): + if tree_item.has_meta("loading_placeholder"): + tree_item.get_meta("loading_placeholder").free() + + func set_as_folder(tree_item: TreeItem): + tree_item.set_icon(0, _theme_source.get_theme_icon("Folder", "EditorIcons")) + tree_item.set_icon_modulate(0, _theme_source.get_theme_color("folder_icon_color", "FileDialog")) + var placeholder = _tree.create_item(tree_item) + placeholder.set_text(0, tr("loading...")) + # TODO animate + placeholder.set_icon(0, _theme_source.get_theme_icon("Progress1", "EditorIcons")) + tree_item.set_meta("loading_placeholder", placeholder) + + +class Item: + func is_loaded() -> bool: + return false + + func async_expand(tree: RemoteTree): + return + + func handle_button_clicked(col, id, mouse): + pass + + func update_visibility(filters): + pass + + func get_children() -> Array[Item]: + return [] diff --git a/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd new file mode 100644 index 00000000..bea58fde --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd @@ -0,0 +1,243 @@ +extends VBoxContainer + + +signal _loadings_number_changed(value) + +const uuid = preload("res://addons/uuid.gd") + +@onready var _tree: Tree = %Tree +@onready var _check_box_container: HFlowContainer = %CheckBoxContainer + +var _refresh_button: Button +var _remote_assets: RemoteEditorsTreeDataSource.RemoteAssets + +var _src: RemoteEditorsTreeDataSource.I +var _i_remote_tree: RemoteEditorsTreeDataSource.RemoteTree +var _root_loaded = false +var _row_filters: Array[RowFilter] = [NotRelatedFilter.new()] +var _current_loadings_number = 0: + set(value): + _current_loadings_number = value + _loadings_number_changed.emit(value) +var _remote_editors_checkbox_checked = Cache.smart_section( + Cache.section_of(self) + ".checkbox_checked", true +) + + +func post_ready(refresh_button: Button): + _refresh_button = refresh_button + + _setup_tree() + _setup_checkboxes() + + _refresh_button.pressed.connect(func(): + _refresh() + ) + + _loadings_number_changed.connect(func(value): + _refresh_button.disabled = value != 0 + ) + + +func _ready(): + visibility_changed.connect(_on_visibility_changed) + + +func set_data_source(src: RemoteEditorsTreeDataSource.I): + if _src != null: + _src.cleanup(_tree) + _src = src + _src.setup(_tree) + _refresh() + + +func _refresh(): + for c in _tree.get_root().get_children(): + c.free() + _expand(_delegate_of(_tree.get_root())) + + +func _setup_checkboxes(): + %CheckBoxPanelContainer.add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Tree")) + + var checkbox = func(text, filter, button_pressed=false): + var box = CheckBox.new() + box.text = text + box.button_pressed = button_pressed + if button_pressed: + _row_filters.append(filter) + box.toggled.connect(func(pressed): + if pressed: + _row_filters.append(filter) + else: + var idx = _row_filters.find(filter) + _row_filters.remove_at(idx) + _remote_editors_checkbox_checked.set_value(text, pressed) + _update_whole_tree_visibility(_delegate_of(_tree.get_root())) + ) + return box + + var inverted_checkbox = func(text, filter, button_pressed=false): + var box = CheckBox.new() + box.text = text + box.button_pressed = button_pressed + if not button_pressed: + _row_filters.append(filter) + box.toggled.connect(func(pressed): + if pressed: + var idx = _row_filters.find(filter) + if idx >= 0: + _row_filters.remove_at(idx) + else: + _row_filters.append(filter) + _remote_editors_checkbox_checked.set_value(text, pressed) + _update_whole_tree_visibility(_delegate_of(_tree.get_root())) + ) + return box + + var contains_any = func(words): + return func(row: RemoteEditorsTreeDataSource.FilterTarget): + return words.any(func(x): return row.get_name().to_lower().contains(x.to_lower())) + + var _not = func(original): + return func(row): return not original.call(row) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("mono"), + RowFilter.new(contains_any.call(["mono"])), + _remote_editors_checkbox_checked.get_value("mono", true) + ) + ) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("unstable"), + RowFilter.new(contains_any.call(["rc", "beta", "alpha", "dev", "fixup"])), + _remote_editors_checkbox_checked.get_value("unstable", false) + ) + ) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("any platform"), + RowFilter.new(func(row): + return row.is_file() and row.is_for_different_platform(_src.get_platform_suffixes())), + _remote_editors_checkbox_checked.get_value("any platform", false) + ) + ) + + if not OS.has_feature("macos"): + var bit + var opposite + if OS.has_feature("32"): + bit = "32" + opposite = "64" + elif OS.has_feature("64"): + bit = "64" + opposite = "32" + if bit: + _check_box_container.add_child( + checkbox.call( + "%s-bit" % bit, + RowFilter.new(contains_any.call([opposite])), + _remote_editors_checkbox_checked.get_value("%s-bit" % bit, true) + ) + ) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("4.x"), + RowFilter.new(func(row: RemoteEditorsTreeDataSource.FilterTarget): + return row.is_possible_version_folder() and row.get_name().begins_with("4")), + _remote_editors_checkbox_checked.get_value("4.x", true) + ) + ) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("3.x"), + RowFilter.new(func(row: RemoteEditorsTreeDataSource.FilterTarget): + return row.is_possible_version_folder() and row.get_name().begins_with("3")), + _remote_editors_checkbox_checked.get_value("3.x", true) + ) + ) + + _check_box_container.add_child( + inverted_checkbox.call( + tr("x.x"), + RowFilter.new(func(row: RemoteEditorsTreeDataSource.FilterTarget): + return row.is_possible_version_folder() and not (row.get_name().begins_with("4") or row.get_name().begins_with("3"))), + _remote_editors_checkbox_checked.get_value("x.x", false) + ) + ) + + +func _delegate_of(item: TreeItem) -> RemoteEditorsTreeDataSource.Item: + return _src.to_remote_item(item) + + +func _setup_tree(): + _i_remote_tree = RemoteEditorsTreeDataSource.RemoteTree.new(_tree, self) + + _tree.item_collapsed.connect( + func(x: TreeItem): + var expanded = not x.collapsed + var delegate = _delegate_of(x) + var not_loaded_yet = not delegate.is_loaded() + if expanded and not_loaded_yet: + _expand.call_deferred(delegate) +# _expand(delegate) + ) + + _tree.button_clicked.connect(func(item, col, id, mouse): + var delegate = _delegate_of(item) + delegate.handle_button_clicked(col, id, mouse) + ) + + +func _expand(remote_tree_item: RemoteEditorsTreeDataSource.Item): + _current_loadings_number += 1 + await remote_tree_item.async_expand(_i_remote_tree) + _update_whole_tree_visibility(remote_tree_item) + _current_loadings_number -= 1 + + +func _update_whole_tree_visibility(from: RemoteEditorsTreeDataSource.Item): + from.update_visibility(_row_filters) + for child in from.get_children(): + _update_whole_tree_visibility(child) + + +func _on_visibility_changed() -> void: + if is_visible_in_tree() and not _root_loaded: + _expand(_delegate_of(_tree.get_root())) + _root_loaded = true + + +class RowFilter: + var _delegate + + func _init(delegate): + _delegate = delegate + + func test(row: RemoteEditorsTreeDataSource.FilterTarget) -> bool: + return _delegate.call(row) + + +class SimpleContainsFilter extends RowFilter: + func _init(what: String): + super._init( + func(row: RemoteEditorsTreeDataSource.FilterTarget): + return row.get_name().to_lower().contains(what) + ) + + +class NotRelatedFilter extends RowFilter: + func _init(): + super._init( + func(row: RemoteEditorsTreeDataSource.FilterTarget): + return ["media", "patreon", "testing", "toolchains"].any( + func(x): return row.get_name() == x + ) + ) diff --git a/src/components/editors/remote/remote_editors_tree/remote_editors_tree.tscn b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.tscn new file mode 100644 index 00000000..edaee71a --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=2 format=3 uid="uid://jq7lowjivf6a"] + +[ext_resource type="Script" path="res://src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd" id="1_exg7g"] + +[node name="RemoteEditorsTree" type="VBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_exg7g") + +[node name="Tree" type="Tree" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +hide_root = true + +[node name="CheckBoxPanelContainer" type="PanelContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[node name="CheckBoxContainer" type="HFlowContainer" parent="CheckBoxPanelContainer"] +unique_name_in_owner = true +layout_mode = 2 +alignment = 1 diff --git a/src/components/editors/remote/remote_editors_tree/sources/github.gd b/src/components/editors/remote/remote_editors_tree/sources/github.gd new file mode 100644 index 00000000..aa1346ac --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/sources/github.gd @@ -0,0 +1,392 @@ +class_name RemoteEditorsTreeDataSourceGithub + + +class Self extends RemoteEditorsTreeDataSource.I: + var _assets: RemoteEditorsTreeDataSource.RemoteAssets + const platforms = { + "X11": { + "suffixes": ["_x11.64.zip", "_linux.64.zip", "_linux.x86_64.zip", "_linux.x86_32.zip"], + }, + "OSX": { + "suffixes": ["_osx.universal.zip", "_macos.universal.zip", "_osx.fat.zip", "_osx32.zip", "_osx64.zip"], + }, + "Windows": { + "suffixes": ["_win64.exe.zip", "_win32.exe.zip", "_win64.zip", "_win32.zip"], + } + } + + func _init(assets): + _assets = assets + + func setup(tree: Tree): + var root = tree.create_item() + root.set_meta( + "delegate", + GithubRootItem.new( + root, + _assets, + GithubVersionSourceParseYml.new( + YmlSourceGithub.new(), + GithubAssetSourceDefault.new() + ), + ) + ) + + func cleanup(tree: Tree): + tree.clear() + + func get_platform_suffixes(): + var current_platform + if OS.has_feature("windows"): + current_platform = platforms["Windows"] + elif OS.has_feature("macos"): + current_platform = platforms["OSX"] + elif OS.has_feature("linux"): + current_platform = platforms["X11"] + return current_platform["suffixes"] + + func to_remote_item(item: TreeItem) -> RemoteEditorsTreeDataSource.Item: + return item.get_meta("delegate") + + +class GithubVersion: + var name: String + var flavor: String + var releases: Array[String] = [] + var _assets_src: GithubAssetSource + + func get_flavor_release() -> GodotRelease: + return GodotRelease.new(name, flavor, _assets_src) + + func get_recent_releases() -> Array[GodotRelease]: + var result: Array[GodotRelease] = [] + for r in releases: + result.append(GodotRelease.new(name, r, _assets_src)) + return result + + +class GodotRelease: + var name: String + var _version: String + var _assets_src: GithubAssetSource + + func _init(version, name, assets_src: GithubAssetSource): + self.name = name + _assets_src = assets_src + _version = version + + func is_stable() -> bool: + return name == "stable" + + func async_load_assets() -> Array[GodotAsset]: + return await _assets_src.async_load(_version, name) + + +class GodotAsset: + var _json: Dictionary + + var name: String: + get: return _json.get("name", "") + + var file_name: String: + get: return browser_download_url.get_file() + + var browser_download_url: String: + get: return _json.get("browser_download_url", "") + + var is_zip: + get: return name.get_extension() == "zip" + + func _init(json): + _json = json + + +class GithubItemBase extends RemoteEditorsTreeDataSource.Item: + var _item: TreeItem + var _assets: RemoteEditorsTreeDataSource.RemoteAssets + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets): + _item = item + _assets = assets + + func is_loaded() -> bool: + return _item.has_meta("loaded") + + func async_expand(tree: RemoteTree): + return + + func handle_button_clicked(col, id, mouse): + pass + + func update_visibility(filters): + var filter_target = _to_filter_target() + if filter_target == null: + return + _item.visible = _should_be_visible(filter_target, filters) + + func _should_be_visible(target: GithubFilterTarget, filters): + if target.is_file() and not target.is_zip(): + return false + + for filter in filters: + if filter.test(target): + return false + + return true + + func _to_filter_target() -> GithubFilterTarget: + return null + + func _asset_to_item(asset: GodotAsset, tree: RemoteEditorsTreeDataSource.RemoteTree): + var tree_item = tree.create_item(_item) + tree_item.set_meta("delegate", GithubAssetItem.new(tree_item, _assets, asset)) + tree_item.set_text(0, asset.name) + tree_item.set_icon(0, tree.theme_source.get_theme_icon("Godot", "EditorIcons")) + var btn_texture: Texture2D = tree.theme_source.get_theme_icon("AssetLib", "EditorIcons") + tree_item.add_button(0, btn_texture) + tree_item.collapsed = true + + func get_children() -> Array[RemoteEditorsTreeDataSource.Item]: + var result: Array[RemoteEditorsTreeDataSource.Item] = [] + for child in _item.get_children(): + if child.has_meta("delegate"): + result.append(child.get_meta("delegate")) + return result + + +class GithubAssetItem extends GithubItemBase: + var _asset: GodotAsset + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets, asset: GodotAsset): + super._init(item, assets) + _asset = asset + + func _to_filter_target() -> GithubFilterTarget: + return GithubFilterTarget.new(_asset.name, false, true, _asset.is_zip) + + func handle_button_clicked(col, id, mouse): + _assets.download(_asset.browser_download_url, _asset.file_name) + + +class GithubReleaseItem extends GithubItemBase: + var _release: GodotRelease + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets, release: GodotRelease): + super._init(item, assets) + _release = release + + func async_expand(tree: RemoteEditorsTreeDataSource.RemoteTree): + _item.set_meta("loaded", true) + var assets = await _release.async_load_assets() + for asset in assets: + _asset_to_item(asset, tree) + tree.free_loading_placeholder(_item) + + func _to_filter_target() -> GithubFilterTarget: + return GithubFilterTarget.new(_release.name, false, false, false) + + +class GithubVersionItem extends GithubItemBase: + var _version: GithubVersion + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets, version: GithubVersion): + super._init(item, assets) + _version = version + + func async_expand(tree: RemoteEditorsTreeDataSource.RemoteTree): + _item.set_meta("loaded", true) + + var releases: Array[GodotRelease] = [] + var flavor = _version.get_flavor_release() + if not flavor.is_stable(): + releases.append(flavor) + releases.append_array(_version.get_recent_releases()) + + for release in releases: + var tree_item = tree.create_item(_item) + tree_item.visible = false + tree_item.set_text(0, release.name) + tree.set_as_folder(tree_item) + tree_item.set_meta( + "delegate", + GithubReleaseItem.new(tree_item, _assets, release) + ) + tree_item.collapsed = true + + if flavor.is_stable(): + var assets = await flavor.async_load_assets() + for asset in assets: + _asset_to_item(asset, tree) + + tree.free_loading_placeholder(_item) + + func _to_filter_target() -> GithubFilterTarget: + return GithubFilterTarget.new(_version.name, true, false, false) + + +class GithubRootItem extends GithubItemBase: + var _versions_source: GithubVersionSource + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets, versions_source: GithubVersionSource): + super._init(item, assets) + _versions_source = versions_source + + func async_expand(tree: RemoteEditorsTreeDataSource.RemoteTree): + _item.set_meta("loaded", true) + var versions = await _versions_source.async_load() + for version in versions: + var tree_item = tree.create_item(_item) + tree.set_as_folder(tree_item) + tree_item.set_text(0, version.name) + tree_item.set_meta("delegate", GithubVersionItem.new(tree_item, _assets, version)) + tree_item.collapsed = true + tree.free_loading_placeholder(_item) + + +class GithubFilterTarget extends RemoteEditorsTreeDataSource.FilterTarget: + var _name + var _is_possible_version_folder + var _is_file + var _is_zip + + func _init(name, is_possible_version_folder, is_file, is_zip): + _name = name + _is_possible_version_folder = is_possible_version_folder + _is_file = is_file + _is_zip = is_zip + + func is_possible_version_folder() -> bool: + return _is_possible_version_folder + + func is_file() -> bool: + return _is_file + + func is_zip() -> bool: + return _is_zip + + func is_for_different_platform(platform_suffixes) -> bool: + var cached_name = get_name() + return not platform_suffixes.any(func(suffix): return cached_name.ends_with(suffix)) + + func get_name() -> String: + return _name + + +class GithubVersionSource: + func async_load() -> Array[GithubVersion]: + return [] + + +class GithubAssetSource: + func async_load(version: String, release: String) -> Array[GodotAsset]: + return [] + + +class GithubAssetSourceDefault extends GithubAssetSource: + const url = "https://api.github.com/repos/godotengine/godot-builds/releases/tags/%s" + + func async_load(version: String, release: String) -> Array[GodotAsset]: + var tag = "%s-%s" % [version, release] + var response = await HttpClient.async_http_get( + url % tag, + ["Accept: application/vnd.github.v3+json"] + ) + var json = JSON.parse_string( + response[3].get_string_from_utf8() + ) + var result: Array[GodotAsset] = [] + for asset_json in json.get('assets', []): + result.append(GodotAsset.new(asset_json)) + return result + + +class GithubAssetSourceFileJson extends GithubAssetSource: + var _file_path: String + + func _init(file_path: String): + _file_path = file_path + + func async_load(version: String, release: String) -> Array[GodotAsset]: + var json = JSON.parse_string(FileAccess.open(_file_path, FileAccess.READ).get_as_text()) + var result: Array[GodotAsset] = [] + for asset_json in json.get('assets', []): + result.append(GodotAsset.new(asset_json)) + return result + + +class GithubVersionSourceFileJson extends GithubVersionSource: + var _file_path: String + var _assets_src: GithubAssetSource + + func _init(file_path: String, assets_src: GithubAssetSource): + _file_path = file_path + _assets_src = assets_src + + func async_load() -> Array[GithubVersion]: + var json = JSON.parse_string(FileAccess.open(_file_path, FileAccess.READ).get_as_text()) + var result: Array[GithubVersion] = [] + for el in json: + var version = GithubVersion.new() + version._assets_src = _assets_src + version.name = el.name + version.flavor = el.flavor + for release in el.get('releases', []): + version.releases.append(release.name) + result.append(version) + return result + + +class YmlSource: + func async_load() -> String: + return "" + + +class YmlSourceFile extends YmlSource: + var _file_path: String + + func _init(file_path: String): + _file_path = file_path + + func async_load() -> String: + var text = FileAccess.open(_file_path, FileAccess.READ).get_as_text() + return text + + +class YmlSourceGithub extends YmlSource: + const url = "https://raw.githubusercontent.com/godotengine/godot-website/master/_data/versions.yml" + func async_load() -> String: + var response = await HttpClient.async_http_get(url) + var text = response[3].get_string_from_utf8() + return text + + +class GithubVersionSourceParseYml extends GithubVersionSource: + var _src: YmlSource + var _assets_src: GithubAssetSource + + var _version_regex := RegEx.create_from_string('(?m)^-[\\s\\S]*?(?=^-|\\Z)') + var _name_regex := RegEx.create_from_string('(?m)\\sname:\\s"(?[^"]+)"$') + var _flavor_regex := RegEx.create_from_string('(?m)\\sflavor:\\s"(?[^"]+)"$') + + func _init(src: YmlSource, assets_src: GithubAssetSource): + _src = src + _assets_src = assets_src + + func async_load() -> Array[GithubVersion]: + var yml = await _src.async_load() + var result: Array[GithubVersion] = [] + var versions = _version_regex.search_all(yml) + for version_result in versions: + var version_string = version_result.get_string() + var name_results = _name_regex.search_all(version_string) + var flavor_result = _flavor_regex.search(version_string) + if len(name_results) == 0 or flavor_result == null: + continue + var version = GithubVersion.new() + version._assets_src = _assets_src + version.name = name_results[0].get_string("name") + version.flavor = flavor_result.get_string("flavor") + for release_name in name_results.slice(1): + version.releases.append(release_name.get_string("name")) + result.append(version) + return result diff --git a/src/components/editors/remote/remote_editors_tree/sources/tux_family.gd b/src/components/editors/remote/remote_editors_tree/sources/tux_family.gd new file mode 100644 index 00000000..58c2e88d --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/sources/tux_family.gd @@ -0,0 +1,191 @@ +class_name RemoteEditorsTreeDataSourceTuxFamily + + +class Self extends RemoteEditorsTreeDataSource.I: + const url = "https://downloads.tuxfamily.org/godotengine/" + const platforms = { + "X11": { + "suffixes": ["_x11.64.zip", "_linux.64.zip", "_linux.x86_64.zip", "_linux.x86_32.zip"], + }, + "OSX": { + "suffixes": ["_osx.universal.zip", "_macos.universal.zip", "_osx.fat.zip", "_osx32.zip", "_osx64.zip"], + }, + "Windows": { + "suffixes": ["_win64.exe.zip", "_win32.exe.zip", "_win64.zip", "_win32.zip"], + } + } + + var _assets: RemoteEditorsTreeDataSource.RemoteAssets + + func _init(assets): + _assets = assets + + func setup(tree: Tree): + var root = tree.create_item() + root.set_meta("delegate", RemoteTreeItemTuxFamily.new(root, _assets, true)) + root.set_meta("url_part", url) + + func cleanup(tree: Tree): + tree.clear() + + func get_platform_suffixes(): + var current_platform + if OS.has_feature("windows"): + current_platform = platforms["Windows"] + elif OS.has_feature("macos"): + current_platform = platforms["OSX"] + elif OS.has_feature("linux"): + current_platform = platforms["X11"] + return current_platform["suffixes"] + + func to_remote_item(item: TreeItem) -> RemoteEditorsTreeDataSource.Item: + return item.get_meta("delegate") + + +class RemoteTreeItemTuxFamily extends RemoteEditorsTreeDataSource.Item: + var _item: TreeItem + var _assets: RemoteEditorsTreeDataSource.RemoteAssets + var _is_tree_root: bool + + func _init(item: TreeItem, assets: RemoteEditorsTreeDataSource.RemoteAssets, is_tree_root: bool): + _item = item + _assets = assets + _is_tree_root = is_tree_root + + func is_loaded() -> bool: + return _item.has_meta("loaded") + + func async_expand(tree: RemoteEditorsTreeDataSource.RemoteTree): + _item.set_meta("loaded", true) + + var resp = await HttpClient.async_http_get(_restore_url(_item)) + var body = XML.parse_buffer(resp[3]) + var tbody = exml.smart(body.root).find_smart_child_recursive( + exml.Filters.by_name("tbody") + ) + if not tbody: return + var nodes = [] + for node in tbody.iter_children_recursive(): + nodes.append(node) + if _is_tree_root: + nodes.reverse() + for node in nodes: + if node.name == "tr": + var row = TuxfamilyRow.new(node, _is_tree_root) + var tree_item = tree.create_item(_item) + tree_item.set_text(0, row.name) + tree_item.set_meta("delegate", RemoteTreeItemTuxFamily.new(tree_item, _assets, false)) + + if row.is_dir: + tree.set_as_folder(tree_item) + elif row.is_zip: + tree_item.set_icon(0, tree.theme_source.get_theme_icon("Godot", "EditorIcons")) + # FIX save ref to button_click_handler etc. + var btn_texture: Texture2D = tree.theme_source.get_theme_icon("AssetLib", "EditorIcons") + tree_item.add_button(0, btn_texture) + + tree_item.collapsed = true + tree_item.set_meta("url_part", row.href) + tree_item.set_meta("file_name", row.name) + tree_item.set_meta("row", row) + tree.free_loading_placeholder(_item) + + func get_children() -> Array[RemoteEditorsTreeDataSource.Item]: + var result: Array[RemoteEditorsTreeDataSource.Item] = [] + for child in _item.get_children(): + if child.has_meta("delegate"): + result.append(child.get_meta("delegate")) + return result + + func handle_button_clicked(col, id, mouse): + if not _item.has_meta("file_name"): return + var file_name = _item.get_meta("file_name") + var url = _restore_url(_item) + _assets.download(url, file_name) + + func update_visibility(filters): + if _item.has_meta("row"): + var row = _item.get_meta("row") + _item.visible = _should_be_visible(row, filters) + + func _should_be_visible(row: TuxfamilyRow, filters): + if row.is_parent_ref: + return false + + if row.is_file and not row.is_zip: + return false + + for filter in filters: + if filter.test(FilterTargetTuxfamilyRow.new(row)): + return false + + return true + + func _restore_url(item: TreeItem): + var path_steps = [] + var path_src = item + while path_src != null: + path_steps.append(path_src.get_meta("url_part")) + path_src = path_src.get_parent() + path_steps.reverse() + var result_url = "".join(path_steps) + return result_url + + +class FilterTargetTuxfamilyRow extends RemoteEditorsTreeDataSource.FilterTarget: + var _row: TuxfamilyRow + + func _init(row): + _row = row + + func is_possible_version_folder() -> bool: + return _row.is_possible_version_folder + + func is_file() -> bool: + return _row.is_file + + func is_for_different_platform(platform_suffixes) -> bool: + return _row.is_for_different_platform(platform_suffixes) + + func get_name() -> String: + return _row.name + + +class TuxfamilyRow extends RefCounted: + var _src: exml.XMLNodeSmart + var _is_possible_version_folder = false + + var is_parent_ref: + get: return self.href == "../" + + var href: + get: return _src.find_smart_child_recursive(exml.Filters.by_name("a")).attr("href") + + var is_dir: + get: return self.type == "Directory" + + var type: + get: return _src.find_child_recursive(exml.Filters.by_attr("class", "t")).content + + var name: String: + get: return _src.find_child_recursive(exml.Filters.by_name("a")).content + + var is_zip: + get: return self.type == "application/zip" + + var is_file: + get: return not is_dir + + var is_possible_version_folder: + get: return _is_possible_version_folder + + func _init(src, is_possible_version_folder=false): + if src is exml.XMLNodeSmart: + _src = src + else: + _src = exml.smart(src) + _is_possible_version_folder = is_possible_version_folder + + func is_for_different_platform(platform_suffixes): + var cached_name = name + return not platform_suffixes.any(func(suffix): return cached_name.ends_with(suffix)) diff --git a/src/components/misc/news_button.gd b/src/components/misc/news_button.gd index 5ee8fe7d..ff836081 100644 --- a/src/components/misc/news_button.gd +++ b/src/components/misc/news_button.gd @@ -1,7 +1,5 @@ extends LinkButton -const exml = preload("res://src/extensions/xml.gd") - const HOUR = 60 * 60 const NEWS_CACHE_LIFETIME_SEC = 12 * HOUR diff --git a/src/components/projects/import_project_dialog/import_project_dialog.tscn b/src/components/projects/import_project_dialog/import_project_dialog.tscn index ba37d8a9..a1061b9f 100644 --- a/src/components/projects/import_project_dialog/import_project_dialog.tscn +++ b/src/components/projects/import_project_dialog/import_project_dialog.tscn @@ -16,8 +16,8 @@ anchor_right = 1.0 anchor_bottom = 1.0 offset_left = 8.0 offset_top = 8.0 -offset_right = -8.0 -offset_bottom = -49.0 +offset_right = 248.0 +offset_bottom = 103.0 grow_horizontal = 2 grow_vertical = 2 diff --git a/src/components/settings/settings_window.gd b/src/components/settings/settings_window.gd index a6afac53..6171d03f 100644 --- a/src/components/settings/settings_window.gd +++ b/src/components/settings/settings_window.gd @@ -52,12 +52,6 @@ func _prepare_settings(): SettingFilePath, tr("Dir for downloaded editors.") ))), - SettingChangeObserved(SettingCfg( - "application/advanced/use_github", - Config.USE_GITHUB, - SettingCheckbox, - tr("Will download files from github when possible, if enabled.") - )), SettingChangeObserved(SettingCfg( "application/advanced/show_orphan_editor_explorer", Config.SHOW_ORPHAN_EDITOR, diff --git a/src/config.gd b/src/config.gd index bb9a2976..7fa3b63e 100644 --- a/src/config.gd +++ b/src/config.gd @@ -125,15 +125,6 @@ var USE_SYSTEM_TITLE_BAR = ConfigFileValue.new( set(_v): _readonly() -var USE_GITHUB = ConfigFileValue.new( - _cfg_auto_save, - "app", - "use_github", - true -): - set(_v): _readonly() - - var ALLOW_INSTALL_TO_NOT_EMPTY_DIR = ConfigFileValue.new( _cfg_auto_save, "app", diff --git a/src/extensions/xml.gd b/src/extensions/xml.gd index f0279543..5bbeed3e 100644 --- a/src/extensions/xml.gd +++ b/src/extensions/xml.gd @@ -1,3 +1,5 @@ +class_name exml + class XMLNodeSmart extends RefCounted: var o : XMLNode diff --git a/src/services/godots_recent_releases.gd b/src/services/godots_recent_releases.gd index cf476822..47b8a915 100644 --- a/src/services/godots_recent_releases.gd +++ b/src/services/godots_recent_releases.gd @@ -13,7 +13,6 @@ class Default extends I: _releases = releases func async_has_updates(): - print("call") var has_updates = await _releases.async_has_newest_version() return has_updates diff --git a/tests/assets/godot_release.json b/tests/assets/godot_release.json new file mode 100644 index 00000000..53076576 --- /dev/null +++ b/tests/assets/godot_release.json @@ -0,0 +1,732 @@ +{ + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/124742855", + "assets_url": "https://api.github.com/repos/godotengine/godot-builds/releases/124742855/assets", + "upload_url": "https://uploads.github.com/repos/godotengine/godot-builds/releases/124742855/assets{?name,label}", + "html_url": "https://github.com/godotengine/godot-builds/releases/tag/4.2-beta1", + "id": 124742855, + "author": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "node_id": "RE_kwDOKREZmc4Hb2zH", + "tag_name": "4.2-beta1", + "target_commitish": "main", + "name": "4.2-beta1", + "draft": false, + "prerelease": true, + "created_at": "2023-10-12T07:24:37Z", + "published_at": "2023-10-12T07:24:43Z", + "assets": [ + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206414", + "id": 130206414, + "node_id": "RA_kwDOKREZmc4HwsrO", + "name": "godot-4.2-beta1.tar.xz", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/x-xz", + "state": "uploaded", + "size": 25816956, + "download_count": 68, + "created_at": "2023-10-12T07:24:44Z", + "updated_at": "2023-10-12T07:24:48Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/godot-4.2-beta1.tar.xz" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206418", + "id": 130206418, + "node_id": "RA_kwDOKREZmc4HwsrS", + "name": "godot-4.2-beta1.tar.xz.sha256", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 89, + "download_count": 7, + "created_at": "2023-10-12T07:24:48Z", + "updated_at": "2023-10-12T07:24:49Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/godot-4.2-beta1.tar.xz.sha256" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206845", + "id": 130206845, + "node_id": "RA_kwDOKREZmc4Hwsx9", + "name": "godot-lib.4.2.beta1.mono.template_release.aar", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 79465125, + "download_count": 29, + "created_at": "2023-10-12T07:28:10Z", + "updated_at": "2023-10-12T07:28:20Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/godot-lib.4.2.beta1.mono.template_release.aar" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206419", + "id": 130206419, + "node_id": "RA_kwDOKREZmc4HwsrT", + "name": "godot-lib.4.2.beta1.template_release.aar", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 79178628, + "download_count": 31, + "created_at": "2023-10-12T07:24:49Z", + "updated_at": "2023-10-12T07:24:59Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/godot-lib.4.2.beta1.template_release.aar" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206431", + "id": 130206431, + "node_id": "RA_kwDOKREZmc4Hwsrf", + "name": "Godot_v4.2-beta1_android_editor.aab", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 168892505, + "download_count": 21, + "created_at": "2023-10-12T07:25:00Z", + "updated_at": "2023-10-12T07:25:22Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_android_editor.aab" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206471", + "id": 130206471, + "node_id": "RA_kwDOKREZmc4HwssH", + "name": "Godot_v4.2-beta1_android_editor.apk", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/vnd.android.package-archive", + "state": "uploaded", + "size": 167441128, + "download_count": 140, + "created_at": "2023-10-12T07:25:23Z", + "updated_at": "2023-10-12T07:25:44Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_android_editor.apk" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206503", + "id": 130206503, + "node_id": "RA_kwDOKREZmc4Hwssn", + "name": "Godot_v4.2-beta1_export_templates.tpz", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 805206522, + "download_count": 430, + "created_at": "2023-10-12T07:25:45Z", + "updated_at": "2023-10-12T07:27:26Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_export_templates.tpz" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206738", + "id": 130206738, + "node_id": "RA_kwDOKREZmc4HwswS", + "name": "Godot_v4.2-beta1_linux.x86_32.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 41799030, + "download_count": 18, + "created_at": "2023-10-12T07:27:26Z", + "updated_at": "2023-10-12T07:27:32Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_linux.x86_32.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206771", + "id": 130206771, + "node_id": "RA_kwDOKREZmc4Hwswz", + "name": "Godot_v4.2-beta1_linux.x86_64.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 42234559, + "download_count": 496, + "created_at": "2023-10-12T07:27:33Z", + "updated_at": "2023-10-12T07:27:39Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_linux.x86_64.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206782", + "id": 130206782, + "node_id": "RA_kwDOKREZmc4Hwsw-", + "name": "Godot_v4.2-beta1_macos.universal.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 88448393, + "download_count": 288, + "created_at": "2023-10-12T07:27:39Z", + "updated_at": "2023-10-12T07:27:51Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_macos.universal.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206855", + "id": 130206855, + "node_id": "RA_kwDOKREZmc4HwsyH", + "name": "Godot_v4.2-beta1_mono_export_templates.tpz", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 766510525, + "download_count": 283, + "created_at": "2023-10-12T07:28:21Z", + "updated_at": "2023-10-12T07:29:55Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_export_templates.tpz" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206996", + "id": 130206996, + "node_id": "RA_kwDOKREZmc4Hws0U", + "name": "Godot_v4.2-beta1_mono_linux_x86_32.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 51652728, + "download_count": 11, + "created_at": "2023-10-12T07:29:56Z", + "updated_at": "2023-10-12T07:30:03Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_linux_x86_32.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130207011", + "id": 130207011, + "node_id": "RA_kwDOKREZmc4Hws0j", + "name": "Godot_v4.2-beta1_mono_linux_x86_64.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 52060096, + "download_count": 140, + "created_at": "2023-10-12T07:30:04Z", + "updated_at": "2023-10-12T07:30:11Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_linux_x86_64.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130207019", + "id": 130207019, + "node_id": "RA_kwDOKREZmc4Hws0r", + "name": "Godot_v4.2-beta1_mono_macos.universal.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 98551408, + "download_count": 135, + "created_at": "2023-10-12T07:30:12Z", + "updated_at": "2023-10-12T07:30:24Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_macos.universal.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130207040", + "id": 130207040, + "node_id": "RA_kwDOKREZmc4Hws1A", + "name": "Godot_v4.2-beta1_mono_win32.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 56366707, + "download_count": 10, + "created_at": "2023-10-12T07:30:25Z", + "updated_at": "2023-10-12T07:30:33Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_win32.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130207052", + "id": 130207052, + "node_id": "RA_kwDOKREZmc4Hws1M", + "name": "Godot_v4.2-beta1_mono_win64.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 55474714, + "download_count": 853, + "created_at": "2023-10-12T07:30:34Z", + "updated_at": "2023-10-12T07:30:41Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_mono_win64.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206820", + "id": 130206820, + "node_id": "RA_kwDOKREZmc4Hwsxk", + "name": "Godot_v4.2-beta1_web_editor.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 27645197, + "download_count": 43, + "created_at": "2023-10-12T07:27:52Z", + "updated_at": "2023-10-12T07:27:55Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_web_editor.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206833", + "id": 130206833, + "node_id": "RA_kwDOKREZmc4Hwsxx", + "name": "Godot_v4.2-beta1_win32.exe.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 46377056, + "download_count": 20, + "created_at": "2023-10-12T07:27:56Z", + "updated_at": "2023-10-12T07:28:02Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_win32.exe.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130206841", + "id": 130206841, + "node_id": "RA_kwDOKREZmc4Hwsx5", + "name": "Godot_v4.2-beta1_win64.exe.zip", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/zip", + "state": "uploaded", + "size": 45500489, + "download_count": 1881, + "created_at": "2023-10-12T07:28:03Z", + "updated_at": "2023-10-12T07:28:09Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/Godot_v4.2-beta1_win64.exe.zip" + }, + { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/assets/130207063", + "id": 130207063, + "node_id": "RA_kwDOKREZmc4Hws1X", + "name": "SHA512-SUMS.txt", + "label": "", + "uploader": { + "login": "GodotBuilder", + "id": 17677850, + "node_id": "MDQ6VXNlcjE3Njc3ODUw", + "avatar_url": "https://avatars.githubusercontent.com/u/17677850?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/GodotBuilder", + "html_url": "https://github.com/GodotBuilder", + "followers_url": "https://api.github.com/users/GodotBuilder/followers", + "following_url": "https://api.github.com/users/GodotBuilder/following{/other_user}", + "gists_url": "https://api.github.com/users/GodotBuilder/gists{/gist_id}", + "starred_url": "https://api.github.com/users/GodotBuilder/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/GodotBuilder/subscriptions", + "organizations_url": "https://api.github.com/users/GodotBuilder/orgs", + "repos_url": "https://api.github.com/users/GodotBuilder/repos", + "events_url": "https://api.github.com/users/GodotBuilder/events{/privacy}", + "received_events_url": "https://api.github.com/users/GodotBuilder/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "text/plain; charset=utf-8", + "state": "uploaded", + "size": 3146, + "download_count": 20, + "created_at": "2023-10-12T07:30:42Z", + "updated_at": "2023-10-12T07:30:42Z", + "browser_download_url": "https://github.com/godotengine/godot-builds/releases/download/4.2-beta1/SHA512-SUMS.txt" + } + ], + "tarball_url": "https://api.github.com/repos/godotengine/godot-builds/tarball/4.2-beta1", + "zipball_url": "https://api.github.com/repos/godotengine/godot-builds/zipball/4.2-beta1", + "body": "**Godot 4.2 beta 1** is a beta snapshot for the 4.2 feature release. Beta snapshots are feature-complete and provided for public beta testing to catch as many bugs as possible ahead of the stable release.\n\nReport bugs on GitHub after checking that they haven't been reported:\n- https://github.com/godotengine/godot/issues\n\nBuilt from commit [b1371806ad3907c009458ea939bd4b810f9deb21](https://github.com/godotengine/godot/commit/b1371806ad3907c009458ea939bd4b810f9deb21).\nTo make a custom build which would also be recognized as beta1, you should define `GODOT_VERSION_STATUS=beta1` in your build environment prior to compiling.\n\n----\n\n- [Release notes](https://godotengine.org/article/dev-snapshot-godot-4-2-beta-1/)\n- [Complete changelog](https://godotengine.github.io/godot-interactive-changelog/#4.2-beta1)\n- Download (GitHub): Expand **Assets** below\n- [Download (TuxFamily)](https://downloads.tuxfamily.org/godotengine/4.2/beta1)\n\n*All files for this release are mirrored under **Assets** below.*\n", + "reactions": { + "url": "https://api.github.com/repos/godotengine/godot-builds/releases/124742855/reactions", + "total_count": 61, + "+1": 21, + "-1": 0, + "laugh": 0, + "hooray": 18, + "confused": 0, + "heart": 11, + "rocket": 11, + "eyes": 0 + } +} diff --git a/tests/assets/godot_versions.json b/tests/assets/godot_versions.json new file mode 100644 index 00000000..a25a7e7b --- /dev/null +++ b/tests/assets/godot_versions.json @@ -0,0 +1,1321 @@ +[ + { + "name": "4.2", + "flavor": "beta1", + "release_date": "12 October 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-beta-1/", + "releases": [ + { + "name": "dev6", + "release_date": "3 October 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-6/" + }, + { + "name": "dev5", + "release_date": "19 September 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-5/" + }, + { + "name": "dev4", + "release_date": "5 September 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-4/" + }, + { + "name": "dev3", + "release_date": "11 August 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-3/" + }, + { + "name": "dev2", + "release_date": "28 July 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-2/" + }, + { + "name": "dev1", + "release_date": "19 July 2023", + "release_notes": "/article/dev-snapshot-godot-4-2-dev-1/" + } + ] + }, + { + "name": "4.1.2", + "flavor": "stable", + "release_date": "4 October 2023", + "release_notes": "/article/maintenance-release-godot-4-1-2/", + "featured": "4", + "releases": [ + { + "name": "rc1", + "release_date": "22 September 2023", + "release_notes": "/article/release-candidate-godot-4-1-2-rc-1/" + } + ] + }, + { + "name": "4.1.1", + "flavor": "stable", + "release_date": "17 July 2023", + "release_notes": "/article/maintenance-release-godot-4-1-1/", + "releases": [ + { + "name": "rc1", + "release_date": "12 July 2023", + "release_notes": "/article/release-candidate-godot-4-1-1-rc-1/" + } + ] + }, + { + "name": "4.1", + "flavor": "stable", + "release_date": "6 July 2023", + "release_notes": "/article/godot-4-1-is-here/", + "releases": [ + { + "name": "rc3", + "release_date": "4 July 2023", + "release_notes": "/article/release-candidate-godot-4-1-rc-3/" + }, + { + "name": "rc2", + "release_date": "30 June 2023", + "release_notes": "/article/release-candidate-godot-4-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "27 June 2023", + "release_notes": "/article/release-candidate-godot-4-1-rc-1/" + }, + { + "name": "beta3", + "release_date": "21 June 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-beta-3/" + }, + { + "name": "beta2", + "release_date": "14 June 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-beta-2/" + }, + { + "name": "beta1", + "release_date": "8 June 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-beta-1/" + }, + { + "name": "dev4", + "release_date": "1 June 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-dev-4/" + }, + { + "name": "dev3", + "release_date": "25 May 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-dev-3/" + }, + { + "name": "dev2", + "release_date": "09 May 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-dev-2/" + }, + { + "name": "dev1", + "release_date": "21 April 2023", + "release_notes": "/article/dev-snapshot-godot-4-1-dev-1/" + } + ] + }, + { + "name": "4.0.4", + "flavor": "stable", + "release_date": "3 August 2023", + "release_notes": "/article/maintenance-release-godot-4-0-4/", + "releases": [ + { + "name": "rc1", + "release_date": "21 July 2023", + "release_notes": "/article/release-candidate-godot-4-0-4-rc-1/" + } + ] + }, + { + "name": "4.0.3", + "flavor": "stable", + "release_date": "19 May 2023", + "release_notes": "/article/maintenance-release-godot-4-0-3/", + "releases": [ + { + "name": "rc2", + "release_date": "12 May 2023", + "release_notes": "/article/release-candidate-godot-4-0-3-rc-2/" + }, + { + "name": "rc1", + "release_date": "27 April 2023", + "release_notes": "/article/release-candidate-godot-4-0-3-rc-1/" + } + ] + }, + { + "name": "4.0.2", + "flavor": "stable", + "release_date": "4 April 2023", + "release_notes": "/article/maintenance-release-godot-4-0-2/", + "releases": [ + { + "name": "rc1", + "release_date": "31 March 2023", + "release_notes": "/article/release-candidate-godot-4-0-2-rc-1/" + } + ] + }, + { + "name": "4.0.1", + "flavor": "stable", + "release_date": "20 March 2023", + "release_notes": "/article/maintenance-release-godot-4-0-1/", + "releases": [ + { + "name": "rc2", + "release_date": "17 March 2023", + "release_notes": "/article/release-candidate-godot-4-0-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "15 March 2023", + "release_notes": "/article/release-candidate-godot-4-0-1-rc-1/" + } + ] + }, + { + "name": "4.0", + "flavor": "stable", + "release_date": "1 March 2023", + "release_notes": "/article/godot-4-0-sets-sail/", + "releases": [ + { + "name": "rc6", + "release_date": "27 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-6/" + }, + { + "name": "rc5", + "release_date": "24 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-5/" + }, + { + "name": "rc4", + "release_date": "23 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-4/" + }, + { + "name": "rc3", + "release_date": "21 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-3/" + }, + { + "name": "rc2", + "release_date": "14 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-2/" + }, + { + "name": "rc1", + "release_date": "8 February 2023", + "release_notes": "/article/release-candidate-godot-4-0-rc-1/" + }, + { + "name": "beta17", + "release_date": "1 February 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-17/" + }, + { + "name": "beta16", + "release_date": "27 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-16/" + }, + { + "name": "beta15", + "release_date": "25 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-15/" + }, + { + "name": "beta14", + "release_date": "20 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-14/" + }, + { + "name": "beta13", + "release_date": "17 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-13/" + }, + { + "name": "beta12", + "release_date": "13 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-12/" + }, + { + "name": "beta11", + "release_date": "10 January 2023", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-11/" + }, + { + "name": "beta10", + "release_date": "23 December 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-10/" + }, + { + "name": "beta9", + "release_date": "19 December 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-9/" + }, + { + "name": "beta8", + "release_date": "9 December 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-8/" + }, + { + "name": "beta7", + "release_date": "1 December 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-7/" + }, + { + "name": "beta6", + "release_date": "23 November 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-6/" + }, + { + "name": "beta5", + "release_date": "16 November 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-5/" + }, + { + "name": "beta4", + "release_date": "4 November 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-4/" + }, + { + "name": "beta3", + "release_date": "14 October 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-3/" + }, + { + "name": "beta2", + "release_date": "29 September 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-2/" + }, + { + "name": "beta1", + "release_date": "15 September 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-beta-1/" + }, + { + "name": "alpha17", + "release_date": "13 September 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-17/" + }, + { + "name": "alpha16", + "release_date": "7 September 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-16/" + }, + { + "name": "alpha15", + "release_date": "30 August 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-15/" + }, + { + "name": "alpha14", + "release_date": "11 August 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-14/" + }, + { + "name": "alpha13", + "release_date": "28 July 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-13/" + }, + { + "name": "alpha12", + "release_date": "14 July 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-12/" + }, + { + "name": "alpha11", + "release_date": "1 July 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-11/" + }, + { + "name": "alpha10", + "release_date": "15 June 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-10/" + }, + { + "name": "alpha9", + "release_date": "2 June 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-9/" + }, + { + "name": "alpha8", + "release_date": "12 May 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-8/" + }, + { + "name": "alpha7", + "release_date": "28 April 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-7/" + }, + { + "name": "alpha6", + "release_date": "6 April 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-6/" + }, + { + "name": "alpha5", + "release_date": "24 March 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-5/" + }, + { + "name": "alpha4", + "release_date": "8 March 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-4/" + }, + { + "name": "alpha3", + "release_date": "22 February 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-3/" + }, + { + "name": "alpha2", + "release_date": "9 February 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-2/" + }, + { + "name": "alpha1", + "release_date": "24 January 2022", + "release_notes": "/article/dev-snapshot-godot-4-0-alpha-1/" + } + ] + }, + { + "name": "3.6", + "flavor": "beta3", + "release_date": "16 August 2023", + "release_notes": "/article/dev-snapshot-godot-3-6-beta-3/", + "releases": [ + { + "name": "beta2", + "release_date": "25 May 2023", + "release_notes": "/article/dev-snapshot-godot-3-6-beta-2/" + }, + { + "name": "beta1", + "release_date": "13 April 2023", + "release_notes": "/article/dev-snapshot-godot-3-6-beta-1/" + } + ] + }, + { + "name": "3.5.3", + "flavor": "stable", + "release_date": "25 September 2023", + "release_notes": "/article/maintenance-release-godot-3-5-3/", + "featured": "3", + "releases": [ + { + "name": "rc1", + "release_date": "8 September 2023", + "release_notes": "/article/release-candidate-godot-3-5-3-rc-1/" + } + ] + }, + { + "name": "3.5.2", + "flavor": "stable", + "release_date": "7 March 2023", + "release_notes": "/article/maintenance-release-godot-3-5-2/", + "releases": [ + { + "name": "rc2", + "release_date": "12 January 2023", + "release_notes": "/article/release-candidate-godot-3-5-2-rc-2/" + }, + { + "name": "rc1", + "release_date": "15 December 2022", + "release_notes": "/article/release-candidate-godot-3-5-2-rc-1/" + } + ] + }, + { + "name": "3.5.1", + "flavor": "stable", + "release_date": "28 September 2022", + "release_notes": "/article/maintenance-release-godot-3-5-1/", + "releases": [ + { + "name": "rc2", + "release_date": "21 September 2022", + "release_notes": "/article/release-candidate-godot-3-5-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "2 September 2022", + "release_notes": "/article/release-candidate-godot-3-5-1-rc-1/" + } + ] + }, + { + "name": "3.5", + "flavor": "stable", + "release_date": "5 August 2022", + "release_notes": "/article/godot-3-5-cant-stop-wont-stop/", + "releases": [ + { + "name": "rc8", + "release_date": "28 July 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-8/" + }, + { + "name": "rc7", + "release_date": "22 July 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-7/" + }, + { + "name": "rc6", + "release_date": "8 July 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-6/" + }, + { + "name": "rc5", + "release_date": "27 June 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-5/" + }, + { + "name": "rc4", + "release_date": "17 June 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-4/" + }, + { + "name": "rc3", + "release_date": "1 June 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-3/" + }, + { + "name": "rc2", + "release_date": "24 May 2022", + "release_notes": "/article/release-candidate-3-5-rc-2/" + }, + { + "name": "rc1", + "release_date": "18 May 2022", + "release_notes": "/article/release-candidate-godot-3-5-rc-1/" + }, + { + "name": "beta5", + "release_date": "3 May 2022", + "release_notes": "/article/dev-snapshot-godot-3-5-beta-5/" + }, + { + "name": "beta4", + "release_date": "16 April 2022", + "release_notes": "/article/dev-snapshot-godot-3-5-beta-4/" + }, + { + "name": "beta3", + "release_date": "30 March 2022", + "release_notes": "/article/dev-snapshot-godot-3-5-beta-3/" + }, + { + "name": "beta2", + "release_date": "15 March 2022", + "release_notes": "/article/dev-snapshot-godot-3-5-beta-2/" + }, + { + "name": "beta1", + "release_date": "13 January 2022", + "release_notes": "/article/dev-snapshot-godot-3-5-beta-1/" + } + ] + }, + { + "name": "3.4.5", + "flavor": "stable", + "release_date": "2 August 2022", + "release_notes": "/article/maintenance-release-godot-3-4-5/", + "releases": [ + { + "name": "rc1", + "release_date": "20 July 2022", + "release_notes": "/article/release-candidate-godot-3-4-5-rc-1/" + } + ] + }, + { + "name": "3.4.4", + "flavor": "stable", + "release_date": "23 March 2022", + "release_notes": "/article/maintenance-release-godot-3-4-4/", + "releases": [ + { + "name": "rc2", + "release_date": "16 March 2022", + "release_notes": "/article/release-candidate-godot-3-4-4-rc-2/" + }, + { + "name": "rc1", + "release_date": "8 March 2022", + "release_notes": "/article/release-candidate-godot-3-4-4-rc-1/" + } + ] + }, + { + "name": "3.4.3", + "flavor": "stable", + "release_date": "25 February 2022", + "release_notes": "/article/maintenance-release-godot-3-4-3/", + "releases": [ + { + "name": "rc2", + "release_date": "17 February 2022", + "release_notes": "/article/release-candidate-godot-3-4-3-rc-2/" + }, + { + "name": "rc1", + "release_date": "3 February 2022", + "release_notes": "/article/release-candidate-godot-3-4-3-rc-1/" + } + ] + }, + { + "name": "3.4.2", + "flavor": "stable", + "release_date": "22 December 2021", + "release_notes": "/article/maintenance-release-godot-3-4-2/" + }, + { + "name": "3.4.1", + "flavor": "stable", + "release_date": "17 December 2021", + "release_notes": "/article/maintenance-release-godot-3-4-1/", + "releases": [ + { + "name": "rc3", + "release_date": "15 December 2021", + "release_notes": "/article/release-candidate-godot-3-4-1-rc-3/" + }, + { + "name": "rc2", + "release_date": "8 December 2021", + "release_notes": "/article/release-candidate-godot-3-4-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "26 November 2021", + "release_notes": "/article/release-candidate-godot-3-4-1-rc-1/" + } + ] + }, + { + "name": "3.4", + "flavor": "stable", + "release_date": "6 November 2021", + "release_notes": "/article/godot-3-4-is-released/", + "releases": [ + { + "name": "rc3", + "release_date": "2 November 2021", + "release_notes": "/article/release-candidate-godot-3-4-rc-3/" + }, + { + "name": "rc2", + "release_date": "27 October 2021", + "release_notes": "/article/release-candidate-godot-3-4-rc-2/" + }, + { + "name": "rc1", + "release_date": "19 October 2021", + "release_notes": "/article/release-candidate-godot-3-4-rc-1/" + }, + { + "name": "beta6", + "release_date": "6 October 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-6/" + }, + { + "name": "beta5", + "release_date": "22 September 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-5/" + }, + { + "name": "beta4", + "release_date": "19 August 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-4/" + }, + { + "name": "beta3", + "release_date": "6 August 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-3/" + }, + { + "name": "beta2", + "release_date": "27 July 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-2/" + }, + { + "name": "beta1", + "release_date": "27 July 2021", + "release_notes": "/article/dev-snapshot-godot-3-4-beta-2/" + } + ] + }, + { + "name": "3.3.4", + "flavor": "stable", + "release_date": "1 October 2021", + "release_notes": "/article/maintenance-release-godot-3-3-4/", + "releases": [ + { + "name": "rc1", + "release_date": "29 September 2021", + "release_notes": "/article/release-candidate-godot-3-3-4-rc-1/" + } + ] + }, + { + "name": "3.3.3", + "flavor": "stable", + "release_date": "19 August 2021", + "release_notes": "/article/maintenance-release-godot-3-3-3/", + "releases": [ + { + "name": "rc2", + "release_date": "16 August 2021", + "release_notes": "/article/release-candidate-godot-3-3-3-rc-2/" + }, + { + "name": "rc1", + "release_date": "4 August 2021", + "release_notes": "/article/release-candidate-godot-3-3-3-rc-1/" + } + ] + }, + { + "name": "3.3.2", + "flavor": "stable", + "release_date": "24 May 2021", + "release_notes": "/article/maintenance-release-godot-3-3-2/" + }, + { + "name": "3.3.1", + "flavor": "stable", + "release_date": "18 May 2021", + "release_notes": "/article/maintenance-release-godot-3-3-1/", + "releases": [ + { + "name": "rc2", + "release_date": "15 May 2021", + "release_notes": "/article/release-candidate-godot-3-3-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "10 May 2021", + "release_notes": "/article/release-candidate-godot-3-3-1-rc-1/" + } + ] + }, + { + "name": "3.3", + "flavor": "stable", + "release_date": "22 April 2021", + "release_notes": "/article/godot-3-3-has-arrived/", + "releases": [ + { + "name": "rc9", + "release_date": "14 April 2021", + "release_notes": "/article/release-candidate-godot-3-3-rc-9/" + }, + { + "name": "rc8", + "release_date": "7 April 2021", + "release_notes": "/article/release-candidate-godot-3-3-rc-8/" + }, + { + "name": "rc7", + "release_date": "30 March 2021", + "release_notes": "/article/release-candidate-godot-3-3-rc-7/" + }, + { + "name": "rc6", + "release_date": "19 March 2021", + "release_notes": "/article/release-candidate-godot-3-3-rc-6/" + }, + { + "name": "rc5", + "release_date": "13 March 2021", + "release_notes": "/article/release-candidate-godot-3-2-4-rc-5/" + }, + { + "name": "rc4", + "release_date": "9 March 2021", + "release_notes": "/article/release-candidate-godot-3-2-4-rc-4/" + }, + { + "name": "rc3", + "release_date": "23 February 2021", + "release_notes": "/article/release-candidate-godot-3-2-4-rc-3/" + }, + { + "name": "rc2", + "release_date": "12 February 2021", + "release_notes": "/article/release-candidate-godot-3-2-4-rc-2/" + }, + { + "name": "rc1", + "release_date": "28 January 2021", + "release_notes": "/article/release-candidate-godot-3-2-4-rc-1/" + }, + { + "name": "beta6", + "release_date": "16 January 2021", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-6/" + }, + { + "name": "beta5", + "release_date": "7 January 2021", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-5/" + }, + { + "name": "beta4", + "release_date": "11 December 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-4/" + }, + { + "name": "beta3", + "release_date": "27 November 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-3/" + }, + { + "name": "beta2", + "release_date": "18 November 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-2/" + }, + { + "name": "beta1", + "release_date": "21 October 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-4-beta-1/" + } + ] + }, + { + "name": "3.2.3", + "flavor": "stable", + "release_date": "17 September 2020", + "release_notes": "/article/maintenance-release-godot-3-2-3/", + "releases": [ + { + "name": "rc6", + "release_date": "9 September 2020", + "release_notes": "/article/release-candidate-godot-3-2-3-rc-6/" + }, + { + "name": "rc5", + "release_date": "2 September 2020", + "release_notes": "/article/release-candidate-godot-3-2-3-rc-5/" + }, + { + "name": "rc4", + "release_date": "21 August 2020", + "release_notes": "/article/release-candidate-godot-3-2-3-rc-4/" + }, + { + "name": "rc3", + "release_date": "31 July 2020", + "release_notes": "/article/release-candidate-3-2-3-rc-3/" + }, + { + "name": "rc2", + "release_date": "28 July 2020", + "release_notes": "/article/release-candidate-godot-3-2-3-rc-2/" + }, + { + "name": "rc1", + "release_date": "24 July 2020", + "release_notes": "/article/release-candidate-godot-3-2-3-rc-1/" + }, + { + "name": "beta1", + "release_date": "20 July 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-3-beta-1/" + } + ] + }, + { + "name": "3.2.2", + "flavor": "stable", + "release_date": "26 June 2020", + "release_notes": "/article/maintenance-release-godot-3-2-2/", + "releases": [ + { + "name": "rc4", + "release_date": "25 June 2020", + "release_notes": "/article/release-candidate-godot-3-2-2-rc-4/" + }, + { + "name": "rc3", + "release_date": "22 June 2020", + "release_notes": "/article/release-candidate-godot-3-2-2-rc-3/" + }, + { + "name": "rc2", + "release_date": "18 June 2020", + "release_notes": "/article/release-candidate-godot-3-2-2-rc-2/" + }, + { + "name": "rc1", + "release_date": "12 June 2020", + "release_notes": "/article/release-candidate-godot-3-2-2-rc-1/" + }, + { + "name": "beta4", + "release_date": "5 June 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-2-beta-4/" + }, + { + "name": "beta3", + "release_date": "22 May 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-2-beta-3/" + }, + { + "name": "beta2", + "release_date": "7 May 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-2-beta-2/" + }, + { + "name": "beta1", + "release_date": "19 April 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-2-beta-1/" + } + ] + }, + { + "name": "3.2.1", + "flavor": "stable", + "release_date": "10 March 2020", + "release_notes": "/article/maintenance-release-godot-3-2-1/", + "releases": [ + { + "name": "rc2", + "release_date": "5 March 2020", + "release_notes": "/article/release-candidate-godot-3-2-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "22 February 2020", + "release_notes": "/article/release-candidate-godot-3-2-1-rc-1/" + } + ] + }, + { + "name": "3.2", + "flavor": "stable", + "release_date": "29 January 2020", + "release_notes": "/article/here-comes-godot-3-2/", + "releases": [ + { + "name": "rc4", + "release_date": "27 January 2020", + "release_notes": "/article/release-candidate-3-2-rc-4/" + }, + { + "name": "rc3", + "release_date": "24 January 2020", + "release_notes": "/article/release-candidate-godot-3-2-rc-3/" + }, + { + "name": "rc2", + "release_date": "20 January 2020", + "release_notes": "/article/release-candidate-godot-3-2-rc-2/" + }, + { + "name": "rc1", + "release_date": "17 January 2020", + "release_notes": "/article/release-candidate-godot-3-2-rc-1/" + }, + { + "name": "beta6", + "release_date": "11 January 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-6/" + }, + { + "name": "beta5", + "release_date": "3 January 2020", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-5/" + }, + { + "name": "beta4", + "release_date": "18 December 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-4/" + }, + { + "name": "beta3", + "release_date": "4 December 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-3/" + }, + { + "name": "beta2", + "release_date": "22 November 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-2/" + }, + { + "name": "beta1", + "release_date": "6 November 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-beta-1/" + }, + { + "name": "alpha3", + "release_date": "24 October 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-alpha-3/" + }, + { + "name": "alpha2", + "release_date": "11 October 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-alpha-2/" + }, + { + "name": "alpha1", + "release_date": "6 October 2019", + "release_notes": "/article/dev-snapshot-godot-3-2-alpha-1/" + }, + { + "name": "alpha0-unofficial", + "release_date": "21 September 2019", + "release_notes": "" + } + ] + }, + { + "name": "3.1.2", + "flavor": "stable", + "release_date": "30 November 2019", + "release_notes": "/article/maintenance-release-godot-3-1-2/", + "releases": [ + { + "name": "rc1", + "release_date": "13 November 2019", + "release_notes": "/article/release-candidate-godot-3-1-2-rc-1/" + } + ] + }, + { + "name": "3.1.1", + "flavor": "stable", + "release_date": "27 April 2019", + "release_notes": "/article/maintenance-release-godot-3-1-1/", + "releases": [ + { + "name": "rc1", + "release_date": "23 April 2019", + "release_notes": "/article/release-candidate-godot-3-1-1-rc-1/" + } + ] + }, + { + "name": "3.1", + "flavor": "stable", + "release_date": "13 March 2019", + "release_notes": "/article/godot-3-1-released/", + "releases": [ + { + "name": "rc3", + "release_date": "12 March 2019", + "release_notes": "/article/release-candidate-godot-3-1-rc-3/" + }, + { + "name": "rc2", + "release_date": "11 March 2019", + "release_notes": "/article/release-candidate-godot-3-1-rc-2/" + }, + { + "name": "rc1", + "release_date": "8 March 2019", + "release_notes": "/article/release-candidate-godot-3-1-rc-1/" + }, + { + "name": "beta11", + "release_date": "5 March 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-11/" + }, + { + "name": "beta10", + "release_date": "2 March 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-10/" + }, + { + "name": "beta9", + "release_date": "28 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-9/" + }, + { + "name": "beta8", + "release_date": "26 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-8/" + }, + { + "name": "beta7", + "release_date": "24 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-7/" + }, + { + "name": "beta6", + "release_date": "22 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-6/" + }, + { + "name": "beta5", + "release_date": "17 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-5/" + }, + { + "name": "beta4", + "release_date": "12 February 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-4/" + }, + { + "name": "beta3", + "release_date": "27 January 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-3/" + }, + { + "name": "beta2", + "release_date": "18 January 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-2/" + }, + { + "name": "beta1", + "release_date": "8 January 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-beta-1/" + }, + { + "name": "alpha5", + "release_date": "2 January 2019", + "release_notes": "/article/dev-snapshot-godot-3-1-alpha-5/" + }, + { + "name": "alpha4", + "release_date": "22 December 2018", + "release_notes": "/article/dev-snapshot-godot-3-1-alpha-4/" + }, + { + "name": "alpha3", + "release_date": "12 December 2018", + "release_notes": "/article/dev-snapshot-godot-3-1-alpha-3/" + }, + { + "name": "alpha2", + "release_date": "2 November 2018", + "release_notes": "/article/dev-snapshot-godot-3-1-alpha-2/" + }, + { + "name": "alpha1", + "release_date": "31 August 2018", + "release_notes": "/article/dev-snapshot-godot-3-1-alpha-1/" + } + ] + }, + { + "name": "3.0.6", + "flavor": "stable", + "release_date": "29 July 2018", + "release_notes": "/article/maintenance-release-godot-3-0-6/" + }, + { + "name": "3.0.5", + "flavor": "stable", + "release_date": "8 July 2018", + "release_notes": "/article/maintenance-release-godot-3-0-5/" + }, + { + "name": "3.0.4", + "flavor": "stable", + "release_date": "22 June 2018", + "release_notes": "/article/maintenance-release-godot-3-0-4/" + }, + { + "name": "3.0.3", + "flavor": "stable", + "release_date": "13 June 2018", + "release_notes": "/article/maintenance-release-godot-3-0-3/", + "releases": [ + { + "name": "rc3", + "release_date": "2 June 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-3-rc-3/" + }, + { + "name": "rc2", + "release_date": "14 May 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-3-rc-2/" + }, + { + "name": "rc1", + "release_date": "3 May 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-3-rc-1/" + } + ] + }, + { + "name": "3.0.2", + "flavor": "stable", + "release_date": "4 March 2018", + "release_notes": "/article/maintenance-release-godot-302/" + }, + { + "name": "3.0.1", + "flavor": "stable", + "release_date": "25 February 2018", + "release_notes": "/article/maintenance-release-godot-3-0-1/", + "releases": [ + { + "name": "rc1", + "release_date": "23 February 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-1-rc1/" + } + ] + }, + { + "name": "3.0", + "flavor": "stable", + "release_date": "29 January 2018", + "release_notes": "/article/godot-3-0-released/", + "releases": [ + { + "name": "rc3", + "release_date": "24 January 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-rc-3/" + }, + { + "name": "rc2", + "release_date": "20 January 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-rc-2/" + }, + { + "name": "rc1", + "release_date": "14 January 2018", + "release_notes": "/article/dev-snapshot-godot-3-0-rc-1/" + }, + { + "name": "beta2", + "release_date": "21 December 2017", + "release_notes": "/article/dev-snapshot-godot-3-0-beta-2/" + }, + { + "name": "beta1", + "release_date": "30 November 2017", + "release_notes": "/article/dev-snapshot-godot-3-0-beta-1/" + }, + { + "name": "alpha2", + "release_date": "31 October 2017", + "release_notes": "/article/dev-snapshot-godot-3-0-alpha-2/" + }, + { + "name": "alpha1", + "release_date": "26 July 2017", + "release_notes": "/article/dev-snapshot-godot-3-0-alpha-1/" + } + ] + }, + { + "name": "2.1.6", + "flavor": "stable", + "release_date": "11 July 2019", + "release_notes": "/article/maintenance-release-godot-2-1-6/", + "releases": [ + { + "name": "rc1", + "release_date": "4 June 2019", + "release_notes": "/article/dev-snapshot-godot-2-1-6-rc-1/" + } + ] + }, + { + "name": "2.1.5", + "flavor": "stable", + "release_date": "29 July 2018", + "release_notes": "/article/maintenance-release-godot-2-1-5/" + }, + { + "name": "2.1.4", + "flavor": "stable", + "release_date": "30 August 2017", + "release_notes": "/article/maintenance-release-godot-2-1-4/" + }, + { + "name": "2.1.3", + "flavor": "stable", + "release_date": "12 April 2017", + "release_notes": "/article/maintenance-release-godot-2-1-3/" + }, + { + "name": "2.1.2", + "flavor": "stable", + "release_date": "21 January 2017", + "release_notes": "/article/maintenance-release-godot-2-1-2/" + }, + { + "name": "2.1.1", + "flavor": "stable", + "release_date": "17 November 2016", + "release_notes": "/article/maintenance-release-godot-2-1-1/" + }, + { + "name": "2.1", + "flavor": "stable", + "release_date": "9 August 2016", + "release_notes": "/article/godot-reaches-2-1-stable/" + }, + { + "name": "2.0.4.1", + "flavor": "stable", + "release_date": "10 July 2016", + "release_notes": "/article/maintenance-release-godot-2-0-4/" + }, + { + "name": "2.0.4", + "flavor": "stable", + "release_date": "9 July 2016", + "release_notes": "/article/maintenance-release-godot-2-0-4/" + }, + { + "name": "2.0.3", + "flavor": "stable", + "release_date": "13 May 2016", + "release_notes": "/article/maintenance-release-godot-2-0-3/" + }, + { + "name": "2.0.2", + "flavor": "stable", + "release_date": "8 April 2016", + "release_notes": "/article/maintenance-release-godot-2-0-2/" + }, + { + "name": "2.0.1", + "flavor": "stable", + "release_date": "7 March 2016", + "release_notes": "/article/updates-on-the-release-cycle-and-godot-2-0-1/" + }, + { + "name": "2.0", + "flavor": "stable", + "release_date": "23 February 2016", + "release_notes": "/article/godot-engine-reaches-2-0-stable/" + }, + { + "name": "1.1", + "flavor": "stable", + "release_date": "21 May 2015", + "release_notes": "/article/godot-1-1-out/" + }, + { + "name": "1.0", + "flavor": "stable", + "release_date": "15 December 2014", + "release_notes": "/article/godot-engine-reaches-1-0/" + } +] diff --git a/tests/assets/godot_versions.yml b/tests/assets/godot_versions.yml new file mode 100644 index 00000000..3d209fce --- /dev/null +++ b/tests/assets/godot_versions.yml @@ -0,0 +1,860 @@ +- name: "4.2" + flavor: "beta1" + release_date: "12 October 2023" + release_notes: "/article/dev-snapshot-godot-4-2-beta-1/" + releases: + - name: "dev6" + release_date: "3 October 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-6/" + - name: "dev5" + release_date: "19 September 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-5/" + - name: "dev4" + release_date: "5 September 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-4/" + - name: "dev3" + release_date: "11 August 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-3/" + - name: "dev2" + release_date: "28 July 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-2/" + - name: "dev1" + release_date: "19 July 2023" + release_notes: "/article/dev-snapshot-godot-4-2-dev-1/" + +- name: "4.1.2" + flavor: "stable" + release_date: "4 October 2023" + release_notes: "/article/maintenance-release-godot-4-1-2/" + featured: "4" + releases: + - name: "rc1" + release_date: "22 September 2023" + release_notes: "/article/release-candidate-godot-4-1-2-rc-1/" + +- name: "4.1.1" + flavor: "stable" + release_date: "17 July 2023" + release_notes: "/article/maintenance-release-godot-4-1-1/" + releases: + - name: "rc1" + release_date: "12 July 2023" + release_notes: "/article/release-candidate-godot-4-1-1-rc-1/" + +- name: "4.1" + flavor: "stable" + release_date: "6 July 2023" + release_notes: "/article/godot-4-1-is-here/" + releases: + - name: "rc3" + release_date: "4 July 2023" + release_notes: "/article/release-candidate-godot-4-1-rc-3/" + - name: "rc2" + release_date: "30 June 2023" + release_notes: "/article/release-candidate-godot-4-1-rc-2/" + - name: "rc1" + release_date: "27 June 2023" + release_notes: "/article/release-candidate-godot-4-1-rc-1/" + - name: "beta3" + release_date: "21 June 2023" + release_notes: "/article/dev-snapshot-godot-4-1-beta-3/" + - name: "beta2" + release_date: "14 June 2023" + release_notes: "/article/dev-snapshot-godot-4-1-beta-2/" + - name: "beta1" + release_date: "8 June 2023" + release_notes: "/article/dev-snapshot-godot-4-1-beta-1/" + - name: "dev4" + release_date: "1 June 2023" + release_notes: "/article/dev-snapshot-godot-4-1-dev-4/" + - name: "dev3" + release_date: "25 May 2023" + release_notes: "/article/dev-snapshot-godot-4-1-dev-3/" + - name: "dev2" + release_date: "09 May 2023" + release_notes: "/article/dev-snapshot-godot-4-1-dev-2/" + - name: "dev1" + release_date: "21 April 2023" + release_notes: "/article/dev-snapshot-godot-4-1-dev-1/" + +- name: "4.0.4" + flavor: "stable" + release_date: "3 August 2023" + release_notes: "/article/maintenance-release-godot-4-0-4/" + releases: + - name: "rc1" + release_date: "21 July 2023" + release_notes: "/article/release-candidate-godot-4-0-4-rc-1/" + +- name: "4.0.3" + flavor: "stable" + release_date: "19 May 2023" + release_notes: "/article/maintenance-release-godot-4-0-3/" + releases: + - name: "rc2" + release_date: "12 May 2023" + release_notes: "/article/release-candidate-godot-4-0-3-rc-2/" + - name: "rc1" + release_date: "27 April 2023" + release_notes: "/article/release-candidate-godot-4-0-3-rc-1/" + +- name: "4.0.2" + flavor: "stable" + release_date: "4 April 2023" + release_notes: "/article/maintenance-release-godot-4-0-2/" + releases: + - name: "rc1" + release_date: "31 March 2023" + release_notes: "/article/release-candidate-godot-4-0-2-rc-1/" + +- name: "4.0.1" + flavor: "stable" + release_date: "20 March 2023" + release_notes: "/article/maintenance-release-godot-4-0-1/" + releases: + - name: "rc2" + release_date: "17 March 2023" + release_notes: "/article/release-candidate-godot-4-0-1-rc-2/" + - name: "rc1" + release_date: "15 March 2023" + release_notes: "/article/release-candidate-godot-4-0-1-rc-1/" + +- name: "4.0" + flavor: "stable" + release_date: "1 March 2023" + release_notes: "/article/godot-4-0-sets-sail/" + releases: + - name: "rc6" + release_date: "27 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-6/" + - name: "rc5" + release_date: "24 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-5/" + - name: "rc4" + release_date: "23 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-4/" + - name: "rc3" + release_date: "21 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-3/" + - name: "rc2" + release_date: "14 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-2/" + - name: "rc1" + release_date: "8 February 2023" + release_notes: "/article/release-candidate-godot-4-0-rc-1/" + - name: "beta17" + release_date: "1 February 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-17/" + - name: "beta16" + release_date: "27 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-16/" + - name: "beta15" + release_date: "25 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-15/" + - name: "beta14" + release_date: "20 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-14/" + - name: "beta13" + release_date: "17 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-13/" + - name: "beta12" + release_date: "13 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-12/" + - name: "beta11" + release_date: "10 January 2023" + release_notes: "/article/dev-snapshot-godot-4-0-beta-11/" + - name: "beta10" + release_date: "23 December 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-10/" + - name: "beta9" + release_date: "19 December 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-9/" + - name: "beta8" + release_date: "9 December 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-8/" + - name: "beta7" + release_date: "1 December 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-7/" + - name: "beta6" + release_date: "23 November 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-6/" + - name: "beta5" + release_date: "16 November 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-5/" + - name: "beta4" + release_date: "4 November 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-4/" + - name: "beta3" + release_date: "14 October 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-3/" + - name: "beta2" + release_date: "29 September 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-2/" + - name: "beta1" + release_date: "15 September 2022" + release_notes: "/article/dev-snapshot-godot-4-0-beta-1/" + - name: "alpha17" + release_date: "13 September 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-17/" + - name: "alpha16" + release_date: "7 September 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-16/" + - name: "alpha15" + release_date: "30 August 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-15/" + - name: "alpha14" + release_date: "11 August 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-14/" + - name: "alpha13" + release_date: "28 July 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-13/" + - name: "alpha12" + release_date: "14 July 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-12/" + - name: "alpha11" + release_date: "1 July 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-11/" + - name: "alpha10" + release_date: "15 June 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-10/" + - name: "alpha9" + release_date: "2 June 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-9/" + - name: "alpha8" + release_date: "12 May 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-8/" + - name: "alpha7" + release_date: "28 April 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-7/" + - name: "alpha6" + release_date: "6 April 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-6/" + - name: "alpha5" + release_date: "24 March 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-5/" + - name: "alpha4" + release_date: "8 March 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-4/" + - name: "alpha3" + release_date: "22 February 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-3/" + - name: "alpha2" + release_date: "9 February 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-2/" + - name: "alpha1" + release_date: "24 January 2022" + release_notes: "/article/dev-snapshot-godot-4-0-alpha-1/" + +- name: "3.6" + flavor: "beta3" + release_date: "16 August 2023" + release_notes: "/article/dev-snapshot-godot-3-6-beta-3/" + releases: + - name: "beta2" + release_date: "25 May 2023" + release_notes: "/article/dev-snapshot-godot-3-6-beta-2/" + - name: "beta1" + release_date: "13 April 2023" + release_notes: "/article/dev-snapshot-godot-3-6-beta-1/" + +- name: "3.5.3" + flavor: "stable" + release_date: "25 September 2023" + release_notes: "/article/maintenance-release-godot-3-5-3/" + featured: "3" + releases: + - name: "rc1" + release_date: "8 September 2023" + release_notes: "/article/release-candidate-godot-3-5-3-rc-1/" + +- name: "3.5.2" + flavor: "stable" + release_date: "7 March 2023" + release_notes: "/article/maintenance-release-godot-3-5-2/" + releases: + - name: "rc2" + release_date: "12 January 2023" + release_notes: "/article/release-candidate-godot-3-5-2-rc-2/" + - name: "rc1" + release_date: "15 December 2022" + release_notes: "/article/release-candidate-godot-3-5-2-rc-1/" + +- name: "3.5.1" + flavor: "stable" + release_date: "28 September 2022" + release_notes: "/article/maintenance-release-godot-3-5-1/" + releases: + - name: "rc2" + release_date: "21 September 2022" + release_notes: "/article/release-candidate-godot-3-5-1-rc-2/" + - name: "rc1" + release_date: "2 September 2022" + release_notes: "/article/release-candidate-godot-3-5-1-rc-1/" + +- name: "3.5" + flavor: "stable" + release_date: "5 August 2022" + release_notes: "/article/godot-3-5-cant-stop-wont-stop/" + releases: + - name: "rc8" + release_date: "28 July 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-8/" + - name: "rc7" + release_date: "22 July 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-7/" + - name: "rc6" + release_date: "8 July 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-6/" + - name: "rc5" + release_date: "27 June 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-5/" + - name: "rc4" + release_date: "17 June 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-4/" + - name: "rc3" + release_date: "1 June 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-3/" + - name: "rc2" + release_date: "24 May 2022" + release_notes: "/article/release-candidate-3-5-rc-2/" + - name: "rc1" + release_date: "18 May 2022" + release_notes: "/article/release-candidate-godot-3-5-rc-1/" + - name: "beta5" + release_date: "3 May 2022" + release_notes: "/article/dev-snapshot-godot-3-5-beta-5/" + - name: "beta4" + release_date: "16 April 2022" + release_notes: "/article/dev-snapshot-godot-3-5-beta-4/" + - name: "beta3" + release_date: "30 March 2022" + release_notes: "/article/dev-snapshot-godot-3-5-beta-3/" + - name: "beta2" + release_date: "15 March 2022" + release_notes: "/article/dev-snapshot-godot-3-5-beta-2/" + - name: "beta1" + release_date: "13 January 2022" + release_notes: "/article/dev-snapshot-godot-3-5-beta-1/" + +- name: "3.4.5" + flavor: "stable" + release_date: "2 August 2022" + release_notes: "/article/maintenance-release-godot-3-4-5/" + releases: + - name: "rc1" + release_date: "20 July 2022" + release_notes: "/article/release-candidate-godot-3-4-5-rc-1/" + +- name: "3.4.4" + flavor: "stable" + release_date: "23 March 2022" + release_notes: "/article/maintenance-release-godot-3-4-4/" + releases: + - name: "rc2" + release_date: "16 March 2022" + release_notes: "/article/release-candidate-godot-3-4-4-rc-2/" + - name: "rc1" + release_date: "8 March 2022" + release_notes: "/article/release-candidate-godot-3-4-4-rc-1/" + +- name: "3.4.3" + flavor: "stable" + release_date: "25 February 2022" + release_notes: "/article/maintenance-release-godot-3-4-3/" + releases: + - name: "rc2" + release_date: "17 February 2022" + release_notes: "/article/release-candidate-godot-3-4-3-rc-2/" + - name: "rc1" + release_date: "3 February 2022" + release_notes: "/article/release-candidate-godot-3-4-3-rc-1/" + +- name: "3.4.2" + flavor: "stable" + release_date: "22 December 2021" + release_notes: "/article/maintenance-release-godot-3-4-2/" + +- name: "3.4.1" + flavor: "stable" + release_date: "17 December 2021" + release_notes: "/article/maintenance-release-godot-3-4-1/" + releases: + - name: "rc3" + release_date: "15 December 2021" + release_notes: "/article/release-candidate-godot-3-4-1-rc-3/" + - name: "rc2" + release_date: "8 December 2021" + release_notes: "/article/release-candidate-godot-3-4-1-rc-2/" + - name: "rc1" + release_date: "26 November 2021" + release_notes: "/article/release-candidate-godot-3-4-1-rc-1/" + +- name: "3.4" + flavor: "stable" + release_date: "6 November 2021" + release_notes: "/article/godot-3-4-is-released/" + releases: + - name: "rc3" + release_date: "2 November 2021" + release_notes: "/article/release-candidate-godot-3-4-rc-3/" + - name: "rc2" + release_date: "27 October 2021" + release_notes: "/article/release-candidate-godot-3-4-rc-2/" + - name: "rc1" + release_date: "19 October 2021" + release_notes: "/article/release-candidate-godot-3-4-rc-1/" + - name: "beta6" + release_date: "6 October 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-6/" + - name: "beta5" + release_date: "22 September 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-5/" + - name: "beta4" + release_date: "19 August 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-4/" + - name: "beta3" + release_date: "6 August 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-3/" + - name: "beta2" + release_date: "27 July 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-2/" + - name: "beta1" + release_date: "27 July 2021" + release_notes: "/article/dev-snapshot-godot-3-4-beta-2/" + +- name: "3.3.4" + flavor: "stable" + release_date: "1 October 2021" + release_notes: "/article/maintenance-release-godot-3-3-4/" + releases: + - name: "rc1" + release_date: "29 September 2021" + release_notes: "/article/release-candidate-godot-3-3-4-rc-1/" + +- name: "3.3.3" + flavor: "stable" + release_date: "19 August 2021" + release_notes: "/article/maintenance-release-godot-3-3-3/" + releases: + - name: "rc2" + release_date: "16 August 2021" + release_notes: "/article/release-candidate-godot-3-3-3-rc-2/" + - name: "rc1" + release_date: "4 August 2021" + release_notes: "/article/release-candidate-godot-3-3-3-rc-1/" + +- name: "3.3.2" + flavor: "stable" + release_date: "24 May 2021" + release_notes: "/article/maintenance-release-godot-3-3-2/" + +- name: "3.3.1" + flavor: "stable" + release_date: "18 May 2021" + release_notes: "/article/maintenance-release-godot-3-3-1/" + releases: + - name: "rc2" + release_date: "15 May 2021" + release_notes: "/article/release-candidate-godot-3-3-1-rc-2/" + - name: "rc1" + release_date: "10 May 2021" + release_notes: "/article/release-candidate-godot-3-3-1-rc-1/" + +- name: "3.3" + flavor: "stable" + release_date: "22 April 2021" + release_notes: "/article/godot-3-3-has-arrived/" + releases: + - name: "rc9" + release_date: "14 April 2021" + release_notes: "/article/release-candidate-godot-3-3-rc-9/" + - name: "rc8" + release_date: "7 April 2021" + release_notes: "/article/release-candidate-godot-3-3-rc-8/" + - name: "rc7" + release_date: "30 March 2021" + release_notes: "/article/release-candidate-godot-3-3-rc-7/" + - name: "rc6" + release_date: "19 March 2021" + release_notes: "/article/release-candidate-godot-3-3-rc-6/" + - name: "rc5" + release_date: "13 March 2021" + release_notes: "/article/release-candidate-godot-3-2-4-rc-5/" + - name: "rc4" + release_date: "9 March 2021" + release_notes: "/article/release-candidate-godot-3-2-4-rc-4/" + - name: "rc3" + release_date: "23 February 2021" + release_notes: "/article/release-candidate-godot-3-2-4-rc-3/" + - name: "rc2" + release_date: "12 February 2021" + release_notes: "/article/release-candidate-godot-3-2-4-rc-2/" + - name: "rc1" + release_date: "28 January 2021" + release_notes: "/article/release-candidate-godot-3-2-4-rc-1/" + - name: "beta6" + release_date: "16 January 2021" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-6/" + - name: "beta5" + release_date: "7 January 2021" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-5/" + - name: "beta4" + release_date: "11 December 2020" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-4/" + - name: "beta3" + release_date: "27 November 2020" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-3/" + - name: "beta2" + release_date: "18 November 2020" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-2/" + - name: "beta1" + release_date: "21 October 2020" + release_notes: "/article/dev-snapshot-godot-3-2-4-beta-1/" + +- name: "3.2.3" + flavor: "stable" + release_date: "17 September 2020" + release_notes: "/article/maintenance-release-godot-3-2-3/" + releases: + - name: "rc6" + release_date: "9 September 2020" + release_notes: "/article/release-candidate-godot-3-2-3-rc-6/" + - name: "rc5" + release_date: "2 September 2020" + release_notes: "/article/release-candidate-godot-3-2-3-rc-5/" + - name: "rc4" + release_date: "21 August 2020" + release_notes: "/article/release-candidate-godot-3-2-3-rc-4/" + - name: "rc3" + release_date: "31 July 2020" + release_notes: "/article/release-candidate-3-2-3-rc-3/" + - name: "rc2" + release_date: "28 July 2020" + release_notes: "/article/release-candidate-godot-3-2-3-rc-2/" + - name: "rc1" + release_date: "24 July 2020" + release_notes: "/article/release-candidate-godot-3-2-3-rc-1/" + - name: "beta1" + release_date: "20 July 2020" + release_notes: "/article/dev-snapshot-godot-3-2-3-beta-1/" + +- name: "3.2.2" + flavor: "stable" + release_date: "26 June 2020" + release_notes: "/article/maintenance-release-godot-3-2-2/" + releases: + - name: "rc4" + release_date: "25 June 2020" + release_notes: "/article/release-candidate-godot-3-2-2-rc-4/" + - name: "rc3" + release_date: "22 June 2020" + release_notes: "/article/release-candidate-godot-3-2-2-rc-3/" + - name: "rc2" + release_date: "18 June 2020" + release_notes: "/article/release-candidate-godot-3-2-2-rc-2/" + - name: "rc1" + release_date: "12 June 2020" + release_notes: "/article/release-candidate-godot-3-2-2-rc-1/" + - name: "beta4" + release_date: "5 June 2020" + release_notes: "/article/dev-snapshot-godot-3-2-2-beta-4/" + - name: "beta3" + release_date: "22 May 2020" + release_notes: "/article/dev-snapshot-godot-3-2-2-beta-3/" + - name: "beta2" + release_date: "7 May 2020" + release_notes: "/article/dev-snapshot-godot-3-2-2-beta-2/" + - name: "beta1" + release_date: "19 April 2020" + release_notes: "/article/dev-snapshot-godot-3-2-2-beta-1/" + +- name: "3.2.1" + flavor: "stable" + release_date: "10 March 2020" + release_notes: "/article/maintenance-release-godot-3-2-1/" + releases: + - name: "rc2" + release_date: "5 March 2020" + release_notes: "/article/release-candidate-godot-3-2-1-rc-2/" + - name: "rc1" + release_date: "22 February 2020" + release_notes: "/article/release-candidate-godot-3-2-1-rc-1/" + +- name: "3.2" + flavor: "stable" + release_date: "29 January 2020" + release_notes: "/article/here-comes-godot-3-2/" + releases: + - name: "rc4" + release_date: "27 January 2020" + release_notes: "/article/release-candidate-3-2-rc-4/" + - name: "rc3" + release_date: "24 January 2020" + release_notes: "/article/release-candidate-godot-3-2-rc-3/" + - name: "rc2" + release_date: "20 January 2020" + release_notes: "/article/release-candidate-godot-3-2-rc-2/" + - name: "rc1" + release_date: "17 January 2020" + release_notes: "/article/release-candidate-godot-3-2-rc-1/" + - name: "beta6" + release_date: "11 January 2020" + release_notes: "/article/dev-snapshot-godot-3-2-beta-6/" + - name: "beta5" + release_date: "3 January 2020" + release_notes: "/article/dev-snapshot-godot-3-2-beta-5/" + - name: "beta4" + release_date: "18 December 2019" + release_notes: "/article/dev-snapshot-godot-3-2-beta-4/" + - name: "beta3" + release_date: "4 December 2019" + release_notes: "/article/dev-snapshot-godot-3-2-beta-3/" + - name: "beta2" + release_date: "22 November 2019" + release_notes: "/article/dev-snapshot-godot-3-2-beta-2/" + - name: "beta1" + release_date: "6 November 2019" + release_notes: "/article/dev-snapshot-godot-3-2-beta-1/" + - name: "alpha3" + release_date: "24 October 2019" + release_notes: "/article/dev-snapshot-godot-3-2-alpha-3/" + - name: "alpha2" + release_date: "11 October 2019" + release_notes: "/article/dev-snapshot-godot-3-2-alpha-2/" + - name: "alpha1" + release_date: "6 October 2019" + release_notes: "/article/dev-snapshot-godot-3-2-alpha-1/" + - name: "alpha0-unofficial" + release_date: "21 September 2019" + release_notes: "" + +- name: "3.1.2" + flavor: "stable" + release_date: "30 November 2019" + release_notes: "/article/maintenance-release-godot-3-1-2/" + releases: + - name: "rc1" + release_date: "13 November 2019" + release_notes: "/article/release-candidate-godot-3-1-2-rc-1/" + +- name: "3.1.1" + flavor: "stable" + release_date: "27 April 2019" + release_notes: "/article/maintenance-release-godot-3-1-1/" + releases: + - name: "rc1" + release_date: "23 April 2019" + release_notes: "/article/release-candidate-godot-3-1-1-rc-1/" + +- name: "3.1" + flavor: "stable" + release_date: "13 March 2019" + release_notes: "/article/godot-3-1-released/" + releases: + - name: "rc3" + release_date: "12 March 2019" + release_notes: "/article/release-candidate-godot-3-1-rc-3/" + - name: "rc2" + release_date: "11 March 2019" + release_notes: "/article/release-candidate-godot-3-1-rc-2/" + - name: "rc1" + release_date: "8 March 2019" + release_notes: "/article/release-candidate-godot-3-1-rc-1/" + - name: "beta11" + release_date: "5 March 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-11/" + - name: "beta10" + release_date: "2 March 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-10/" + - name: "beta9" + release_date: "28 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-9/" + - name: "beta8" + release_date: "26 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-8/" + - name: "beta7" + release_date: "24 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-7/" + - name: "beta6" + release_date: "22 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-6/" + - name: "beta5" + release_date: "17 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-5/" + - name: "beta4" + release_date: "12 February 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-4/" + - name: "beta3" + release_date: "27 January 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-3/" + - name: "beta2" + release_date: "18 January 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-2/" + - name: "beta1" + release_date: "8 January 2019" + release_notes: "/article/dev-snapshot-godot-3-1-beta-1/" + - name: "alpha5" + release_date: "2 January 2019" + release_notes: "/article/dev-snapshot-godot-3-1-alpha-5/" + - name: "alpha4" + release_date: "22 December 2018" + release_notes: "/article/dev-snapshot-godot-3-1-alpha-4/" + - name: "alpha3" + release_date: "12 December 2018" + release_notes: "/article/dev-snapshot-godot-3-1-alpha-3/" + - name: "alpha2" + release_date: "2 November 2018" + release_notes: "/article/dev-snapshot-godot-3-1-alpha-2/" + - name: "alpha1" + release_date: "31 August 2018" + release_notes: "/article/dev-snapshot-godot-3-1-alpha-1/" + +- name: "3.0.6" + flavor: "stable" + release_date: "29 July 2018" + release_notes: "/article/maintenance-release-godot-3-0-6/" + +- name: "3.0.5" + flavor: "stable" + release_date: "8 July 2018" + release_notes: "/article/maintenance-release-godot-3-0-5/" + +- name: "3.0.4" + flavor: "stable" + release_date: "22 June 2018" + release_notes: "/article/maintenance-release-godot-3-0-4/" + +- name: "3.0.3" + flavor: "stable" + release_date: "13 June 2018" + release_notes: "/article/maintenance-release-godot-3-0-3/" + releases: + - name: "rc3" + release_date: "2 June 2018" + release_notes: "/article/dev-snapshot-godot-3-0-3-rc-3/" + - name: "rc2" + release_date: "14 May 2018" + release_notes: "/article/dev-snapshot-godot-3-0-3-rc-2/" + - name: "rc1" + release_date: "3 May 2018" + release_notes: "/article/dev-snapshot-godot-3-0-3-rc-1/" + +- name: "3.0.2" + flavor: "stable" + release_date: "4 March 2018" + release_notes: "/article/maintenance-release-godot-302/" + +- name: "3.0.1" + flavor: "stable" + release_date: "25 February 2018" + release_notes: "/article/maintenance-release-godot-3-0-1/" + releases: + - name: "rc1" + release_date: "23 February 2018" + release_notes: "/article/dev-snapshot-godot-3-0-1-rc1/" + +- name: "3.0" + flavor: "stable" + release_date: "29 January 2018" + release_notes: "/article/godot-3-0-released/" + releases: + - name: "rc3" + release_date: "24 January 2018" + release_notes: "/article/dev-snapshot-godot-3-0-rc-3/" + - name: "rc2" + release_date: "20 January 2018" + release_notes: "/article/dev-snapshot-godot-3-0-rc-2/" + - name: "rc1" + release_date: "14 January 2018" + release_notes: "/article/dev-snapshot-godot-3-0-rc-1/" + - name: "beta2" + release_date: "21 December 2017" + release_notes: "/article/dev-snapshot-godot-3-0-beta-2/" + - name: "beta1" + release_date: "30 November 2017" + release_notes: "/article/dev-snapshot-godot-3-0-beta-1/" + - name: "alpha2" + release_date: "31 October 2017" + release_notes: "/article/dev-snapshot-godot-3-0-alpha-2/" + - name: "alpha1" + release_date: "26 July 2017" + release_notes: "/article/dev-snapshot-godot-3-0-alpha-1/" + +- name: "2.1.6" + flavor: "stable" + release_date: "11 July 2019" + release_notes: "/article/maintenance-release-godot-2-1-6/" + releases: + - name: "rc1" + release_date: "4 June 2019" + release_notes: "/article/dev-snapshot-godot-2-1-6-rc-1/" + +- name: "2.1.5" + flavor: "stable" + release_date: "29 July 2018" + release_notes: "/article/maintenance-release-godot-2-1-5/" + +- name: "2.1.4" + flavor: "stable" + release_date: "30 August 2017" + release_notes: "/article/maintenance-release-godot-2-1-4/" + +- name: "2.1.3" + flavor: "stable" + release_date: "12 April 2017" + release_notes: "/article/maintenance-release-godot-2-1-3/" + +- name: "2.1.2" + flavor: "stable" + release_date: "21 January 2017" + release_notes: "/article/maintenance-release-godot-2-1-2/" + +- name: "2.1.1" + flavor: "stable" + release_date: "17 November 2016" + release_notes: "/article/maintenance-release-godot-2-1-1/" + +- name: "2.1" + flavor: "stable" + release_date: "9 August 2016" + release_notes: "/article/godot-reaches-2-1-stable/" + +- name: "2.0.4.1" + flavor: "stable" + release_date: "10 July 2016" + release_notes: "/article/maintenance-release-godot-2-0-4/" + +- name: "2.0.4" + flavor: "stable" + release_date: "9 July 2016" + release_notes: "/article/maintenance-release-godot-2-0-4/" + +- name: "2.0.3" + flavor: "stable" + release_date: "13 May 2016" + release_notes: "/article/maintenance-release-godot-2-0-3/" + +- name: "2.0.2" + flavor: "stable" + release_date: "8 April 2016" + release_notes: "/article/maintenance-release-godot-2-0-2/" + +- name: "2.0.1" + flavor: "stable" + release_date: "7 March 2016" + release_notes: "/article/updates-on-the-release-cycle-and-godot-2-0-1/" + +- name: "2.0" + flavor: "stable" + release_date: "23 February 2016" + release_notes: "/article/godot-engine-reaches-2-0-stable/" + +- name: "1.1" + flavor: "stable" + release_date: "21 May 2015" + release_notes: "/article/godot-1-1-out/" + +- name: "1.0" + flavor: "stable" + release_date: "15 December 2014" + release_notes: "/article/godot-engine-reaches-1-0/"