Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Export] Add one-click deploy over SSH for the desktop exports. #63312

Merged
merged 1 commit into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1320,6 +1320,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 @@ -1640,5 +1755,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);
bruvzg marked this conversation as resolved.
Show resolved Hide resolved
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);
bruvzg marked this conversation as resolved.
Show resolved Hide resolved

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 @@ -484,29 +484,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