Skip to content

Commit

Permalink
Merge pull request godotengine#63312 from bruvzg/one_click
Browse files Browse the repository at this point in the history
[Export] Add one-click deploy over SSH for the desktop exports.
  • Loading branch information
akien-mga committed Jan 13, 2023
2 parents 3c9bf4b + cebefc9 commit 3dffe0b
Show file tree
Hide file tree
Showing 43 changed files with 1,392 additions and 187 deletions.
1 change: 1 addition & 0 deletions editor/editor_property_name_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["rmb"] = "RMB";
capitalize_string_remaps["rpc"] = "RPC";
capitalize_string_remaps["s3tc"] = "S3TC";
capitalize_string_remaps["scp"] = "SCP";
capitalize_string_remaps["sdf"] = "SDF";
capitalize_string_remaps["sdfgi"] = "SDFGI";
capitalize_string_remaps["sdk"] = "SDK";
Expand Down
4 changes: 3 additions & 1 deletion editor/editor_run_native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ Error EditorRunNative::run_native(int p_idx, int p_platform) {
Error err = eep->run(preset, p_idx, flags);
result_dialog_log->clear();
if (eep->fill_log_messages(result_dialog_log, err)) {
result_dialog->popup_centered_ratio(0.5);
if (eep->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_ERROR) {
result_dialog->popup_centered_ratio(0.5);
}
}
return err;
}
Expand Down
6 changes: 6 additions & 0 deletions editor/export/editor_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ void EditorExport::_notification(int p_what) {
case NOTIFICATION_PROCESS: {
update_export_presets();
} break;

case NOTIFICATION_EXIT_TREE: {
for (int i = 0; i < export_platforms.size(); i++) {
export_platforms.write[i]->cleanup();
}
} break;
}
}

Expand Down
233 changes: 233 additions & 0 deletions editor/export/editor_export_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,121 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj
return OK;
}

void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);

Ref<DirAccess> da = DirAccess::open(dir);
da->list_dir_begin();
String f = da->get_next();
while (!f.is_empty()) {
if (f == "." || f == "..") {
f = da->get_next();
continue;
}
if (da->is_link(f)) {
OS::DateTime dt = OS::get_singleton()->get_datetime();

zip_fileinfo zipfi;
zipfi.tmz_date.tm_year = dt.year;
zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
zipfi.tmz_date.tm_mday = dt.day;
zipfi.tmz_date.tm_hour = dt.hour;
zipfi.tmz_date.tm_min = dt.minute;
zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
// 0120000: symbolic link type
// 0000755: permissions rwxr-xr-x
// 0000644: permissions rw-r--r--
uint32_t _mode = 0120644;
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
zipfi.internal_fa = 0;

zipOpenNewFileInZip4(p_zip,
p_folder.path_join(f).utf8().get_data(),
&zipfi,
nullptr,
0,
nullptr,
0,
nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION,
0,
-MAX_WBITS,
DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY,
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);

String target = da->read_link(f);
zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
zipCloseFileInZip(p_zip);
} else if (da->current_is_dir()) {
zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
} else {
bool _is_executable = is_executable(dir.path_join(f));

OS::DateTime dt = OS::get_singleton()->get_datetime();

zip_fileinfo zipfi;
zipfi.tmz_date.tm_year = dt.year;
zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
zipfi.tmz_date.tm_mday = dt.day;
zipfi.tmz_date.tm_hour = dt.hour;
zipfi.tmz_date.tm_min = dt.minute;
zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
// 0100000: regular file type
// 0000755: permissions rwxr-xr-x
// 0000644: permissions rw-r--r--
uint32_t _mode = (_is_executable ? 0100755 : 0100644);
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
zipfi.internal_fa = 0;

zipOpenNewFileInZip4(p_zip,
p_folder.path_join(f).utf8().get_data(),
&zipfi,
nullptr,
0,
nullptr,
0,
nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION,
0,
-MAX_WBITS,
DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY,
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);

Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
if (fa.is_null()) {
add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
return;
}
const int bufsize = 16384;
uint8_t buf[bufsize];

while (true) {
uint64_t got = fa->get_buffer(buf, bufsize);
if (got == 0) {
break;
}
zipWriteInFileInZip(p_zip, buf, got);
}

zipCloseFileInZip(p_zip);
}
f = da->get_next();
}
da->list_dir_end();
}

Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
EditorProgress ep("savepack", TTR("Packing"), 102, true);

Expand Down Expand Up @@ -1642,5 +1757,123 @@ bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, S
return valid;
}

Error EditorExportPlatform::ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out, int p_port_fwd) const {
String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh");
if (ssh_path.is_empty()) {
ssh_path = "ssh";
}

List<String> args;
args.push_back("-p");
args.push_back(p_port);
for (const String &E : p_ssh_args) {
args.push_back(E);
}
if (p_port_fwd > 0) {
args.push_back("-R");
args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd));
}
args.push_back(p_host);
args.push_back(p_cmd_args);

String out;
int exit_code = -1;

if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data());
for (const String &arg : args) {
OS::get_singleton()->print(" %s", arg.utf8().get_data());
}
OS::get_singleton()->print("\n");
}

Error err = OS::get_singleton()->execute(ssh_path, args, &out, &exit_code, true);
if (out.is_empty()) {
print_verbose(vformat("Exit code: %d", exit_code));
} else {
print_verbose(vformat("Exit code: %d, Output: %s", exit_code, out.replace("\r\n", "\n")));
}
if (r_out) {
*r_out = out.replace("\r\n", "\n").get_slice("\n", 0);
}
if (err != OK) {
return err;
} else if (exit_code != 0) {
if (!out.is_empty()) {
print_line(out);
}
return FAILED;
}
return OK;
}

Error EditorExportPlatform::ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid, int p_port_fwd) const {
String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh");
if (ssh_path.is_empty()) {
ssh_path = "ssh";
}

List<String> args;
args.push_back("-p");
args.push_back(p_port);
for (const String &E : p_ssh_args) {
args.push_back(E);
}
if (p_port_fwd > 0) {
args.push_back("-R");
args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd));
}
args.push_back(p_host);
args.push_back(p_cmd_args);

if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data());
for (const String &arg : args) {
OS::get_singleton()->print(" %s", arg.utf8().get_data());
}
OS::get_singleton()->print("\n");
}

return OS::get_singleton()->create_process(ssh_path, args, r_pid);
}

Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const {
String scp_path = EditorSettings::get_singleton()->get("export/ssh/scp");
if (scp_path.is_empty()) {
scp_path = "scp";
}

List<String> args;
args.push_back("-P");
args.push_back(p_port);
for (const String &E : p_scp_args) {
args.push_back(E);
}
args.push_back(p_src_file);
args.push_back(vformat("%s:%s", p_host, p_dst_file));

String out;
int exit_code = -1;

if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Executing: %s", scp_path.utf8().get_data());
for (const String &arg : args) {
OS::get_singleton()->print(" %s", arg.utf8().get_data());
}
OS::get_singleton()->print("\n");
}

Error err = OS::get_singleton()->execute(scp_path, args, &out, &exit_code, true);
if (err != OK) {
return err;
} else if (exit_code != 0) {
if (!out.is_empty()) {
print_line(out);
}
return FAILED;
}
return OK;
}

EditorExportPlatform::EditorExportPlatform() {
}
10 changes: 9 additions & 1 deletion editor/export/editor_export_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class EditorFileSystemDirectory;
struct EditorProgress;

#include "core/io/dir_access.h"
#include "core/io/zip_io.h"
#include "editor_export_preset.h"
#include "editor_export_shared_object.h"
#include "scene/gui/rich_text_label.h"
Expand Down Expand Up @@ -92,7 +93,6 @@ class EditorExportPlatform : public RefCounted {
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);

void gen_debug_flags(Vector<String> &r_flags, int p_flags);
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);

Expand Down Expand Up @@ -126,6 +126,13 @@ class EditorExportPlatform : public RefCounted {
bool exists_export_template(String template_file_name, String *err) const;
String find_export_template(String template_file_name, String *err = nullptr) const;
void gen_export_flags(Vector<String> &r_flags, int p_flags);
void gen_debug_flags(Vector<String> &r_flags, int p_flags);

virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);

Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const;
Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const;
Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;

public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const = 0;
Expand Down Expand Up @@ -215,6 +222,7 @@ class EditorExportPlatform : public RefCounted {
DEBUG_FLAG_VIEW_NAVIGATION = 16,
};

virtual void cleanup() {}
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }

Expand Down
1 change: 0 additions & 1 deletion editor/export/editor_export_platform_pc.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ class EditorExportPlatformPC : public EditorExportPlatform {
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; };
virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);

void set_extension(const String &p_extension, const String &p_feature_key = "default");
void set_name(const String &p_name);
void set_os_name(const String &p_name);

Expand Down
5 changes: 5 additions & 0 deletions editor/export/editor_export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "scene/resources/resource_format_text.h"

Expand Down Expand Up @@ -226,4 +227,8 @@ void EditorExportPlugin::_bind_methods() {
}

EditorExportPlugin::EditorExportPlugin() {
GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false);

EDITOR_DEF("export/ssh/ssh", "");
EDITOR_DEF("export/ssh/scp", "");
}
36 changes: 18 additions & 18 deletions methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,29 +488,29 @@ def mySpawn(sh, escape, cmd, args, env):
def save_active_platforms(apnames, ap):

for x in ap:
names = ["logo"]
if os.path.isfile(x + "/run_icon.png"):
names.append("run_icon")

for name in names:
pngf = open(x + "/" + name + ".png", "rb")
b = pngf.read(1)
str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
str += " static const unsigned char _" + x[9:] + "_" + name + "[]={"
svg_names = []
if os.path.isfile(x + "/logo.svg"):
svg_names.append("logo")
if os.path.isfile(x + "/run_icon.svg"):
svg_names.append("run_icon")

for name in svg_names:
svgf = open(x + "/" + name + ".svg", "rb")
b = svgf.read(1)
svg_str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
svg_str += " static const char *_" + x[9:] + "_" + name + '_svg = "'
while len(b) == 1:
str += hex(ord(b))
b = pngf.read(1)
if len(b) == 1:
str += ","
svg_str += "\\" + hex(ord(b))[1:]
b = svgf.read(1)

str += "};\n"
svg_str += '";\n'

pngf.close()
svgf.close()

# NOTE: It is safe to generate this file here, since this is still executed serially
wf = x + "/" + name + ".gen.h"
with open(wf, "w") as pngw:
pngw.write(str)
wf = x + "/" + name + "_svg.gen.h"
with open(wf, "w") as svgw:
svgw.write(svg_str)


def no_verbose(sys, env):
Expand Down
Loading

0 comments on commit 3dffe0b

Please sign in to comment.